planner: skill + plan-recon agent migrated

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/<milestone>.md` / `docs/plans/NNNN-<iteration>.md`
  → `paths.spec_dir` / `paths.plan_dir`
- `cargo test --workspace -p <crate>` → "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.
This commit is contained in:
2026-05-28 16:05:38 +02:00
parent 808044c0cf
commit 62060ba124
2 changed files with 530 additions and 0 deletions
+275
View File
@@ -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: `<exact path>` — <one-line responsibility>
- Modify: `<exact path>:<line range if known>`
- Test: `<exact path>` — <one-line case description>
```
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: <Component Name>
**Files:**
- Create: `<exact path>`
- Modify: `<exact path>:<line range if known>`
- Test: `<exact path>`
- [ ] **Step 1: Write the failing test**
(exact test source code here)
- [ ] **Step 2: Run test to verify it fails**
Run: `<the project's test command from commands.test, scoped to this test>`
Expected: FAIL with "<exact expected message>"
- [ ] **Step 3: Write minimal implementation**
(exact implementation source code here)
- [ ] **Step 4: Run test to verify it passes**
Run: `<the same test command>`
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
# <Iteration Title> — Implementation Plan
> **Parent spec:** `<path under paths.spec_dir>`
>
> **For agentic workers:** REQUIRED SUB-SKILL: use the
> `implement` skill to run this plan. Steps use `- [ ]`
> checkboxes for tracking.
**Goal:** <one sentence>
**Architecture:** <2-3 sentences>
**Tech Stack:** <key components touched>
---
```
### 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("<substring>")` (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: <iteration> <subject>`).
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 <X>" — 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.
+255
View File
@@ -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 <path>` or
`test -e <path>` 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
- `<path>` — <one-line responsibility>
### Modify
- `<path>:<line range>` — <one-line site description>
- relevant function: `<fn name>` at `<path>:<line>`
- cross-reference: `<other path>:<line>` (lockstep partner)
### Compile-driven site set (signature / variant / removal scopes only)
- channel: `<type-checker stub | git grep -nE '<pattern>' | macro-expansion>`
- raw command output / line count: `<n sites>`
- enumerated paths + line numbers: `<path:line>` (one bullet per site)
- hand-list comparison (advisory): `<n sites listed by hand>`
delta vs. compile-driven set, with the missed sites named.
### Non-compile-checked sites
- drift pins / fixtures / design-doc references / prose mentions:
`<path>` — <one-line site description>
### Spec-named-path existence table
| Path the spec names | Verified by | Exists? | Handling task (file-map row) |
|---------------------|-------------|---------|------------------------------|
| `<path>` | `git ls-files <path>` / `test -e <path>` | yes / no | <where it lands in "Modify" or "Anchors not yet present"> |
### Anchors not yet present
- `<path>` — <what the spec implies must be added but does not exist today>
(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 <X>` or `test -e <X>` 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)