# formulas/run-planner.toml — Strategic planning formula (v3: Prerequisite Tree) # # Executed directly by planner-run.sh via cron — no action issues. # planner-run.sh creates a tmux session with Claude (opus) and injects # this formula as context. Claude executes all steps autonomously. # # Steps: preflight → prediction-triage → update-prerequisite-tree # → file-at-constraints → journal-and-memory → commit-and-pr # # Core change from v2: replaces gap-analysis-and-spray with a constraint- # focused executive using a Prerequisite Tree (Theory of Constraints). # Issues are only filed at the top 3 unresolved constraints — everything # beyond the bottleneck exists in the tree but NOT as issues. # # AGENTS.md maintenance is handled by the gardener (#246). # All git writes (tree, journal, memory) happen in one commit at the end. name = "run-planner" description = "Planner v3: prerequisite tree + resource-aware constraint executive" version = 3 model = "opus" [context] files = ["VISION.md", "AGENTS.md", "RESOURCES.md", "planner/prerequisite-tree.md"] # Recent planner/journal/*.md files are loaded by planner-run.sh (last 5 entries) [[steps]] id = "preflight" title = "Pull latest code and load planner memory" description = """ Set up the working environment for this planning run. 1. Change to the project repository: cd "$PROJECT_REPO_ROOT" 2. Pull the latest code: git fetch origin "$PRIMARY_BRANCH" --quiet git checkout "$PRIMARY_BRANCH" --quiet git pull --ff-only origin "$PRIMARY_BRANCH" --quiet 3. Record the current HEAD SHA: HEAD_SHA=$(git rev-parse HEAD) echo "$HEAD_SHA" > /tmp/planner-head-sha 4. Read the planner memory file at: $PROJECT_REPO_ROOT/planner/MEMORY.md If it does not exist, this is the first planning run. Keep this memory context in mind for all subsequent steps. 5. Read the prerequisite tree at: $PROJECT_REPO_ROOT/planner/prerequisite-tree.md If it does not exist, create an initial tree from VISION.md in the update-prerequisite-tree step. """ [[steps]] id = "prediction-triage" title = "Triage prediction/unreviewed issues" description = """ Triage prediction issues filed by the predictor (goblin). Evidence from the preflight step informs whether each prediction is valid (e.g. "red-team stale since March 12" is confirmed by evidence/ timestamps). 1. Fetch unreviewed predictions: curl -sf -H "Authorization: token $CODEBERG_TOKEN" \ "$CODEBERG_API/issues?state=open&type=issues&labels=prediction%2Funreviewed&limit=50" If there are none, note that and skip to step 3b (label resolution is still required — the file-at-constraints step needs label IDs). 2. Read available formulas from $FACTORY_ROOT/formulas/*.toml so you know what actions can be dispatched. 3. Fetch all open issues to check for overlap: curl -sf -H "Authorization: token $CODEBERG_TOKEN" \ "$CODEBERG_API/issues?state=open&type=issues&limit=50" 3b. Resolve label IDs needed for triage AND filing (fetch via $CODEBERG_API/labels). ALWAYS execute this step, even if there are no predictions to triage — the file-at-constraints step depends on these IDs: - → prediction/unreviewed - → prediction/backlog - → prediction/actioned (create if missing, color #c2e0c6, description "Prediction triaged by planner") - → backlog - → action These are DISTINCT labels — do not reuse IDs across them. 4. For each prediction, read the title and body. Choose one action: - PROMOTE_ACTION: maps to an available formula → create an action issue with YAML front matter referencing the formula name and vars. Relabel prediction/unreviewed → prediction/actioned, then close with comment "Actioned as #NNN — ". - PROMOTE_BACKLOG: warrants dev work → create a backlog issue. Relabel prediction/unreviewed → prediction/actioned, then close with comment "Actioned as #NNN — ". - WATCH: not urgent but worth tracking → post a comment explaining why it is not urgent, then relabel from prediction/unreviewed to prediction/backlog. Do NOT close. - DISMISS: noise, already covered by an open issue, or not actionable → relabel prediction/unreviewed → prediction/actioned, post a comment with explicit reasoning, then close the prediction. Every decision MUST include reasoning in a comment on the prediction issue. 5. Executing triage decisions via API: For PROMOTE_ACTION / PROMOTE_BACKLOG: a. Create the new issue with the 'action' or 'backlog' label. IMPORTANT — the issue body MUST include these sections so the gardener quality gate does not strip the backlog label: - "## Acceptance criteria" with at least one checkbox (- [ ] ...) - "## Affected files" with at least one file path Example body structure: ## Problem\n\n\n## Proposed solution\n\n\n## Affected files\n- \n- \n\n## Acceptance criteria\n- [ ] \n- [ ] CI green Create the issue: curl -sf -X POST -H "Authorization: token $CODEBERG_TOKEN" \ -H "Content-Type: application/json" "$CODEBERG_API/issues" \ -d '{"title":"...","body":"...","labels":[]}' Extract the issue number from the response (jq -r '.number'). a2. Verify the label was applied (Codeberg may silently drop labels on creation). Re-apply via a separate POST if missing: curl -sf -X POST -H "Authorization: token $CODEBERG_TOKEN" \ -H "Content-Type: application/json" \ "$CODEBERG_API/issues//labels" \ -d '{"labels":[]}' b. Comment on the prediction with "Actioned as #NNN — ": curl -sf -X POST -H "Authorization: token $CODEBERG_TOKEN" \ -H "Content-Type: application/json" \ "$CODEBERG_API/issues//comments" \ -d '{"body":"Actioned as #NNN — "}' c. Relabel: remove prediction/unreviewed, add prediction/actioned: curl -sf -X DELETE -H "Authorization: token $CODEBERG_TOKEN" \ "$CODEBERG_API/issues//labels/" curl -sf -X POST -H "Authorization: token $CODEBERG_TOKEN" \ -H "Content-Type: application/json" \ "$CODEBERG_API/issues//labels" \ -d '{"labels":[]}' d. Close the prediction: curl -sf -X PATCH -H "Authorization: token $CODEBERG_TOKEN" \ -H "Content-Type: application/json" \ "$CODEBERG_API/issues/" \ -d '{"state":"closed"}' For WATCH: a. Comment with reasoning why not urgent: curl -sf -X POST -H "Authorization: token $CODEBERG_TOKEN" \ -H "Content-Type: application/json" \ "$CODEBERG_API/issues//comments" \ -d '{"body":"Watching — "}' b. Replace prediction/unreviewed label with prediction/backlog: curl -sf -X DELETE -H "Authorization: token $CODEBERG_TOKEN" \ "$CODEBERG_API/issues//labels/" curl -sf -X POST -H "Authorization: token $CODEBERG_TOKEN" \ -H "Content-Type: application/json" \ "$CODEBERG_API/issues//labels" \ -d '{"labels":[]}' For DISMISS: a. Comment with explicit reasoning: curl -sf -X POST -H "Authorization: token $CODEBERG_TOKEN" \ -H "Content-Type: application/json" \ "$CODEBERG_API/issues//comments" \ -d '{"body":"Dismissed — "}' b. Relabel: remove prediction/unreviewed, add prediction/actioned: curl -sf -X DELETE -H "Authorization: token $CODEBERG_TOKEN" \ "$CODEBERG_API/issues//labels/" curl -sf -X POST -H "Authorization: token $CODEBERG_TOKEN" \ -H "Content-Type: application/json" \ "$CODEBERG_API/issues//labels" \ -d '{"labels":[]}' c. Close the prediction: curl -sf -X PATCH -H "Authorization: token $CODEBERG_TOKEN" \ -H "Content-Type: application/json" \ "$CODEBERG_API/issues/" \ -d '{"state":"closed"}' 6. Track promoted predictions — they are added to the prerequisite tree in the next step if they represent real prerequisites. 7. Validation: if you reference a formula, verify it exists on disk. Fall back to a freeform backlog issue for unknown formulas. Be decisive — the predictor intentionally over-signals; your job is to filter. CRITICAL: If this step fails, log the failure and move on to update-prerequisite-tree. """ needs = ["preflight"] [[steps]] id = "update-prerequisite-tree" title = "Scan repo state and update the prerequisite tree" description = """ This is the constraint discovery step. Read the current state, then update the prerequisite tree to reflect reality. Read these inputs: - VISION.md — where we want to be (objectives come from milestones) - planner/prerequisite-tree.md — current tree (loaded in preflight) - RESOURCES.md — available agents, boxes, assets, formulas - $FACTORY_ROOT/formulas/*.toml — what actions can be dispatched - Open issues (fetched via API, or reuse from prediction-triage) - Closed issues (fetch recently closed to detect resolved prerequisites): curl -sf -H "Authorization: token $CODEBERG_TOKEN" \ "$CODEBERG_API/issues?state=closed&type=issues&limit=50&sort=updated&direction=desc" - Planner memory (loaded in preflight) - Promoted predictions from prediction-triage (add as prerequisites if relevant) Update the tree by applying these operations: 1. **Mark resolved prerequisites**: For each prerequisite in the tree, check if the corresponding issue is closed or the capability is now present in the repo. Mark resolved items with [x]. 2. **Update objective status**: Recalculate each objective's status: - All prerequisites resolved → Status: READY (or DONE if the objective itself is closed/implemented) - Some unresolved → Status: BLOCKED — N prerequisites unresolved - Depends on blocked objectives → Status: BLOCKED — prerequisite chain 3. **Discover new prerequisites**: As you scan repo state, you may find new prerequisites not yet in the tree. Add them. The tree grows organically — this is expected and desirable. 4. **Add new objectives**: If VISION.md has objectives not yet in the tree, add them with their prerequisite chains. 5. **Propose new capabilities**: If you identify a capability the factory needs (e.g., "marketing formula, runs weekly"), add it to the tree as a proposed prerequisite. Anything with recurring cost (new accounts, new infra, new cron entries, new formulas) should be procured through the vault — see the file-at-constraints step for how to file requests. 6. **Check vault state**: Scan vault directories for procurement status: - `$PROJECT_REPO_ROOT/vault/pending/*.md` — requests awaiting human action. Any prerequisite that depends on a pending procurement request should be marked: `[ ] ⏳ blocked-on-vault (vault/pending/.md)` - `$PROJECT_REPO_ROOT/vault/approved/*.md` — fulfilled, being processed. - `$PROJECT_REPO_ROOT/vault/fired/*.md` — completed. Check if the resource now appears in RESOURCES.md and mark the prerequisite resolved. - Do NOT file issues for objectives blocked on pending vault items. 7. **Re-read RESOURCES.md**: Check for newly available capabilities that were not present last run. If a new resource appears, mark the corresponding prerequisite as resolved. Write the updated tree to: $PROJECT_REPO_ROOT/planner/prerequisite-tree.md Use this format: # Prerequisite Tree ## Objective: (#issue or description) - [x] Resolved prerequisite (reference) - [ ] Unresolved prerequisite (#issue or description) - [ ] Resource need ⏳ blocked-on-vault (vault/pending/.md) Status: READY | BLOCKED — | BLOCKED — awaiting vault | DONE Keep the tree focused — only include objectives from VISION.md milestones and their genuine prerequisites. Do not inflate the tree with nice-to-haves. IMPORTANT: Do NOT write the tree to disk yet — hold it in memory for the next step. The tree will be written along with the journal in commit-and-pr. """ needs = ["prediction-triage"] [[steps]] id = "file-at-constraints" title = "Identify top 3 constraints and file issues" description = """ This is the constraint-focused filing step. The key principle from Theory of Constraints: only work on the bottleneck. Everything else is waste. From the updated prerequisite tree, identify the top 3 constraints: A **constraint** is an unresolved prerequisite that blocks the most downstream objectives. To find them: 1. For each unresolved prerequisite ([ ] item), count how many objectives it transitively blocks. A prerequisite that blocks objective A, which in turn blocks objectives B and C, has a blocking score of 3. 2. Rank all unresolved prerequisites by blocking score (descending). 3. Select the top 3. These are the constraints. Filing gate — for each constraint: 1. Check if an issue already exists for this constraint (match by issue number reference in the tree, or search open issues by title). 2. If no issue exists, create one. IMPORTANT — the issue body MUST include these sections so the gardener quality gate does not strip the backlog label: - "## Affected files" with at least one file path - "## Acceptance criteria" with at least one checkbox (- [ ] ...) Use this body structure: ## Problem\n\n\n## Proposed solution\n\n\n## Affected files\n- \n- \n\n## Acceptance criteria\n- [ ] \n- [ ] CI green\n\n## Dependencies\n- #NNN (if depends on other open issues) Create the issue: curl -sf -X POST \ -H "Authorization: token $CODEBERG_TOKEN" \ -H "Content-Type: application/json" \ "$CODEBERG_API/issues" \ -d '{"title":"...","body":"...","labels":[]}' Extract the issue number from the response (jq -r '.number'). 2b. Verify the label was applied (Codeberg may silently drop labels on creation). Always re-apply via a separate POST to be safe: curl -sf -X POST -H "Authorization: token $CODEBERG_TOKEN" \ -H "Content-Type: application/json" \ "$CODEBERG_API/issues//labels" \ -d '{"labels":[]}' 3. If an issue already exists and is open, skip it — no duplicate filing. 4. If an issue already exists but is in backlog without proper context, consider adding a comment noting its constraint status. Rules: - **Maximum 3 issues filed per run** — only at constraints - **No issues filed past the bottleneck** — items beyond the top 3 constraints exist in the tree but NOT as issues - **Existing premature issues left as-is** — do not close issues filed by previous planner versions, even if they're past the bottleneck - Do NOT create issues that overlap with ANY existing open issue - Only reference formulas that exist in formulas/*.toml - When deploying/operating, reference the resource alias from RESOURCES.md - Promoted predictions from triage may become constraints if they block downstream objectives — rank them the same way - **Do NOT file issues for objectives blocked on pending vault items** — these are waiting for human procurement, not dev work ### Filing vault procurement requests If a constraint requires a resource the factory does not have (check RESOURCES.md), and that resource has recurring cost (account, infra, domain, API key, new cron job), file a procurement request instead of an issue: 1. Check if a request already exists in vault/pending/ or vault/approved/ for this resource (match by filename). 2. If no request exists, create a markdown file at: $PROJECT_REPO_ROOT/vault/pending/.md Format: ``` # Procurement Request: ## What ## Why ## Unblocks ## Proposed RESOURCES.md Entry ## - type: - capability: - env: ``` 3. Mark the prerequisite in the tree as blocked-on-vault: `[ ] ⏳ blocked-on-vault (vault/pending/.md)` 4. vault-poll.sh will notify the human automatically. Procurement requests count toward the 3-item-per-run limit (issues + procurement requests combined). If all top 3 constraints already have open issues or pending vault requests, note that the backlog is aligned with the constraint focus. No new items needed. """ needs = ["update-prerequisite-tree"] [[steps]] id = "journal-and-memory" title = "Write prerequisite tree, journal entry, and periodic memory update" description = """ Three outputs from this step — tree and journal are ALWAYS written, memory is PERIODIC. ### 1. Prerequisite tree (always — committed to git) Write the updated prerequisite tree to: $PROJECT_REPO_ROOT/planner/prerequisite-tree.md This is the tree you built in the update-prerequisite-tree step. Include the "Last updated" comment at the top. ### 2. Journal entry (always — committed to git) Create a daily journal file at: $PROJECT_REPO_ROOT/planner/journal/$(date -u +%Y-%m-%d).md If the file already exists (multiple runs per day), append a new section with a timestamp header. Format: # Planner run — YYYY-MM-DD HH:MM UTC ## Predictions triaged - #NNN: PROMOTE_ACTION/PROMOTE_BACKLOG/WATCH/DISMISS — reasoning (or "No unreviewed predictions" if none) ## Prerequisite tree updates - Resolved: - Discovered: - Proposed: (or "No tree changes" if none) ## Top 3 constraints 1. — blocks N objectives — issue #NNN (existing|filed|already open) 2. — blocks N objectives — issue #NNN 3. — blocks N objectives — issue #NNN ## Issues created - #NNN: title — why (constraint for objectives X, Y) (or "No new issues — constraints already have open issues" if none) ## Observations - Key patterns, resource state, metric trends noticed during this run ## Deferred (in tree, not filed) - Items in the tree beyond the top 3 constraints, and why they're not filed yet Keep each entry concise — 30-50 lines max. ### 3. Memory update (periodic — every 5th run, committed to git) Decide whether to update memory: 1. Count the total number of run entries across ALL journal files in planner/journal/*.md. Each "# Planner run —" header counts as one run. 2. Check the run count noted in MEMORY.md (look for the "" marker at the top). If the marker is missing, treat it as 0. 3. If (current_run_count - last_summarized_count) >= 5, OR if MEMORY.md does not exist, perform the memory update below. 4. Otherwise, skip the memory update — MEMORY.md remains read-only context. When updating memory, write to: $PROJECT_REPO_ROOT/planner/MEMORY.md (replace the entire file) Start the file with the run counter marker: where N is the current total run count. Include: - Date of this summarization - Current constraint focus (top 3 from this run) - Distilled patterns and learnings from recent journal entries - What was observed (resource state, metric trends, project progress) - Strategic direction and watch list for future runs Rules: - Keep under 100 lines total - Replace the file contents — distill from journal, prune stale entries - Focus on PATTERNS and LEARNINGS, not transient state - Do NOT include specific issue counts or numbers that will be stale - Read the recent journal files provided in context for source material - Most recent entries at top Format: simple markdown with dated sections. """ needs = ["file-at-constraints"] [[steps]] id = "commit-and-pr" title = "One commit with all file changes, push, create PR" description = """ Collect all file changes from this run into a single commit. API calls (issue creation, prediction triage) already happened during the run — only file changes (tree, journal, MEMORY.md) need the PR. 1. Check for staged or unstaged changes: cd "$PROJECT_REPO_ROOT" git status --porcelain If there are no file changes, skip this entire step — no commit, no PR. 2. If there are changes: a. Create a branch: BRANCH="chore/planner-$(date -u +%Y%m%d-%H%M)" git checkout -B "$BRANCH" b. Stage prerequisite tree, journal entries, and planner memory: git add planner/prerequisite-tree.md 2>/dev/null || true git add planner/journal/ 2>/dev/null || true git add planner/MEMORY.md 2>/dev/null || true c. Stage any other tracked files modified during the run: git add -u d. Check if there is anything to commit: git diff --cached --quiet && echo "Nothing staged" && skip e. Commit: git commit -m "chore: planner run $(date -u +%Y-%m-%d)" f. Push: git push -u origin "$BRANCH" g. Create a PR: curl -sf -X POST \ -H "Authorization: token $CODEBERG_TOKEN" \ -H "Content-Type: application/json" \ "$CODEBERG_API/pulls" \ -d '{"title":"chore: planner run — prerequisite tree update", "head":"","base":"", "body":"Automated planner run — prerequisite tree update and journal entry."}' h. Return to primary branch: git checkout "$PRIMARY_BRANCH" 3. If the PR creation fails, log and continue — the journal is committed locally. """ needs = ["journal-and-memory"]