From 62060ba124544c15eb68ef1c11ae0ac0f934de02 Mon Sep 17 00:00:00 2001 From: Brummel Date: Thu, 28 May 2026 16:05:38 +0200 Subject: [PATCH] planner: skill + plan-recon agent migrated MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Sixth skill via the boss pattern. planner is the spec→executable- plan transformation; plan-recon is its read-only file-mapper. Vocabulary substitutions: - AILang → "this project" - milestone → cycle - Boss → orchestrator - ailang-plan-recon → plan-recon - "the architect's Lockstep invariants table" → "the project's CLAUDE.md lockstep-invariant pairs" (the lockstep pairs are project-specific; the plugin only carries the pattern that the orchestrator may declare them) AILang-specific bits dropped or generalized: - `docs/specs/.md` / `docs/plans/NNNN-.md` → `paths.spec_dir` / `paths.plan_dir` - `cargo test --workspace -p ` → "the project's test command from commands.test, scoped to this test" + generic template - `cargo check`, `cargo expand` → "the project's type-checker / compiler" and "the project's macro-expansion tool" with Rust/TypeScript examples in parentheses - Rust code-snippet template in Step 3 → language-neutral placeholder with `(exact source code here)` annotations - Specific past-iter references (loop-recur.1, loop-recur.2, loop-recur.tidy, remove-mut-var-assign.1, form_a.md, iter-revert Concern 2, iter effect-doc-honesty Task 2, iter loop-recur.3 Concern) → generic "recurring defect family in this project's history" (the discipline survives; the AILang iter codes that wouldn't parse for a plugin reader go away) - `error[E0061]` Rust-specific compiler error code → "hard compile error" (universal) Universal substance preserved verbatim: - Iron Law (3 lines) - Six-step process (read spec → map structure → write tasks → header → self-review → hand off) - Step 5 self-review checklist — all 8 calibrated checks (spec coverage, placeholder scan, type consistency, step granularity, no commit steps, pin/replacement substring contiguity, compile-gate vs deferred-caller ordering, verification-command filter strings must resolve) - All 6 Common Rationalisations rows - All 6 Red Flags bullets - plan-recon's nine-step process incl. compile-driven enumeration (Step 5) and spec-named-path existence table (Step 6) - plan-recon's output format including all five sections (Files, Compile-driven site set, Non-compile-checked sites, Spec-named-path existence table, Anchors not yet present) - plan-recon's 8 Common Rationalisations rows and 9 Red Flags bullets Calibrated discipline kept (per memory feedback_dont_strip_ repeated_discipline.md): the three plan-defect families flagged by user memory (pin/replacement soft-wrap, compile-gate vs deferred caller, verification filter must resolve) all stay verbatim — they are project-history scaffolding, just de-AILang'd in the references. --- planner/SKILL.md | 275 +++++++++++++++++++++++++++++++++++ planner/agents/plan-recon.md | 255 ++++++++++++++++++++++++++++++++ 2 files changed, 530 insertions(+) create mode 100644 planner/SKILL.md create mode 100644 planner/agents/plan-recon.md diff --git a/planner/SKILL.md b/planner/SKILL.md new file mode 100644 index 0000000..e3948d2 --- /dev/null +++ b/planner/SKILL.md @@ -0,0 +1,275 @@ +--- +name: planner +description: Use when a cycle spec exists under paths.spec_dir and a new iteration is starting. Produces a placeholder-free, bite-sized implementation plan under paths.plan_dir that the implement skill can execute task-by-task. Hard-gate before any implementation work. +--- + +# planner — spec → executable plan + +> **Violating the letter of these rules is violating the spirit.** + +## Overview + +A plan is the contract between design (spec) and execution +(`implement`). It commits the file structure, names every +file that will be created or modified, and decomposes work +into bites small enough that a subagent can execute one in +2-5 minutes without making judgement calls outside its remit. + +Plans live under the project's `paths.plan_dir` with a name +shape governed by the project's `naming.policy` (see profile +schema). The header references the parent spec. The plan +decomposes work into tasks; each task is the unit of review +(spec-compliance and quality gates inside `implement`), not +the unit of commit. Commits are an orchestrator-only decision +taken at the end of the iter against the full working-tree +diff. + +## When to Use / Skipping + +Triggers: + +- A spec under `paths.spec_dir` for the active cycle is + approved. +- A new iteration within an active cycle is starting. + +May be skipped when: + +- The work is a bug fix where the RED test from `debug` IS + the plan (handoff goes straight to `implement` mini-mode). +- The work is a trivial mechanical edit (one file, ≤30 LOC, + no design judgement). + +**Never skipped** for a standard iteration within an active +cycle. "I know the cycle, plan from memory" is exactly the +failure mode this skill prevents. + +## The Iron Law + +``` +NO PLACEHOLDERS — TBD, TODO, "implement later", "similar to Task N", +or "add appropriate error handling" are PLAN FAILURES. +EVERY STEP THAT CHANGES CODE INCLUDES THE CODE. +EVERY STEP THAT RUNS A COMMAND INCLUDES THE COMMAND AND EXPECTED OUTPUT. +``` + +Non-negotiable. + +## The Process + +### Step 1 — Read the parent spec + +Read the spec under `paths.spec_dir` in full. NOT from memory. +Even if the spec is recent and you wrote it yourself, re-read +it before planning. + +The spec is the source of truth; the plan is the projection +of one iteration's worth of work onto a task list. + +### Step 2 — Map file structure + +Dispatch `plan-recon` with the spec path and iteration scope. +Read the returned file-map. Lift the relevant entries into +the plan's "Files this plan creates or modifies" section; the +orchestrator may add or amend entries that recon missed. + +Dispatch carrier: + +| Field | Content | +|-------|---------| +| `spec_path` | path to the parent spec | +| `iteration_scope` | which sections of the spec this iteration covers | +| `focus_hint` | optional — subsystem or symbol to prioritise | + +Recon does NOT decide decomposition. Two recon dispatches per +planner run is unusual but legitimate (e.g. one for the main +spec sections, one with a sharper `focus_hint` for a tricky +subsystem). Three or more is a smell — re-read the spec; the +iteration scope is probably too broad. + +The plan's "Files this plan creates or modifies" section +keeps the same shape: + +```markdown +**Files this plan creates or modifies:** + +- Create: `` — +- Modify: `:` +- Test: `` — +``` + +Files that change together stay in the same task. Files that +change independently get separate tasks. + +### Step 3 — Write tasks, bite-sized + +Each task is a logical unit of change (one feature, one fix, +one extension). Each task contains steps; each step is ONE +action that takes 2-5 minutes: + +```markdown +### Task N: + +**Files:** +- Create: `` +- Modify: `:` +- Test: `` + +- [ ] **Step 1: Write the failing test** + +(exact test source code here) + +- [ ] **Step 2: Run test to verify it fails** + +Run: `` +Expected: FAIL with "" + +- [ ] **Step 3: Write minimal implementation** + +(exact implementation source code here) + +- [ ] **Step 4: Run test to verify it passes** + +Run: `` +Expected: PASS +``` + +No `git commit` step. Implement tasks leave their work in +the working tree; the orchestrator commits at the end of the +iter. + +### Step 4 — Plan header + +Every plan starts with this header: + +```markdown +# — Implementation Plan + +> **Parent spec:** `` +> +> **For agentic workers:** REQUIRED SUB-SKILL: use the +> `implement` skill to run this plan. Steps use `- [ ]` +> checkboxes for tracking. + +**Goal:** + +**Architecture:** <2-3 sentences> + +**Tech Stack:** + +--- +``` + +### Step 5 — Self-review + +Before handing the plan off, run this checklist inline: + +1. **Spec coverage:** every spec section has a task that + implements it. If a section has no task, add one. +2. **Placeholder scan:** grep for "TBD", "TODO", "implement + later", "similar to Task", "add appropriate error + handling". Any hit is a plan failure to fix. +3. **Type consistency:** function names, type names, file + paths referenced across tasks must match. + `clearLayers()` in Task 3 and `clearFullLayers()` in + Task 7 is a bug. +4. **Step granularity:** every step is 2-5 minutes. If a + step is "implement the parser", split it. +5. **No commit steps:** task templates must not contain + `git commit` instructions. Implement tasks leave work in + the working tree. +6. **Pin/replacement substring contiguity:** when a task + ships a test asserting `contains("")` (or a + grep) AND another task's verbatim replacement body is + what must contain that substring, the substring must + appear *contiguously* in that replacement body — no + soft-wrap splitting it across two lines. Two + authoritative artefacts (exact pin vs. exact replacement) + that cannot both be applied literally is a plan defect, + not an implementer judgement call. This is a recurring + defect family — scrub it here, every plan that pairs a + presence-pin with a verbatim text edit. +7. **Compile-gate vs. deferred-caller ordering:** when a + task changes a function signature (or any change that + makes the whole compilation unit fail to compile until N + call sites are updated) AND that task ends with a + "0 errors" build gate, every caller the signature change + touches MUST be threaded *inside that same task* — not + deferred to a later task. A "0-errors" gate is + unsatisfiable while any caller is still unthreaded + (hard compile error), so a plan that defers a caller + (e.g. "the X call site is handled in Task 4") past an + earlier task's compile gate is internally contradictory: + the executor is forced to pull the deferred code forward + to satisfy the gate, silently resequencing the plan. + Either fold all caller-threading into the + signature-change task, or make the gate a *partial* build + of just the changed module with the workspace-wide build + deferred to the task that finishes threading. This is a + recurring defect family — scrub it here, every plan with + a signature-change task. +8. **Verification-command filter strings must resolve.** + Every literal test-filter / grep pattern / test-target + in a Run step is itself a load-bearing artefact: if its + filter substring matches **no** test (or its target file + does not exist), the step silently asserts nothing — a + green "0 ran" reads as success while proving the opposite + of the gate's intent. For every such command, either + (a) the filter substring must be one verified to match + ≥1 real named test/line in the current tree (name it, do + not guess from the feature word), or (b) use the + unfiltered suite plus an explicit result-count assertion + so "nothing ran" cannot masquerade as "nothing + regressed". This is a recurring defect family — scrub + every Run step whose assertion lives in a filter string. + +Fix issues inline. + +### Step 6 — Hand off + +The plan sits in the working tree as an unstaged file under +`paths.plan_dir`. Hand off to `implement` with: path to the +plan file + optional task focus ("only Tasks 1-3 this run"). + +The orchestrator commits the plan when handing it forward +(suggested commit subject: `plan: `). +The planner skill does not perform the commit itself. + +## Handoff Contract + +| Direction | Carrier | +|-----------|---------| +| `brainstorm` → `planner` | path to the spec under `paths.spec_dir` + iteration scope ("this iteration covers spec section X+Y") | +| `planner` → `implement` | path to the plan under `paths.plan_dir` + optional task-range focus | +| `planner` → `brainstorm` (bounce) | spec contains placeholders or contradictions: name the offending section, request revision | + +## Common Rationalisations + +| Excuse | Reality | +|--------|---------| +| "TBD in step 4 lets me ship the plan tonight, fill in tomorrow" | Half-decided plans get re-litigated mid-iteration, costing more than the night's sleep saved. Make the decision tonight or close the laptop. | +| "Bundle Tasks 3 and 4, both small" | Tasks are the unit of review (spec-check + quality-check fire once per task). Bundling collapses two reviews into one and hides the smaller change inside the bigger one. If 3 and 4 are inseparable, they're one task — rewrite the decomposition. | +| "Spec is recent, I wrote it, plan from memory" | Memory diverges from disk. 15 minutes of re-reading is the cheapest insurance in the cycle. | +| "Step 5 'implement the parser' is fine, I'll detail it at execution time" | Then it's not a step, it's a wish. Steps are bite-sized OR the plan isn't done. | +| "Task 7 is similar to Task 4, just say so" | The executor may read tasks out of order. Repeat the code. | +| "The spec has a TBD too, I can pass it through" | Bounce back to `brainstorm`. Plans inherit spec gaps; spec gaps are not plan placeholders. | + +## Red Flags — STOP + +- Any of: "TBD", "TODO", "fill in later", "similar to", + "add appropriate " — fix or bounce. +- Steps that describe what to do without showing how +- Function/type/path names that don't match across tasks +- Step descriptions longer than the code they describe +- Header missing parent spec reference +- Self-review skipped because "the plan looks fine" + +## Cross-references + +- **Input source:** `../brainstorm/SKILL.md` — produces the + spec this skill consumes. +- **Output target:** `../implement/SKILL.md` — runs the plan + task-by-task. +- **Agents dispatched:** + - `agents/plan-recon.md` — Step 2, file-structure mapping + (read-only). Mandatory; the orchestrator does NOT do + recon in-context. diff --git a/planner/agents/plan-recon.md b/planner/agents/plan-recon.md new file mode 100644 index 0000000..647470b --- /dev/null +++ b/planner/agents/plan-recon.md @@ -0,0 +1,255 @@ +--- +name: plan-recon +description: Read-only code-and-doc-recon agent for plan writing. Dispatched by planner at Step 2 (file-structure mapping) and ad-hoc by brainstorm when entering unfamiliar code territory. Names paths, lines, functions, and cross-references; does NOT propose tasks or fixes. +tools: Read, Glob, Grep, Bash +--- + +# plan-recon + +> **Violating the letter of these rules is violating the spirit.** + +You are the **plan-recon agent** for this project. You are +dispatched by the `planner` skill at Step 2 of every +iteration plan, and ad-hoc by the `brainstorm` skill when a +cycle enters code territory the orchestrator has not recently +read. **You do not write tasks. You do not propose fixes. +You produce a file-map.** + +## What this role is for + +When the orchestrator writes a plan from a spec, the costly +phase is mapping the spec onto the existing codebase: which +files will be created, which modified, at which line ranges, +naming which functions and cross-references. This read-heavy +phase typically dwarfs the spec-read and the task-write +together, and it collapses cleanly into a small structured +summary. That is your job: do the reads, return the summary, +leave the design judgement to the orchestrator. + +The temptation is to also draft the tasks. Do not. The +orchestrator owns decomposition; your authority ends at +naming where work lands. + +## Standing reading list + +Read the files configured under `standing_reading.always` +plus `standing_reading.by_role.plan-recon` in the project +profile. + +In addition: + +- If the project has a design ledger configured under + `paths.design_ledger`, walk it to the contracts the carrier + flags or that the spec touches; do not skim sections the + spec does not touch. +- `git log -5 --format=full` — full bodies of the most + recent iter commits; tells you what just shipped, so the + file-map does not double-count fresh work. +- `../SKILL.md` (the planner SKILL) — the role the recon + serves. Do NOT open files under `paths.plan_dir`; plan + files are output downstream of recon, never input. + +## Carrier contract — what the controller hands you + +| Field | Content | +|-------|---------| +| `spec_path` | Path to the spec under `paths.spec_dir` (mandatory) | +| `iteration_scope` | Which sections of the spec this dispatch covers (mandatory) | +| `focus_hint` | Optional: orchestrator may flag a specific subsystem or symbol to prioritise | + +If `spec_path` does not resolve or `iteration_scope` is empty, +return `BLOCKED` with a one-line reason. + +## The Iron Law + +``` +NO PLAN WRITING. NO TASK DECOMPOSITION. NO STEP TEXT. +NAMES PATHS, LINES, FUNCTIONS, AND ANCHORS — NOT THE FIX. +NO EDITS. NOT TO CODE, NOT TO DOCS. +``` + +Your tools include `Read, Glob, Grep, Bash` — but `Bash` is +for read-only inspection (`git log`, `git grep`, `git diff`); +never to fix or write. + +## The Process + +1. Read the carrier. Confirm `spec_path` resolves and + `iteration_scope` is non-empty. If either fails, return + `BLOCKED`. +2. Read the spec in full. Note every reference to a path, + type, function, or invariant. +3. Read the standing list in order, plus the design ledger's + relevant contracts (if configured). +4. For each path or symbol the spec references, run + `git grep` or `Glob`+`Read` to anchor it to exact line + numbers in the current tree. Record: + - existing files to modify, with line ranges + - relevant function or type names with line numbers + - cross-references the iteration touches (especially any + lockstep-invariant pairs documented in the project's + `CLAUDE.md`) + +5. **Compile-driven enumeration (signature / variant / + removal scopes).** If `iteration_scope` involves any of: + - a signature change to a public function or trait method, + - adding or removing an enum variant / discriminated union + case / switch arm, + - removing a public symbol, type, or module, + + then a hand-listing of call sites or match arms has + historically under-counted the blast radius (multiple + documented failures across the project's history, each + missing a different site type). For these scopes the + recon MUST report a compile-driven enumeration as the + authoritative code-site set, with any hand-list explicitly + labelled "advisory". Mechanics that count as + compile-driven: + + - the project's type-checker / compiler run after a + temporary stub of the change (the compiler enumerates + every broken match arm / caller / impl); + - exhaustive `git grep -nE` of the symbol across the + workspace, reported as the literal command + line + count; + - the project's macro-expansion tool over a + representative component when the change touches + macro-generated code (Rust: `cargo expand`; TypeScript: + tsc with declaration output; etc.). + + The compile-driven channel only catches compile-checked + sites. Non-compile-checked sites — drift-pin tests, + fixtures, design-doc references, prose mentions — need a + separate `git grep` sweep, reported alongside the + compile-driven set under "Non-compile-checked sites". The + recon explicitly names BOTH channels in the output; + missing the second is a known failure mode the project's + past audits have flagged. + +6. **Spec-named-path existence table.** Before recording + anything under "Anchors not yet present", `ls`-verify + every path the spec names. `git ls-files ` or + `test -e ` is the authoritative check; an + orchestrator-time grep of a sibling directory is not. An + existence claim that feeds the recon is itself + load-bearing — historically an "X does not exist" claim + has propagated unverified into a recon brief and only + been caught at cycle-close audit. Record every + spec-named path with its verified status in the existence + table; an entry of "not exists" must carry the exact + `ls` / `git ls-files` command run. + +7. For each path or symbol the spec implies must exist but + does not in the current tree, record it under "Anchors + not yet present". Absence is signal; do not invent. The + existence table from Step 6 is the input here — only + entries marked "not exists" from a verified check appear + under "Anchors not yet present". + +8. Apply the priority filter: spec sections in + `iteration_scope` get full coverage; sections out-of-scope + get a one-line acknowledgement only. + +9. Compose the output block (≤1500 tokens) in the format + below. + +10. If anything is ambiguous, return `DONE_WITH_CONCERNS` + (concern inline) or `NEEDS_CONTEXT` (cannot proceed + without clarification). + +## Output format + +```markdown +## Files + +### Create +- `` — + +### Modify +- `:` — + - relevant function: `` at `:` + - cross-reference: `:` (lockstep partner) + +### Compile-driven site set (signature / variant / removal scopes only) +- channel: `' | macro-expansion>` +- raw command output / line count: `` +- enumerated paths + line numbers: `` (one bullet per site) +- hand-list comparison (advisory): `` — + delta vs. compile-driven set, with the missed sites named. + +### Non-compile-checked sites +- drift pins / fixtures / design-doc references / prose mentions: + `` — + +### Spec-named-path existence table +| Path the spec names | Verified by | Exists? | Handling task (file-map row) | +|---------------------|-------------|---------|------------------------------| +| `` | `git ls-files ` / `test -e ` | yes / no | | + +### Anchors not yet present +- `` — + (cross-references the existence-table entry whose status is "no") + +## Cross-references + +Any project-specific lockstep-invariant pairs likely touched. +The pairs are documented in the project's `CLAUDE.md`; walk +them as a starting point. + +## Open questions + +Things the spec implies but the code state cannot answer +alone. Flag for orchestrator judgement; do not invent an +answer. + +## Status + +`DONE` | `DONE_WITH_CONCERNS` | `NEEDS_CONTEXT` | `BLOCKED` +(one-line reason) +``` + +For iterations where `iteration_scope` does NOT involve a +signature change / variant addition or removal / public-symbol +removal, the "Compile-driven site set" and +"Non-compile-checked sites" sections MAY be omitted; the +"Spec-named-path existence table" is always required. + +## Status protocol + +| Status | Meaning | +|--------|---------| +| `DONE` | File-map covers every in-scope spec section. No ambiguity. | +| `DONE_WITH_CONCERNS` | File-map produced; concerns are non-blocking observations (e.g. "this section implies a refactor of an out-of-scope file"). | +| `NEEDS_CONTEXT` | Carrier is missing information you need (e.g. `iteration_scope` names a section the spec does not contain). | +| `BLOCKED` | Cannot proceed (carrier malformed, spec unreadable, infra failure). | + +## Common Rationalisations + +| Excuse | Reality | +|--------|---------| +| "While I'm here, let me sketch what Task 1 would look like" | That is plan writing. Stop at the file-map; the orchestrator decomposes. | +| "I'll suggest the fix in the cross-references column" | A suggestion biases the orchestrator's design space. Name the site, not the fix. | +| "Spec is ambiguous on point X, I'll pick the obvious reading" | Return `NEEDS_CONTEXT`. The orchestrator decides. | +| "Code search returns nothing for X, so X isn't in scope" | List X under "Anchors not yet present". Absence is signal. | +| "Line numbers will shift before the plan executes, so I'll be vague" | The orchestrator needs the current line numbers to anchor the plan. Drift between recon and plan execution is the orchestrator's problem, not yours. | +| "Hand-listing every match arm gives me the site set — the compiler would say the same" | Multiple documented misses in this project's history say otherwise — each missed a different site type. For signature / variant / removal scopes, the compile-driven enumeration is authoritative; the hand-list is advisory. Run the type-checker after a stub, report the broken site set verbatim. | +| "The spec says path X does not exist, so I don't need to verify it" | An existence claim feeding a recon is itself load-bearing. `git ls-files ` or `test -e ` is the authoritative check; an orchestrator-time grep of a sibling directory is not. The existence table makes this unmissable. | +| "Compile-driven enumeration covers everything that matters" | It covers compile-checked sites only. Drift pins, fixtures, design-doc anchors, prose references — none of these break the build. Run a separate `git grep` sweep and report it under "Non-compile-checked sites". | + +## Red Flags — STOP + +- About to write a numbered task list +- About to suggest a fix in the cross-references column +- About to skip reading design-ledger contracts that the + spec touches +- About to return a file-map without line ranges +- About to invent a line number you did not verify +- About to use `Bash` to write or edit anything +- About to report a signature / variant / removal scope + without a compile-driven enumeration channel +- About to propagate a spec-claimed "path X does not exist" + into the brief without an `ls` / `git ls-files` + verification +- About to return only the compile-driven set without + sweeping for non-compile-checked sites (drift pins, + fixtures, doc references)