feat(pipeline): consolidate cycle recon, add fieldtest probe tier, enforce assumption economy

Implements the three efficiency findings from the #310 pipeline
measurement (refs #35): ~691k subagent tokens / ~3.5 h for a
deliberately small cycle, with recon duplication as the single
biggest obligatory per-cycle cost.

1. Cycle recon (pipeline.md § Cycle recon; specify, planner,
   brainstorm, boss, plan-recon): recon fan-out is budgeted per
   cycle, not per phase. One full plan-recon dispatch — normally
   specify Step 1, in a new pre-spec sources/recon_scope carrier
   form — serves both the spec's concrete code shapes and the
   plan's file-map. planner Step 2 becomes reuse-first (freshness /
   quotability / coverage conditions) with narrowly-bounded delta
   dispatches (new Delta dispatch contract in plan-recon.md);
   broad Explore-type grounding sweeps are banned in every mode.
   Field evidence: 151k (Explore) + 99k (plan-recon) with ~40 %
   overlap in one bounded cycle.

2. Fieldtest probe tier (fieldtest, fieldtester, pipeline.md,
   agent-template.md § model rule 2): a cycle whose user-visible
   delta is a single narrow axis dispatches the per-cycle
   fieldtest at tier: probe — 1-2 examples on exactly that axis,
   ~200-word report, explicit model:sonnet dispatch override (no
   dispatch-level effort override exists; frontmatter effort
   applies). Probe is a tier, not a skip; the milestone fieldtest
   always runs full tier on the frontmatter model, keeping the
   milestone-close gate's >=2-example floor unconditional.

3. Assumption economy (specify Step 3 + self-review item 6):
   grounding-check cost scales with the spec's assumption count,
   so specs state current-behaviour claims only where the change
   relies on them (the gate's own falsity test), demote
   context-only mentions to ledger citations, and keep iteration
   scope tight — fewer restatements, never fewer reliances. The
   gate itself is unchanged.

Design reviewed pre-implementation (opus plan review); diff
adversarially verified by a 3-lens review workflow (consistency,
operability, cross-reference), confirmed findings folded in.

refs #35
This commit is contained in:
2026-07-25 13:52:04 +02:00
parent b2c92db725
commit fe5ca8f8c0
9 changed files with 353 additions and 67 deletions
+7
View File
@@ -177,6 +177,13 @@ new feature work with no spec or plan yet, run the **Entry-path
reflection** below before dispatching anything. Read each skill's reflection** below before dispatching anything. Read each skill's
`SKILL.md` trigger section if unsure. `SKILL.md` trigger section if unsure.
**Pre-dispatch grounding stays light.** Queue triage reads issue
bodies and `git log`; it does not fan out. A code-territory read an
item needs belongs to the dispatched phase skill's own recon step —
the cycle recon (`../docs/pipeline.md` § Cycle recon) — and is
never a broad Explore-type sweep fired before, or instead of, that
step.
**Entry-path reflection (feature work).** Entry paths form an **ordered **Entry-path reflection (feature work).** Entry paths form an **ordered
cascade**, and the choice is made by reflection every time, not by cascade**, and the choice is made by reflection every time, not by
habit. The discriminator is no longer a single "design line": it adds a habit. The discriminator is no longer a single "design line": it adds a
+7 -4
View File
@@ -152,8 +152,9 @@ unconditional default.
### Step 3 — Populate the space with the synthetic-user swarm ### Step 3 — Populate the space with the synthetic-user swarm
Dispatch the read-only `agents/synthetic-user.md` agent **in parallel, one per Dispatch the read-only `agents/synthetic-user.md` agent **in parallel, one per
stance, in a single turn** — the same ad-hoc fan-out brainstorm already uses stance, in a single turn** — the same ad-hoc fan-out `specify` uses
for `plan-recon` and `specify` uses for `spec-skeptic`. No Workflow wrapper: a for `spec-skeptic`. (brainstorm's ad-hoc `plan-recon` is a SINGLE dispatch,
never a fan-out — `../docs/pipeline.md` § Cycle recon.) No Workflow wrapper: a
one-shot stateless fan-out has no re-loop, no ordering, and no in-code verdict one-shot stateless fan-out has no re-loop, no ordering, and no in-code verdict
to enforce, so it is a direct dispatch, not an `implement-loop`-style script. to enforce, so it is a direct dispatch, not an `implement-loop`-style script.
@@ -343,8 +344,10 @@ reflection for how this folds `brainstorm` into the autonomous cascade.
- **Autonomous behaviour:** `../boss/SKILL.md` § Direction freedom (trigger 4) - **Autonomous behaviour:** `../boss/SKILL.md` § Direction freedom (trigger 4)
and the Entry-path reflection — how the autonomous swarm-brainstorm decides and the Entry-path reflection — how the autonomous swarm-brainstorm decides
convergent forks and bounces only divergent ones. convergent forks and bounces only divergent ones.
- **Ad-hoc dispatch.** The orchestrator MAY also ad-hoc dispatch - **Ad-hoc dispatch.** The orchestrator MAY ad-hoc dispatch
`../planner/agents/plan-recon.md` during Step 1 when the cycle enters code `../planner/agents/plan-recon.md` during Step 1 when the cycle enters code
territory not recently read; ad-hoc, not part of the standard process. territory not recently read. Subject to the coverage condition in
`../docs/pipeline.md` § Cycle recon, that dispatch then IS the cycle's one
full recon; `specify` and `planner` reuse its report.
- **Project feature-acceptance criterion:** declared in the project's - **Project feature-acceptance criterion:** declared in the project's
`CLAUDE.md` — applied prospectively by `specify` (its Step 2), not here. `CLAUDE.md` — applied prospectively by `specify` (its Step 2), not here.
+10 -1
View File
@@ -136,7 +136,16 @@ Assignment rule, in priority order:
gating a cycle (adversarial lenses, plan recon, field/bench gating a cycle (adversarial lenses, plan recon, field/bench
evidence, the in-loop quality review) — run `opus`. Opus is evidence, the in-loop quality review) — run `opus`. Opus is
the documented strength for code review, debugging, and deep the documented strength for code review, debugging, and deep
reasoning. reasoning. One conditioned exception (issue #35): the
per-cycle fieldtest's **probe tier** — the cycle's
user-visible delta is a single narrow axis — is dispatched by
the `fieldtest` skill with an explicit `model: sonnet`
dispatch override; the one-axis scope bounds the judgement
surface and the orchestrator triages every finding anyway.
The frontmatter keeps the opus default, which the milestone
fieldtest always runs. (The direct agent-dispatch surface has
no per-dispatch *effort* override, so the frontmatter effort
applies unchanged on a probe.)
3. **Mechanical scope** — tightly-scoped execution of a pre-made 3. **Mechanical scope** — tightly-scoped execution of a pre-made
plan, recon, extraction, compliance-diffing run `sonnet` plan, recon, extraction, compliance-diffing run `sonnet`
(near-opus coding quality at lower latency and cost). (near-opus coding quality at lower latency and cost).
+82 -5
View File
@@ -92,7 +92,10 @@ hold:
`bug` findings; `friction` / `spec_gap` findings resolved or `bug` findings; `friction` / `spec_gap` findings resolved or
ratified into the design ledger. A `clean` roll-up is honoured ratified into the design ledger. A `clean` roll-up is honoured
only with positive evidence the test actually ran — at least only with positive evidence the test actually ran — at least
2 examples in the working tree (`examples_added`). A `clean` 2 examples in the working tree (`examples_added`). A milestone
fieldtest always runs **full tier** (the probe tier is
per-cycle only — `../fieldtest/SKILL.md` § Two tiers: probe
and full), so this floor is unconditional. A `clean`
with fewer, over a milestone that touched user-visible surface, with fewer, over a milestone that touched user-visible surface,
is itself a `spec_gap`, not a clean close (the agent owes the is itself a `spec_gap`, not a clean close (the agent owes the
same floor — `fieldtest/agents/fieldtester.md` "What you DO NOT same floor — `fieldtest/agents/fieldtester.md` "What you DO NOT
@@ -151,6 +154,74 @@ judgement call like the `fieldtest` / `docwriter` dispatches,
not an arm of the selector cascade; the trigger is observed not an arm of the selector cascade; the trigger is observed
accumulation of settled single-issue items. accumulation of settled single-issue items.
## Cycle recon
Recon fan-out is budgeted **per cycle, not per phase**. The field
evidence behind the budget (issue #35, one bounded cycle): a broad
Explore-type sweep grounding `specify` (151k tokens) plus the
`plan-recon` dispatch grounding `planner` (99k tokens) overlapped
~40 % — two full recon passes over the same territory, the single
biggest obligatory per-cycle cost. This section is the single
source of the budget, reuse, and delta rules; the skills point
here and add only their own dispatch mechanics.
- **At most ONE full recon dispatch while a valid cycle recon
exists** — the **cycle recon**. It is a `plan-recon` dispatch
(`../planner/agents/plan-recon.md`) in the pre-spec carrier
form, normally fired at `specify` Step 1 when the work enters
code territory the orchestrator has not recently read; an
ad-hoc dispatch `brainstorm` fired earlier in the same cycle is
the cycle recon instead (subject to the coverage condition
below). One report serves every downstream consumer: the spec's
concrete code shapes and the plan's file-map. Normally that is
one full dispatch per cycle; a further full dispatch is
legitimate only via the fallback below.
- **Broad exploration sweeps are not recon.** No Explore-type
fan-out for spec, plan, or pre-dispatch grounding — grounding
fan-out goes through the one cycle-recon dispatch. This binds
the orchestrator in every mode, `/boss` included.
- **Reuse over re-dispatch.** A downstream phase reuses the
cycle-recon report while ALL of these hold:
- *Freshness* — no commit since the dispatch has touched the
paths the report maps (`git log <since-recon> -- <mapped
paths>` is empty; commits elsewhere — fixtures, tracker
side-effects — do not invalidate it). In a multi-iteration
cycle, iteration 1's commits to mapped paths invalidate the
report for iteration 2's planning.
- *Quotability* — the orchestrator can still quote the report's
Modify entries verbatim. A summarized memory of the report is
not the report. (The designed reuse window is the standard
same-session `specify``planner` handoff; across a session
break the fallback below applies.)
- *Coverage* (brainstorm-fired recons only) — the ratified
design's territory appears in the report's file-map or
existence-table rows, not merely as a one-line out-of-scope
acknowledgement. Missing territory is a delta below; a design
that pivoted wholly outside the mapped territory voids the
dispatch as cycle recon, and `specify` fires the cycle recon
afresh.
- **Delta dispatches do not count against the budget.** A delta
is a narrow re-dispatch whose `focus_hint` names the gap; its
bounded contract is **Delta dispatch** in
`../planner/agents/plan-recon.md`. Triggers:
- the spec names a path or symbol that appears in neither the
cycle recon's file-map nor its existence table;
- MANDATORY: the iteration scope is a signature / variant /
removal or content-pin-perturbing scope and the cycle recon
carries no compile-driven site set for it — `planner` MUST
delta-dispatch before planning that scope. (`specify` avoids
this delta by naming the anticipated change shape in
`recon_scope`, arming the compile-driven enumeration inside
the cycle recon itself.)
Two deltas in one planner run is the smell that the iteration
scope is too broad — re-read the spec.
- **A full re-dispatch is the bounded fallback, not a failure**:
no cycle recon exists (a direct tidy dispatch that skipped
`specify`; a session break), or a reuse condition failed. The
cost regression is capped at the pre-consolidation status quo —
one full dispatch.
## Phase descriptions ## Phase descriptions
### brainstorm ### brainstorm
@@ -184,7 +255,9 @@ the `grounding-check` gate, and takes sign-off — with review but no
interview. Under the bold stance it decides every load-bearing fork it interview. Under the bold stance it decides every load-bearing fork it
can *derive* an answer for and records the decision in the run's can *derive* an answer for and records the decision in the run's
reference issue; it bounces to `brainstorm` only when a fork hangs on a reference issue; it bounces to `brainstorm` only when a fork hangs on a
pure user preference no source settles. A core node — the spec-production pure user preference no source settles. Its code grounding comes from
the single cycle-recon dispatch (§ Cycle recon), never a broad
exploration sweep. A core node — the spec-production
gate before `planner` on every design path. gate before `planner` on every design path.
Outside `/boss` the sign-off is the user's. Under `/boss` the autonomous Outside `/boss` the sign-off is the user's. Under `/boss` the autonomous
@@ -210,8 +283,10 @@ Hard-gate before implement. Produces a placeholder-free,
bite-sized implementation plan in `docs/plans` (an ephemeral, bite-sized implementation plan in `docs/plans` (an ephemeral,
git-ignored working file, shell-`rm`'d alongside the spec at cycle git-ignored working file, shell-`rm`'d alongside the spec at cycle
close — see `conventions.md` § Lifecycle) that the implement skill can close — see `conventions.md` § Lifecycle) that the implement skill can
execute task-by-task. Dispatches execute task-by-task. Its file-map normally comes from reusing the
the plan-recon agent for read-only file-structure mapping. cycle-recon report (§ Cycle recon); it dispatches `plan-recon` only
for that section's delta triggers, or in full when no valid cycle
recon exists.
### implement ### implement
@@ -282,7 +357,9 @@ practices inside `implement`.
Optional. Orchestrator-dispatched after the audit closes clean Optional. Orchestrator-dispatched after the audit closes clean
on a cycle that touched user-visible surface. Picks 2-4 real- on a cycle that touched user-visible surface. Picks 2-4 real-
world tasks within the cycle's scope, implements them using world tasks within the cycle's scope (probe tier, for a
single-axis surface delta: 1-2 tasks on that axis — see
`../fieldtest/SKILL.md` § Two tiers: probe and full), implements them using
only the design ledger and public examples (never the language's only the design ledger and public examples (never the language's
own implementation), runs the results, and writes a friction- own implementation), runs the results, and writes a friction-
and-bug spec. and-bug spec.
+50 -11
View File
@@ -1,6 +1,6 @@
--- ---
name: fieldtest name: fieldtest
description: Orchestrator-dispatched only, after audit closes clean (or with ratified drift only), when the orchestrator judges the iteration is complete and wants a field test. Picks 2-4 real-world tasks within the cycle's scope, implements each as a downstream consumer of the project, runs them, and writes a friction-and-bug spec. The spec feeds the next plan as a reference. Implementer simulates a downstream user who has only the public interface — never the project's implementation source. description: Orchestrator-dispatched only, after audit closes clean (or with ratified drift only), when the orchestrator judges the iteration is complete and wants a field test. Picks 2-4 real-world tasks within the cycle's scope (probe tier, for a single-axis surface delta: 1-2 tasks on that axis), implements each as a downstream consumer of the project, runs them, and writes a friction-and-bug spec. The spec feeds the next plan as a reference. Implementer simulates a downstream user who has only the public interface — never the project's implementation source.
--- ---
# fieldtest — usability field test for a shipped cycle # fieldtest — usability field test for a shipped cycle
@@ -24,8 +24,8 @@ next to cycle-design specs under `docs/specs`, with a
`fieldtest-` prefix in the slug. `fieldtest-` prefix in the slug.
The substantive process — read the design ledger + cycle spec The substantive process — read the design ledger + cycle spec
+ recent iter/audit commit bodies, pick 2-4 real-world tasks + recent iter/audit commit bodies, pick the dispatched tier's
per cycle axis, implement each as a downstream consumer, run examples, implement each as a downstream consumer, run
the result, classify findings, write the spec — lives in the result, classify findings, write the spec — lives in
`agents/fieldtester.md`. That file also carries the spec `agents/fieldtester.md`. That file also carries the spec
template, the source-isolation discipline (no reading under template, the source-isolation discipline (no reading under
@@ -61,6 +61,44 @@ here. The milestone fieldtest is skippable on the same terms as
the per-cycle one, lifted to milestone level: a milestone whose the per-cycle one, lifted to milestone level: a milestone whose
entire scope is internal (no user-visible surface) is exempt. entire scope is internal (no user-visible surface) is exempt.
## Two tiers: probe and full
Orthogonal to scope, the **per-cycle** fieldtest runs at one of
two tiers. The orchestrator picks the tier at dispatch and
carries it in the carrier's `tier` field — the field, its
example bounds, and its report cap are defined authoritatively
under **Carrier contract** in `agents/fieldtester.md` and
deliberately not restated here:
- **Full** (the default; `tier` absent): one example per axis
the cycle touched — the shape the rest of this file describes.
- **Probe**: for a cycle whose user-visible delta is a **single
narrow axis** — one flag, one diagnostic, one configuration
key. The carrier's `axis_hints` then names exactly that one
axis. Field evidence (issue #35): a full fieldtest cost ~89k
tokens on a one-flag cycle even at minimal example count, yet
that same dispatch found a real self-description bug — the
probe tier keeps that signal at a fraction of the cost.
**Probe is a tier, not a skip.** The skip rules below are
unchanged: a surface-touching cycle still runs its fieldtest;
probe merely right-sizes the dispatch when the surface delta is
one axis. A multi-axis cycle takes the full tier — when in
doubt, full.
**Probe dispatch mechanics.** A probe dispatch passes an
explicit `model: sonnet` override on the agent dispatch — the
dispatch-level model override takes precedence over the agent's
opus frontmatter; the conditioned exception is documented in
`../docs/agent-template.md` § model, rule 2. (The direct
agent-dispatch surface has no per-dispatch effort override, so
the frontmatter effort applies unchanged; the probe's economy
comes from the model tier and the one-axis scope.) The
judgement surface a probe covers is one axis, and the
orchestrator triages every finding anyway. The **milestone
fieldtest always runs full tier on the frontmatter model** — its
carrier takes no `tier` field.
## When to Use / Skipping ## When to Use / Skipping
Triggers: Triggers:
@@ -116,11 +154,12 @@ carrier.
## Dispatch ## Dispatch
Dispatch `fieldtester` with the carrier from the Handoff Dispatch `fieldtester` with the carrier from the Handoff
Contract below. The agent picks 2-4 examples (one per axis Contract below. The agent picks its examples per the dispatched
the cycle touched; fewer loses the signal on variation, more tier (bounds in the `tier` carrier row; on full, one per axis
overflows one readable report), implements them as downstream the cycle touched — fewer loses the signal on variation, more
consumers, runs them, classifies findings, and writes the overflows one readable report), implements them as
spec. The two artefact kinds part ways at commit: the **fixtures are downstream consumers, runs them, classifies findings, and
writes the spec. The two artefact kinds part ways at commit: the **fixtures are
committed as normal code** (they are consumer test assets — suggested committed as normal code** (they are consumer test assets — suggested
subject `fieldtest: <cycle> — <N> examples, <K> findings`), while the subject `fieldtest: <cycle> — <N> examples, <K> findings`), while the
**fieldtest spec is a git-ignored working file that is never committed**. **fieldtest spec is a git-ignored working file that is never committed**.
@@ -137,10 +176,10 @@ tree as the next cycle's input; it plays no part in the delete decision
## Handoff Contract ## Handoff Contract
`fieldtest` consumes (from orchestrator at cycle close) the carrier `fieldtest` consumes (from orchestrator at cycle close) the carrier
fields `cycle_id`, `cycle_scope`, `axis_hints`, and `commit_range`, fields `cycle_id`, `cycle_scope`, `axis_hints`, `commit_range`, and
defined authoritatively under **Carrier contract** in `tier`, defined authoritatively under **Carrier contract** in
`agents/fieldtester.md` (which also specifies the empty-`axis_hints` `agents/fieldtester.md` (which also specifies the empty-`axis_hints`
fallback). fallback and the absent-`tier` default).
`fieldtest` produces, for the orchestrator, the fields `spec_path`, `fieldtest` produces, for the orchestrator, the fields `spec_path`,
`examples_added`, and `findings`, defined authoritatively under `examples_added`, and `findings`, defined authoritatively under
+16 -7
View File
@@ -1,6 +1,6 @@
--- ---
name: fieldtester name: fieldtester
description: Implements 2-4 real-world tasks against the project as a downstream consumer would, runs them, and reports friction, bugs, and spec gaps as a structured spec. Simulates a downstream user who has only the public interface (the project's design ledger, READMEs, public docs, and example corpus) — never the implementation source. Does NOT fix bugs. description: Implements 2-4 real-world tasks (probe tier: 1-2 on a single axis) against the project as a downstream consumer would, runs them, and reports friction, bugs, and spec gaps as a structured spec. Simulates a downstream user who has only the public interface (the project's design ledger, READMEs, public docs, and example corpus) — never the implementation source. Does NOT fix bugs.
tools: Read, Edit, Write, Bash, Glob, Grep tools: Read, Edit, Write, Bash, Glob, Grep
model: opus model: opus
effort: xhigh effort: xhigh
@@ -71,10 +71,13 @@ skill references it rather than restating it.
| `cycle_scope` | 1-3 sentences naming what shipped | | `cycle_scope` | 1-3 sentences naming what shipped |
| `axis_hints` | bullet list, one per axis to probe | | `axis_hints` | bullet list, one per axis to probe |
| `commit_range` | `<prev-cycle-close>..HEAD` | | `commit_range` | `<prev-cycle-close>..HEAD` |
| `tier` | `probe`, or absent (= full). Full: 2-4 examples, one per axis. Probe: the cycle's user-visible delta is a single narrow axis — 1-2 examples on exactly that axis, report capped at ~200 words; `axis_hints` MUST name exactly that one axis, so a probe carrier with zero or several `axis_hints` entries is malformed — return `NEEDS_CONTEXT`. Per-cycle dispatches only; the milestone variant below takes no `tier`. |
If `axis_hints` is empty, infer from the cycle's spec under If `axis_hints` is empty, infer from the cycle's spec under
`docs/specs` and the most recent iter commit bodies; if `docs/specs` and the most recent iter commit bodies; if
both are also empty, return `NEEDS_CONTEXT`. both are also empty, return `NEEDS_CONTEXT`. (This inference
fallback is full-tier only — on a probe dispatch an empty
`axis_hints` is malformed, per the `tier` row.)
### Milestone-scope variant ### Milestone-scope variant
@@ -97,7 +100,9 @@ scenarios as the union of per-cycle axes; the per-cycle
fieldtests already covered axis-local surface. There is no fieldtests already covered axis-local surface. There is no
`axis_hints` fallback in this mode: if `milestone_promise` is `axis_hints` fallback in this mode: if `milestone_promise` is
empty, infer it from the milestone's spec / tracker entry, and empty, infer it from the milestone's spec / tracker entry, and
if that is also empty, return `NEEDS_CONTEXT`. if that is also empty, return `NEEDS_CONTEXT`. The milestone
carrier takes no `tier` field — a milestone fieldtest is always
full tier.
## The Iron Law ## The Iron Law
@@ -143,7 +148,10 @@ Each phase completes before the next starts.
transformation pipeline. Tasks that do NOT work: a transformation pipeline. Tasks that do NOT work: a
one-liner that exercises a single primitive (too thin) or one-liner that exercises a single primitive (too thin) or
a large multi-module application (too thick). a large multi-module application (too thick).
3. Total: 2-4 examples. 3. Total: 2-4 examples (full tier). On a `tier: probe` dispatch:
1-2 examples, on exactly the single delta axis the carrier
names — the tier bound comes from the carrier, never from
you trimming a full dispatch.
### Phase 2 — Implement each example as a downstream consumer ### Phase 2 — Implement each example as a downstream consumer
@@ -280,7 +288,7 @@ End every report with exactly one of:
## Output format ## Output format
At most 350 words, structured: At most 350 words (probe tier: 200), structured:
- **Status:** one of the four above. - **Status:** one of the four above.
- **Examples:** one bullet per example: path + 1-line task - **Examples:** one bullet per example: path + 1-line task
@@ -321,7 +329,8 @@ fields are:
- A `friction` finding without a 1-line recommendation. - A `friction` finding without a 1-line recommendation.
Every finding is actionable or it isn't a finding. Every finding is actionable or it isn't a finding.
- An "all-clean" report on a cycle that touched user-visible - An "all-clean" report on a cycle that touched user-visible
surface without at least 2 examples in the working tree. surface without at least 2 examples in the working tree
(`tier: probe`: at least 1, on the delta axis).
The empty report is a valid status only if no examples The empty report is a valid status only if no examples
were applicable — and that itself is a `spec_gap` finding. were applicable — and that itself is a `spec_gap` finding.
@@ -336,7 +345,7 @@ fields are:
| "The design ledger is fuzzy on this corner, I'll pick the natural reading and proceed" | Pick the reading, RUN the example, AND record `spec_gap` with the reading you picked and why another reading was equally plausible. | | "The design ledger is fuzzy on this corner, I'll pick the natural reading and proceed" | Pick the reading, RUN the example, AND record `spec_gap` with the reading you picked and why another reading was equally plausible. |
| "Bug found — I'll just fix it now, faster than handing off to debug" | Fix-in-place violates the skill split. The fix lands in a separate, RED-tested commit via `debug``implement`. | | "Bug found — I'll just fix it now, faster than handing off to debug" | Fix-in-place violates the skill split. The fix lands in a separate, RED-tested commit via `debug``implement`. |
| "Two examples both ran clean, no findings — short report" | A clean run is itself a finding (`working`). Record what was reached for, what diagnostic showed up when wrong, what was easy. Wins protect the feature from drift. | | "Two examples both ran clean, no findings — short report" | A clean run is itself a finding (`working`). Record what was reached for, what diagnostic showed up when wrong, what was easy. Wins protect the feature from drift. |
| "Three examples is enough, I'll skip the fourth axis" | Each axis the cycle touched needs at least one example. Skipping an axis silently turns the field test into a partial signal, which is worse than no signal because the orchestrator will read it as full coverage. | | "Three examples is enough, I'll skip the fourth axis" | On a full-tier dispatch, each axis the cycle touched needs at least one example. Skipping an axis silently turns the field test into a partial signal, which is worse than no signal because the orchestrator will read it as full coverage. (A probe dispatch is scoped to its single axis by the carrier — that is the orchestrator's cut, not yours to imitate on a full dispatch.) |
## Red Flags — STOP and re-read the public interface ## Red Flags — STOP and re-read the public interface
+33 -13
View File
@@ -80,20 +80,37 @@ of one iteration's worth of work onto a task list.
### Step 2 — Map file structure ### Step 2 — Map file structure
Dispatch `plan-recon` with the spec path and iteration scope. The file-map normally already exists: the **cycle recon** report
Read the returned file-map. Lift the relevant entries into (`../docs/pipeline.md` § Cycle recon), dispatched during `specify`
the plan's "Files this plan creates or modifies" section; the Step 1 or ad-hoc by `brainstorm`. Reuse it while that section's
reuse conditions — freshness, quotability, coverage — hold. 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. orchestrator may add or amend entries that recon missed.
Dispatch carrier: see the **Carrier contract** table in Dispatch `plan-recon` only:
`agents/plan-recon.md` (single source for `spec_path`,
`iteration_scope`, `focus_hint`).
Recon does NOT decide decomposition. Two recon dispatches per - as a **delta**, on the § Cycle recon delta triggers — a narrow
planner run is unusual but legitimate (e.g. one for the main dispatch whose `focus_hint` names the gap, bounded by the
spec sections, one with a sharper `focus_hint` for a tricky **Delta dispatch** contract in `agents/plan-recon.md`;
subsystem). Three or more is a smell — re-read the spec; the - in **full**, when no valid cycle recon exists (a direct tidy
iteration scope is probably too broad. dispatch that skipped `specify`, a session break, or a reuse
condition failed).
Either way the orchestrator does NOT do recon in-context: reuse
means reusing a prior *dispatch's* report, never the
orchestrator's own tree-walking.
Dispatch carrier: see the **Carrier contract** tables in
`agents/plan-recon.md` (single source for `spec_path`,
`iteration_scope`, `focus_hint`; the pre-spec `sources` form is
`specify`'s dispatch, not planner's).
Recon does NOT decide decomposition. Two *delta* dispatches in
one planner run is the smell that the iteration scope is too
broad — re-read the spec. A second *full* dispatch in a cycle
that already has a valid cycle recon is a § Cycle recon budget
violation, not a judgement call.
The plan's "Files this plan creates or modifies" section The plan's "Files this plan creates or modifies" section
keeps the same shape: keeps the same shape:
@@ -282,5 +299,8 @@ shell-`rm`'d alongside the spec at cycle close by `audit` (see
task-by-task. task-by-task.
- **Agents dispatched:** - **Agents dispatched:**
- `agents/plan-recon.md` — Step 2, file-structure mapping - `agents/plan-recon.md` — Step 2, file-structure mapping
(read-only). Mandatory; the orchestrator does NOT do (read-only). Normally reused from the cycle recon
recon in-context. (`../docs/pipeline.md` § Cycle recon); dispatched here only
for deltas, or in full when no valid cycle recon exists. The
file-map always comes from a dispatch's report; the
orchestrator does NOT do recon in-context.
+90 -22
View File
@@ -1,6 +1,6 @@
--- ---
name: plan-recon 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. description: Read-only code-and-doc-recon agent. Normally dispatched once per cycle as the cycle recon (specify Step 1 in pre-spec form, or ad-hoc by brainstorm) and reused downstream; planner re-dispatches only for deltas or in full when no valid cycle recon exists. Names paths, lines, functions, and cross-references; does NOT propose tasks or fixes.
tools: Read, Glob, Grep, Bash tools: Read, Glob, Grep, Bash
model: opus model: opus
effort: xhigh effort: xhigh
@@ -11,11 +11,15 @@ effort: xhigh
> **Violating the letter of these rules is violating the spirit.** > **Violating the letter of these rules is violating the spirit.**
You are the **plan-recon agent** for this project. You are You are the **plan-recon agent** for this project. You are
dispatched by the `planner` skill at Step 2 of every normally dispatched **once per cycle** — the cycle recon
iteration plan, and ad-hoc by the `brainstorm` skill when a (`../../docs/pipeline.md` § Cycle recon): by the `specify`
cycle enters code territory the orchestrator has not recently skill at Step 1 in the pre-spec form below, or ad-hoc by the
read. **You do not write tasks. You do not propose fixes. `brainstorm` skill, whenever the cycle enters code territory
You produce a file-map.** the orchestrator has not recently read. Your report is then
reused downstream; the `planner` skill re-dispatches you only
for a narrow delta (a sharp `focus_hint` naming a gap) or in
full when no valid cycle recon exists. **You do not write
tasks. You do not propose fixes. You produce a file-map.**
## What this role is for ## What this role is for
@@ -26,7 +30,10 @@ naming which functions and cross-references. This read-heavy
phase typically dwarfs the spec-read and the task-write phase typically dwarfs the spec-read and the task-write
together, and it collapses cleanly into a small structured together, and it collapses cleanly into a small structured
summary. That is your job: do the reads, return the summary, summary. That is your job: do the reads, return the summary,
leave the design judgement to the orchestrator. leave the design judgement to the orchestrator. In the
pre-spec form the same read runs one phase earlier — mapping
the design sources — and its report serves `specify`'s
concrete code shapes first, then `planner`'s file-map.
The temptation is to also draft the tasks. Do not. The The temptation is to also draft the tasks. Do not. The
orchestrator owns decomposition; your authority ends at orchestrator owns decomposition; your authority ends at
@@ -42,8 +49,8 @@ In addition:
- If the project has a design ledger (its CLAUDE.md project - If the project has a design ledger (its CLAUDE.md project
facts), walk it to the contracts the carrier facts), walk it to the contracts the carrier
flags or that the spec touches; do not skim sections the flags or that the spec (pre-spec: the sources) touches; do
spec does not touch. not skim sections it does not touch.
- `git log -5 --format=full` — full bodies of the most - `git log -5 --format=full` — full bodies of the most
recent iter commits; tells you what just shipped, so the recent iter commits; tells you what just shipped, so the
file-map does not double-count fresh work. file-map does not double-count fresh work.
@@ -53,19 +60,78 @@ In addition:
## Carrier contract — what the controller hands you ## Carrier contract — what the controller hands you
This table is the authoritative definition of the carrier fields These tables are the authoritative definition of the carrier fields
(including which are mandatory and the BLOCKED-on-missing rule (including which are mandatory and the BLOCKED-on-missing rules
below); the dispatching skill references it rather than restating below); the dispatching skills reference them rather than restating
it. them. A dispatch carries **exactly one** of `spec_path` (the
spec-anchored form) or `sources` (the pre-spec form below).
| Field | Content | | Field | Content |
|-------|---------| |-------|---------|
| `spec_path` | Path to the spec under `docs/specs` (mandatory) | | `spec_path` | Path to the spec under `docs/specs` (mandatory in the spec-anchored form) |
| `iteration_scope` | Which sections of the spec this dispatch covers (mandatory) | | `iteration_scope` | Which sections of the spec this dispatch covers (mandatory with `spec_path`) |
| `focus_hint` | Optional: orchestrator may flag a specific subsystem or symbol to prioritise | | `focus_hint` | Optional: orchestrator may flag a specific subsystem or symbol to prioritise. On a delta dispatch it is mandatory and names the gap (see **Delta dispatch** below). |
If `spec_path` does not resolve or `iteration_scope` is empty, In the spec-anchored form: if `spec_path` does not resolve or
return `BLOCKED` with a one-line reason. `iteration_scope` is empty, return `BLOCKED` with a one-line
reason.
### Pre-spec dispatch (cycle recon)
When dispatched **before the spec exists** — by `specify` Step 1
or ad-hoc by `brainstorm`, as the cycle recon
(`../../docs/pipeline.md` § Cycle recon) — the carrier is anchored
to the design sources instead:
| Field | Content |
|-------|---------|
| `sources` | The design sources: workspace paths (design docs, ledger contracts) plus prose the dispatching skill pastes verbatim (issue bodies, the ratified design narrative). You read the named paths and the pasted prose; you do NOT open the tracker or fish for further context. |
| `recon_scope` | Which territory of the sources this dispatch covers (mandatory) — the pre-spec analogue of `iteration_scope`. When the sources settle the anticipated change shape (a signature change, a variant add / remove, a symbol removal, a content-pin perturbation), the dispatching skill names it here — that arms Step 5's compile-driven enumeration in this dispatch, so `planner` needs no mandatory delta for it later. |
| `focus_hint` | Optional, as above |
If `sources` is empty (no path resolves and no prose was pasted)
or `recon_scope` is empty, return `BLOCKED` with a one-line
reason.
In this form, every rule below that reads "the spec" applies to
the sources — specifically:
- Process step 2 ("Read the spec in full") → read the pasted
prose in full and every named source path in full.
- Process steps 4, 5, and 7 ("the spec references / implies") →
what the sources reference or imply.
- The scope triggers in step 5 and the omission rule after the
output format key on `recon_scope` instead of
`iteration_scope`.
- Step 6's "Spec-named-path existence table" becomes the
**source-named**-path existence table — same verification
commands, same output section (keep the section heading; note
`(pre-spec: source-named)` under it).
- Step 8's priority filter: source territory in `recon_scope`
gets full coverage; territory outside it, a one-line
acknowledgement.
- The `NEEDS_CONTEXT` status row reads: `recon_scope` names
territory the sources do not contain.
- The Rationalisations rows "Spec is ambiguous on point X" and
"The spec says path X does not exist" and the Red-Flag entries
that name the spec apply to the sources unchanged.
### Delta dispatch
Either form may arrive as a **delta** — the dispatching skill
marks it so in the carrier, and `focus_hint` (mandatory on a
delta) names the gap: a path or symbol the cycle recon missed, or
a compile-driven site set it lacks
(`../../docs/pipeline.md` § Cycle recon). A delta inverts the
priority filter:
- Read the spec / sources only as far as needed to anchor the
gap; do NOT re-map territory the cycle recon already covered.
- Produce only the output sections the gap touches: the file-map
rows, the compile-driven set, and existence-table rows for the
gap's paths. The existence table's always-required rule applies
to the gap's paths only.
- Output budget ≤500 tokens.
## The Iron Law ## The Iron Law
@@ -81,11 +147,13 @@ never to fix or write.
## The Process ## The Process
1. Read the carrier. Confirm `spec_path` resolves and 1. Read the carrier. Confirm the mandatory fields of its form
`iteration_scope` is non-empty. If either fails, return (spec-anchored: `spec_path` resolves and `iteration_scope` is
non-empty; pre-spec: `sources` and `recon_scope` per the
Pre-spec dispatch section). If a check fails, return
`BLOCKED`. `BLOCKED`.
2. Read the spec in full. Note every reference to a path, 2. Read the spec in full (pre-spec: the sources in full). Note
type, function, or invariant. every reference to a path, type, function, or invariant.
3. Read the standing list in order, plus the design ledger's 3. Read the standing list in order, plus the design ledger's
relevant contracts (if the project has one). relevant contracts (if the project has one).
4. For each path or symbol the spec references, run 4. For each path or symbol the spec references, run
+58 -4
View File
@@ -113,6 +113,26 @@ Before producing anything, establish what the sources actually say:
walk to the contracts the work touches. Note any `status: walk to the contracts the work touches. Note any `status:
aspirational` source: its code is a target, not verified fact. aspirational` source: its code is a target, not verified fact.
**Grounding fan-out — the cycle recon.** When the work enters code
territory the orchestrator has not recently read, dispatch
`../planner/agents/plan-recon.md` **once**, in its pre-spec form —
carrier fields (`sources`, `recon_scope`, optional `focus_hint`)
defined authoritatively under its **Pre-spec dispatch (cycle
recon)** heading. When the sources already settle the change shape
(a signature change, a variant add / remove, a symbol removal, a
content-pin perturbation), name it in `recon_scope`: that arms the
compile-driven enumeration inside the cycle recon and saves
`planner` a mandatory delta later. The returned file-map grounds
Step 2's criterion evidence and Step 3's `## Concrete code
shapes`, and is reused downstream by `planner` — budget, reuse
conditions, and delta triggers are defined once in
`../docs/pipeline.md` § Cycle recon. A `brainstorm`-fired dispatch
that meets that section's coverage condition IS the cycle recon —
do not dispatch again. Do NOT ground a spec with a broad
Explore-type sweep (§ Cycle recon's ban; field evidence on issue
#35: a 151k-token Explore sweep here, ~40 % duplicated by the
later plan-recon).
### Step 1.5 — Precondition gate (bounce-back) ### Step 1.5 — Precondition gate (bounce-back)
Enumerate the **load-bearing design decisions** the spec must encode Enumerate the **load-bearing design decisions** the spec must encode
@@ -312,6 +332,24 @@ follows as an explicitly secondary subsection, never first. Prose may
*explain* code, never *replace* it. Exact bytes / line-numbers remain *explain* code, never *replace* it. Exact bytes / line-numbers remain
the planner's job — the spec owns the *shape*, shown. the planner's job — the spec owns the *shape*, shown.
**Assumption economy — fewer restatements, never fewer reliances.**
Every assertion the spec makes about *current* codebase behaviour is
a load-bearing-assumption candidate the Step-5 `grounding-check`
must extract and ratify — the gate's cost scales with the count, and
it BLOCKs an over-broad spec outright (`agents/grounding-check.md`).
So: state a current-behaviour claim only where the change actually
relies on it (the gate's test: its falsity would make the change
fail or cost additional work), and render context-only mentions of
existing mechanisms as a design-ledger contract citation instead of
restating their behaviour. This trims *restatements*, never *reliances*: a
mechanism the change builds on remains an assumption — explicit, or
the implicit form the gate extracts anyway — and writing it as a
bare citation does not exempt it; hiding a reliance is exactly the
failure the gate exists to catch. The same economy applies to scope:
a tightly-cut iteration makes fewer claims than a sprawling one
(field evidence on issue #35: gate cost tracks the spec's assumption
count near-linearly).
### Step 4 — Self-review ### Step 4 — Self-review
Inline checklist (not a subagent dispatch): Inline checklist (not a subagent dispatch):
@@ -328,6 +366,16 @@ Inline checklist (not a subagent dispatch):
carry before → after code for every load-bearing change (plus the carry before → after code for every load-bearing change (plus the
worked user-facing example for a surface cycle)? A load-bearing worked user-facing example for a surface cycle)? A load-bearing
change described only in prose is a self-review failure to fix. change described only in prose is a self-review failure to fix.
6. **Assumption economy:** scan every claim about current codebase
behaviour. The gate's own criterion decides which stay: a claim
is a *reliance* iff its being false would make the proposed
change fail or require additional work
(`agents/grounding-check.md` § What "load-bearing assumption"
means). Non-reliances are gate cost with no return — delete
them, or demote them to a citation that carries no behaviour
claim. Never demote a reliance: a bare citation does not ratify
it, and the gate BLOCKs on it unratified (Step 3's
restatements-vs-reliances line).
Fix issues inline. Fix issues inline.
@@ -497,6 +545,7 @@ is narrow by design: only the user's-call forks bounce, not every fork.
|-----------|---------| |-----------|---------|
| user / issue (design settled) → `specify` | the sources: issue body, in-context design discussion, design docs | | user / issue (design settled) → `specify` | the sources: issue body, in-context design discussion, design docs |
| `brainstorm``specify` | ratified design narrative (approaches chosen, constraints, sections approved) — in-context; brainstorm writes no spec file | | `brainstorm``specify` | ratified design narrative (approaches chosen, constraints, sections approved) — in-context; brainstorm writes no spec file |
| `specify``plan-recon` (Step 1, the cycle recon — skipped when a brainstorm-fired dispatch meets the coverage condition) | `sources` + `recon_scope` + optional `focus_hint`; defined authoritatively under **Pre-spec dispatch (cycle recon)** in `../planner/agents/plan-recon.md` |
| `specify``grounding-check` (Step 5) | `spec_path` (absolute) + `iteration_scope` | | `specify``grounding-check` (Step 5) | `spec_path` (absolute) + `iteration_scope` |
| `specify``spec-skeptic` bias-breaker (Step 1.5, optional, orchestrator's call) | `spec_path` (absolute) + `iteration_scope` + one `lens` + `seeding_issue` (or `none`); dispatched ad hoc for a fork the orchestrator is deciding, never as a gate | | `specify``spec-skeptic` bias-breaker (Step 1.5, optional, orchestrator's call) | `spec_path` (absolute) + `iteration_scope` + one `lens` + `seeding_issue` (or `none`); dispatched ad hoc for a fork the orchestrator is deciding, never as a gate |
| `specify` → reference issue (Step 1.5) | a derived-decision comment (rationale) or user-decision comment (provenance); a freshly created seeding issue if the cycle had none | | `specify` → reference issue (Step 1.5) | a derived-decision comment (rationale) or user-decision comment (provenance); a freshly created seeding issue if the cycle had none |
@@ -515,6 +564,7 @@ is narrow by design: only the user's-call forks bounce, not every fork.
| "The shape is clear from the prose, I don't need to paste the code" | If it's clear, pasting it is free; if pasting it is hard, it wasn't clear. The acceptance criterion is unjudgeable without the worked code. | | "The shape is clear from the prose, I don't need to paste the code" | If it's clear, pasting it is free; if pasting it is hard, it wasn't clear. The acceptance criterion is unjudgeable without the worked code. |
| "Just polishing a wording after PASS, no need to re-dispatch" | The grounding-check report attests to specific bytes. A polish edit changes the bytes; the attestation no longer covers them. Re-dispatch is cheap. | | "Just polishing a wording after PASS, no need to re-dispatch" | The grounding-check report attests to specific bytes. A polish edit changes the bytes; the attestation no longer covers them. Re-dispatch is cheap. |
| "It's in a model doc / the design ledger, so it's canonical" | A `models` / RFC / proposal doc holds aspirational code — a target, not verified fact. Flag code lifted from a `status: aspirational` source as such; it is not ratified contract. | | "It's in a model doc / the design ledger, so it's canonical" | A `models` / RFC / proposal doc holds aspirational code — a target, not verified fact. Flag code lifted from a `status: aspirational` source as such; it is not ratified contract. |
| "Fewer assumptions = a cheaper grounding-check, so I'll phrase the reliances as vague citations" | Economy cuts *restatements*, not *reliances*. A mechanism the change builds on stays a claim — vague phrasing merely converts it to the implicit form the gate extracts anyway, or worse, hides a false premise until a downstream iteration BLOCKs on it. Trim what the change does not rest on; never what it does. |
| "In `/boss` this spec looks solid — I'll sign and move on" | Your sense that it looks solid is not the signature. Under `/boss` the signature is the Step-5 `grounding-check` `PASS` — an independent agent's verdict, not your confidence. Run the grounding-check; a no-override `BLOCK` routes to the human, never auto-signed over. | | "In `/boss` this spec looks solid — I'll sign and move on" | Your sense that it looks solid is not the signature. Under `/boss` the signature is the Step-5 `grounding-check` `PASS` — an independent agent's verdict, not your confidence. Run the grounding-check; a no-override `BLOCK` routes to the human, never auto-signed over. |
| "A grounding `BLOCK` looks like a false alarm — I'll sign over it" | With no human in the loop there is no override. A no-override grounding `BLOCK` is the one objective denial of an autonomous sign; it routes to the human sign-off pause. The whole point of trusting grounding-PASS as the signature is that you also honour its `BLOCK`. | | "A grounding `BLOCK` looks like a false alarm — I'll sign over it" | With no human in the loop there is no override. A no-override grounding `BLOCK` is the one objective denial of an autonomous sign; it routes to the human sign-off pause. The whole point of trusting grounding-PASS as the signature is that you also honour its `BLOCK`. |
@@ -543,6 +593,9 @@ is narrow by design: only the user's-call forks bounce, not every fork.
- Jumping straight from spec to `implement` (must go via `planner`) - Jumping straight from spec to `implement` (must go via `planner`)
- Running an interview here (that is `brainstorm`'s job — if the work - Running an interview here (that is `brainstorm`'s job — if the work
needs one, bounce) needs one, bounce)
- Grounding the spec with a broad Explore-type sweep — or a second
full recon dispatch when a cycle recon already exists
(`../docs/pipeline.md` § Cycle recon)
## Cross-references ## Cross-references
@@ -566,9 +619,10 @@ is narrow by design: only the user's-call forks bounce, not every fork.
unanimous five-lens auto-sign panel is retired (baseline tag unanimous five-lens auto-sign panel is retired (baseline tag
`pre-autosign-rework`); the autonomous signature is now the Step-5 `pre-autosign-rework`); the autonomous signature is now the Step-5
`grounding-check` `PASS`. `grounding-check` `PASS`.
- **Ad-hoc dispatch.** The orchestrator MAY also ad-hoc dispatch - **Cycle recon (Step 1).** `../planner/agents/plan-recon.md` in its
`../planner/agents/plan-recon.md` during Step 1 when the work enters pre-spec form is the cycle's ONE full recon dispatch when the work
code territory not recently read; discretionary, not part of the enters code territory not recently read; its report also serves
standard process. `planner` downstream. Budget, reuse conditions, and delta triggers:
`../docs/pipeline.md` § Cycle recon.
- **Project feature-acceptance criterion:** declared in the project's - **Project feature-acceptance criterion:** declared in the project's
`CLAUDE.md`. Applied prospectively in Step 2. `CLAUDE.md`. Applied prospectively in Step 2.