From fe5ca8f8c0d89a21f8b052e9277c172dfd4625c8 Mon Sep 17 00:00:00 2001 From: claude Date: Sat, 25 Jul 2026 13:52:04 +0200 Subject: [PATCH] feat(pipeline): consolidate cycle recon, add fieldtest probe tier, enforce assumption economy MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- boss/SKILL.md | 7 ++ brainstorm/SKILL.md | 11 ++-- docs/agent-template.md | 11 +++- docs/pipeline.md | 87 +++++++++++++++++++++++-- fieldtest/SKILL.md | 61 +++++++++++++---- fieldtest/agents/fieldtester.md | 23 +++++-- planner/SKILL.md | 46 +++++++++---- planner/agents/plan-recon.md | 112 +++++++++++++++++++++++++------- specify/SKILL.md | 62 ++++++++++++++++-- 9 files changed, 353 insertions(+), 67 deletions(-) diff --git a/boss/SKILL.md b/boss/SKILL.md index a213bf4..0aaf30e 100644 --- a/boss/SKILL.md +++ b/boss/SKILL.md @@ -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 `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 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 diff --git a/brainstorm/SKILL.md b/brainstorm/SKILL.md index 67d55f9..dd02ad1 100644 --- a/brainstorm/SKILL.md +++ b/brainstorm/SKILL.md @@ -152,8 +152,9 @@ unconditional default. ### Step 3 — Populate the space with the synthetic-user swarm 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 -for `plan-recon` and `specify` uses for `spec-skeptic`. No Workflow wrapper: a +stance, in a single turn** — the same ad-hoc fan-out `specify` uses +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 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) and the Entry-path reflection — how the autonomous swarm-brainstorm decides 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 - 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 `CLAUDE.md` — applied prospectively by `specify` (its Step 2), not here. diff --git a/docs/agent-template.md b/docs/agent-template.md index 7018e2a..7de8273 100644 --- a/docs/agent-template.md +++ b/docs/agent-template.md @@ -136,7 +136,16 @@ Assignment rule, in priority order: gating a cycle (adversarial lenses, plan recon, field/bench evidence, the in-loop quality review) — run `opus`. Opus is 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 plan, recon, extraction, compliance-diffing run `sonnet` (near-opus coding quality at lower latency and cost). diff --git a/docs/pipeline.md b/docs/pipeline.md index 6738ccf..0e37e82 100644 --- a/docs/pipeline.md +++ b/docs/pipeline.md @@ -92,7 +92,10 @@ hold: `bug` findings; `friction` / `spec_gap` findings resolved or ratified into the design ledger. A `clean` roll-up is honoured 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, is itself a `spec_gap`, not a clean close (the agent owes the 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 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 -- ` 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 ### 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 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 -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. 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, git-ignored working file, shell-`rm`'d alongside the spec at cycle close — see `conventions.md` § Lifecycle) that the implement skill can -execute task-by-task. Dispatches -the plan-recon agent for read-only file-structure mapping. +execute task-by-task. Its file-map normally comes from reusing the +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 @@ -282,7 +357,9 @@ practices inside `implement`. Optional. Orchestrator-dispatched after the audit closes clean 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 own implementation), runs the results, and writes a friction- and-bug spec. diff --git a/fieldtest/SKILL.md b/fieldtest/SKILL.md index 85d18c7..7b34d5a 100644 --- a/fieldtest/SKILL.md +++ b/fieldtest/SKILL.md @@ -1,6 +1,6 @@ --- 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 @@ -24,8 +24,8 @@ next to cycle-design specs under `docs/specs`, with a `fieldtest-` prefix in the slug. The substantive process — read the design ledger + cycle spec -+ recent iter/audit commit bodies, pick 2-4 real-world tasks -per cycle axis, implement each as a downstream consumer, run ++ recent iter/audit commit bodies, pick the dispatched tier's +examples, implement each as a downstream consumer, run the result, classify findings, write the spec — lives in `agents/fieldtester.md`. That file also carries the spec 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 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 Triggers: @@ -116,11 +154,12 @@ carrier. ## Dispatch Dispatch `fieldtester` with the carrier from the Handoff -Contract below. The agent picks 2-4 examples (one per axis -the cycle touched; fewer loses the signal on variation, more -overflows one readable report), implements them as downstream -consumers, runs them, classifies findings, and writes the -spec. The two artefact kinds part ways at commit: the **fixtures are +Contract below. The agent picks its examples per the dispatched +tier (bounds in the `tier` carrier row; on full, one per axis +the cycle touched — fewer loses the signal on variation, more +overflows one readable report), implements them as +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 subject `fieldtest: examples, findings`), while the **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 `fieldtest` consumes (from orchestrator at cycle close) the carrier -fields `cycle_id`, `cycle_scope`, `axis_hints`, and `commit_range`, -defined authoritatively under **Carrier contract** in +fields `cycle_id`, `cycle_scope`, `axis_hints`, `commit_range`, and +`tier`, defined authoritatively under **Carrier contract** in `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`, `examples_added`, and `findings`, defined authoritatively under diff --git a/fieldtest/agents/fieldtester.md b/fieldtest/agents/fieldtester.md index 5a05b2f..48d525a 100644 --- a/fieldtest/agents/fieldtester.md +++ b/fieldtest/agents/fieldtester.md @@ -1,6 +1,6 @@ --- 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 model: opus effort: xhigh @@ -71,10 +71,13 @@ skill references it rather than restating it. | `cycle_scope` | 1-3 sentences naming what shipped | | `axis_hints` | bullet list, one per axis to probe | | `commit_range` | `..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 `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 @@ -97,7 +100,9 @@ scenarios as the union of per-cycle axes; the per-cycle fieldtests already covered axis-local surface. There is no `axis_hints` fallback in this mode: if `milestone_promise` is 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 @@ -143,7 +148,10 @@ Each phase completes before the next starts. transformation pipeline. Tasks that do NOT work: a one-liner that exercises a single primitive (too thin) or 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 @@ -280,7 +288,7 @@ End every report with exactly one of: ## Output format -At most 350 words, structured: +At most 350 words (probe tier: 200), structured: - **Status:** one of the four above. - **Examples:** one bullet per example: path + 1-line task @@ -321,7 +329,8 @@ fields are: - A `friction` finding without a 1-line recommendation. Every finding is actionable or it isn't a finding. - 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 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. | | "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. | -| "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 diff --git a/planner/SKILL.md b/planner/SKILL.md index c381d62..0555ac9 100644 --- a/planner/SKILL.md +++ b/planner/SKILL.md @@ -80,20 +80,37 @@ 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 +The file-map normally already exists: the **cycle recon** report +(`../docs/pipeline.md` § Cycle recon), dispatched during `specify` +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. -Dispatch carrier: see the **Carrier contract** table in -`agents/plan-recon.md` (single source for `spec_path`, -`iteration_scope`, `focus_hint`). +Dispatch `plan-recon` only: -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. +- as a **delta**, on the § Cycle recon delta triggers — a narrow + dispatch whose `focus_hint` names the gap, bounded by the + **Delta dispatch** contract in `agents/plan-recon.md`; +- in **full**, when no valid cycle recon exists (a direct tidy + 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 keeps the same shape: @@ -282,5 +299,8 @@ shell-`rm`'d alongside the spec at cycle close by `audit` (see 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. + (read-only). Normally reused from the cycle recon + (`../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. diff --git a/planner/agents/plan-recon.md b/planner/agents/plan-recon.md index 1ee60fb..ec71b83 100644 --- a/planner/agents/plan-recon.md +++ b/planner/agents/plan-recon.md @@ -1,6 +1,6 @@ --- 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 model: opus effort: xhigh @@ -11,11 +11,15 @@ effort: xhigh > **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.** +normally dispatched **once per cycle** — the cycle recon +(`../../docs/pipeline.md` § Cycle recon): by the `specify` +skill at Step 1 in the pre-spec form below, or ad-hoc by the +`brainstorm` skill, whenever the cycle enters code territory +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 @@ -26,7 +30,10 @@ 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. +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 orchestrator owns decomposition; your authority ends at @@ -42,8 +49,8 @@ In addition: - If the project has a design ledger (its CLAUDE.md project facts), walk it to the contracts the carrier - flags or that the spec touches; do not skim sections the - spec does not touch. + flags or that the spec (pre-spec: the sources) touches; do + not skim sections it 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. @@ -53,19 +60,78 @@ In addition: ## Carrier contract — what the controller hands you -This table is the authoritative definition of the carrier fields -(including which are mandatory and the BLOCKED-on-missing rule -below); the dispatching skill references it rather than restating -it. +These tables are the authoritative definition of the carrier fields +(including which are mandatory and the BLOCKED-on-missing rules +below); the dispatching skills reference them rather than restating +them. A dispatch carries **exactly one** of `spec_path` (the +spec-anchored form) or `sources` (the pre-spec form below). | Field | Content | |-------|---------| -| `spec_path` | Path to the spec under `docs/specs` (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 | +| `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 with `spec_path`) | +| `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, -return `BLOCKED` with a one-line reason. +In the spec-anchored form: if `spec_path` does not resolve or +`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 @@ -81,11 +147,13 @@ 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 +1. Read the carrier. Confirm the mandatory fields of its form + (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`. -2. Read the spec in full. Note every reference to a path, - type, function, or invariant. +2. Read the spec in full (pre-spec: the sources 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 the project has one). 4. For each path or symbol the spec references, run diff --git a/specify/SKILL.md b/specify/SKILL.md index f270bae..76b016d 100644 --- a/specify/SKILL.md +++ b/specify/SKILL.md @@ -113,6 +113,26 @@ Before producing anything, establish what the sources actually say: walk to the contracts the work touches. Note any `status: 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) 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 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 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 worked user-facing example for a surface cycle)? A load-bearing 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. @@ -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 | | `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` → `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 | @@ -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. | | "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. | +| "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. | | "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`) - Running an interview here (that is `brainstorm`'s job — if the work 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 @@ -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 `pre-autosign-rework`); the autonomous signature is now the Step-5 `grounding-check` `PASS`. -- **Ad-hoc dispatch.** The orchestrator MAY also ad-hoc dispatch - `../planner/agents/plan-recon.md` during Step 1 when the work enters - code territory not recently read; discretionary, not part of the - standard process. +- **Cycle recon (Step 1).** `../planner/agents/plan-recon.md` in its + pre-spec form is the cycle's ONE full recon dispatch when the work + enters code territory not recently read; its report also serves + `planner` downstream. Budget, reuse conditions, and delta triggers: + `../docs/pipeline.md` § Cycle recon. - **Project feature-acceptance criterion:** declared in the project's `CLAUDE.md`. Applied prospectively in Step 2.