436 lines
18 KiB
TOML
436 lines
18 KiB
TOML
# 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: $FACTORY_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: $FACTORY_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 proceed to update-prerequisite-tree.
|
|
|
|
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 (fetch via $CODEBERG_API/labels):
|
|
- <unreviewed_label_id> → prediction/unreviewed
|
|
- <prediction_backlog_label_id> → prediction/backlog
|
|
- <actioned_label_id> → prediction/actioned (create if missing,
|
|
color #c2e0c6, description "Prediction triaged by planner")
|
|
- <backlog_label_id> → backlog
|
|
- <action_label_id> → 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 — <reasoning>".
|
|
|
|
- PROMOTE_BACKLOG: warrants dev work → create a backlog issue.
|
|
Relabel prediction/unreviewed → prediction/actioned, then close
|
|
with comment "Actioned as #NNN — <reasoning>".
|
|
|
|
- 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:
|
|
curl -sf -X POST -H "Authorization: token $CODEBERG_TOKEN" \
|
|
-H "Content-Type: application/json" "$CODEBERG_API/issues" \
|
|
-d '{"title":"...","body":"...","labels":[<label_id>]}'
|
|
b. Comment on the prediction with "Actioned as #NNN — <reasoning>":
|
|
curl -sf -X POST -H "Authorization: token $CODEBERG_TOKEN" \
|
|
-H "Content-Type: application/json" \
|
|
"$CODEBERG_API/issues/<pred_num>/comments" \
|
|
-d '{"body":"Actioned as #NNN — <reasoning>"}'
|
|
c. Relabel: remove prediction/unreviewed, add prediction/actioned:
|
|
curl -sf -X DELETE -H "Authorization: token $CODEBERG_TOKEN" \
|
|
"$CODEBERG_API/issues/<pred_num>/labels/<unreviewed_label_id>"
|
|
curl -sf -X POST -H "Authorization: token $CODEBERG_TOKEN" \
|
|
-H "Content-Type: application/json" \
|
|
"$CODEBERG_API/issues/<pred_num>/labels" \
|
|
-d '{"labels":[<actioned_label_id>]}'
|
|
d. Close the prediction:
|
|
curl -sf -X PATCH -H "Authorization: token $CODEBERG_TOKEN" \
|
|
-H "Content-Type: application/json" \
|
|
"$CODEBERG_API/issues/<pred_num>" \
|
|
-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/<pred_num>/comments" \
|
|
-d '{"body":"Watching — <reasoning>"}'
|
|
b. Replace prediction/unreviewed label with prediction/backlog:
|
|
curl -sf -X DELETE -H "Authorization: token $CODEBERG_TOKEN" \
|
|
"$CODEBERG_API/issues/<pred_num>/labels/<unreviewed_label_id>"
|
|
curl -sf -X POST -H "Authorization: token $CODEBERG_TOKEN" \
|
|
-H "Content-Type: application/json" \
|
|
"$CODEBERG_API/issues/<pred_num>/labels" \
|
|
-d '{"labels":[<prediction_backlog_label_id>]}'
|
|
|
|
For DISMISS:
|
|
a. Comment with explicit reasoning:
|
|
curl -sf -X POST -H "Authorization: token $CODEBERG_TOKEN" \
|
|
-H "Content-Type: application/json" \
|
|
"$CODEBERG_API/issues/<pred_num>/comments" \
|
|
-d '{"body":"Dismissed — <reasoning>"}'
|
|
b. Relabel: remove prediction/unreviewed, add prediction/actioned:
|
|
curl -sf -X DELETE -H "Authorization: token $CODEBERG_TOKEN" \
|
|
"$CODEBERG_API/issues/<pred_num>/labels/<unreviewed_label_id>"
|
|
curl -sf -X POST -H "Authorization: token $CODEBERG_TOKEN" \
|
|
-H "Content-Type: application/json" \
|
|
"$CODEBERG_API/issues/<pred_num>/labels" \
|
|
-d '{"labels":[<actioned_label_id>]}'
|
|
c. Close the prediction:
|
|
curl -sf -X PATCH -H "Authorization: token $CODEBERG_TOKEN" \
|
|
-H "Content-Type: application/json" \
|
|
"$CODEBERG_API/issues/<pred_num>" \
|
|
-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 should note:
|
|
"→ vault approval required" so the planner files it to vault next run.
|
|
|
|
6. **Check vault decisions**: Read any open vault issues to see if
|
|
previously proposed capabilities have been approved or rejected.
|
|
Update the tree accordingly.
|
|
|
|
Write the updated tree to: $FACTORY_ROOT/planner/prerequisite-tree.md
|
|
Use this format:
|
|
|
|
# Prerequisite Tree
|
|
<!-- Last updated: YYYY-MM-DD -->
|
|
|
|
## Objective: <name> (#issue or description)
|
|
- [x] Resolved prerequisite (reference)
|
|
- [ ] Unresolved prerequisite (#issue or description)
|
|
Status: READY | BLOCKED — <reason> | 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:
|
|
curl -sf -X POST \
|
|
-H "Authorization: token $CODEBERG_TOKEN" \
|
|
-H "Content-Type: application/json" \
|
|
"$CODEBERG_API/issues" \
|
|
-d '{"title":"...","body":"...","labels":[<backlog_label_id>]}'
|
|
|
|
Issue body should include:
|
|
- What this prerequisite is
|
|
- Which objectives it blocks (with issue numbers)
|
|
- Why it's a constraint (blocking score)
|
|
- Rough approach if known
|
|
- ## Depends on section if it depends on other open issues
|
|
|
|
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
|
|
|
|
If all top 3 constraints already have open issues, note that the backlog
|
|
is aligned with the constraint focus. No new issues 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:
|
|
$FACTORY_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:
|
|
$FACTORY_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: <list of newly resolved prerequisites>
|
|
- Discovered: <list of newly added prerequisites>
|
|
- Proposed: <list of new capabilities proposed>
|
|
(or "No tree changes" if none)
|
|
|
|
## Top 3 constraints
|
|
1. <prerequisite> — blocks N objectives — issue #NNN (existing|filed|already open)
|
|
2. <prerequisite> — blocks N objectives — issue #NNN
|
|
3. <prerequisite> — 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
|
|
"<!-- summarized-through-run: N -->" 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: $FACTORY_ROOT/planner/MEMORY.md
|
|
(replace the entire file)
|
|
|
|
Start the file with the run counter marker:
|
|
<!-- summarized-through-run: N -->
|
|
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":"<branch>","base":"<primary-branch>",
|
|
"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"]
|