diff --git a/docs/agent-template.md b/docs/agent-template.md index 7018e2a..9b2d78e 100644 --- a/docs/agent-template.md +++ b/docs/agent-template.md @@ -137,9 +137,18 @@ Assignment rule, in priority order: evidence, the in-loop quality review) — run `opus`. Opus is the documented strength for code review, debugging, and deep reasoning. -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). +3. **Mechanical scope** — recon, extraction, compliance-diffing + run `sonnet` (near-opus quality on tightly-scoped work at + lower latency and cost). Plan execution itself — the + `implementer` — is owner-ratified (2026-07-20) onto `opus`: + authored code is the pipeline's primary output, so it takes + the judgment model, but pairs it with `high` effort, not + `xhigh` — it is the most-dispatched in-loop role, and + volume × wall-clock (rule 4) still governs its thinking + budget. The compiler-driven-edit arm keeps dispatching the + implementer agentType at `sonnet`/`high`: compiler-enumerated + propagation is mechanical, and that arm's policy header pins + its own tier. 4. **Volume × wall-clock** — agents dispatched per-task inside loops or fanned out in parallel swarms multiply their model's latency; they run `sonnet` unless rule 2 overrides (currently @@ -156,9 +165,9 @@ Assignment rule, in priority order: Current distribution: `fable` — architect, debugger, grounding-check, tdd-author. `opus` — bencher, fieldtester, -plan-recon, quality-reviewer, spec-skeptic. `sonnet` — docwriter, -glossary-extractor, implementer, spec-reviewer, synthetic-user, -tester. +implementer (at `high`), plan-recon, quality-reviewer, +spec-skeptic. `sonnet` — docwriter, glossary-extractor, +spec-reviewer, synthetic-user, tester. The same rule binds Workflow scripts: every `agent()` call passes an explicit `model:` option, and `fable` is never a valid value there — @@ -174,14 +183,16 @@ effort, coupling every dispatch's thinking budget to whatever the user happens to be chatting at — the same session-state coupling the `model` pin exists to remove. Effort follows the model split: -- **`xhigh` on every `fable` and `opus` agent.** The judgement - roles are the pipeline's quality floor; their thinking budget - must not silently degrade because the session runs lower. The - fable gates deliberately stay at `xhigh`, not `max` — the +- **`xhigh` on every `fable` and `opus` judgment agent.** The + judgement roles are the pipeline's quality floor; their thinking + budget must not silently degrade because the session runs lower. + The fable gates deliberately stay at `xhigh`, not `max` — the marginal thinking is not worth the extra latency and - scarce-budget burn. -- **`high` on every `sonnet` agent.** Tightly-scoped execution of a - pre-made plan gains little from `xhigh` but pays its latency on + scarce-budget burn. One `opus` agent is exempt: the + `implementer` pairs `opus` with `high` (§ model rule 3 — the + most-dispatched in-loop role keeps the in-loop effort tier). +- **`high` on every `sonnet` agent.** Tightly-scoped mechanical + work gains little from `xhigh` but pays its latency on every dispatch — and these are exactly the roles dispatched per-task inside loops (wall-clock is the pipeline's efficiency metric). Not lower than `high`: a sloppy in-loop step triggers diff --git a/implement/agents/implementer.md b/implement/agents/implementer.md index e3ec492..31b0d68 100644 --- a/implement/agents/implementer.md +++ b/implement/agents/implementer.md @@ -2,7 +2,7 @@ name: implementer description: Carries out a tightly scoped implementation task. Reads the task extract handed by the controller, implements, builds, tests, reports a structured status with the diff. NOT for architecture decisions, NOT for self-curated scope; this agent executes a plan that has already been made. tools: Read, Edit, Write, Bash, Glob, Grep -model: sonnet +model: opus effort: high --- diff --git a/implement/workflows/compiler-driven-edit.js b/implement/workflows/compiler-driven-edit.js index 22234ca..148205d 100644 --- a/implement/workflows/compiler-driven-edit.js +++ b/implement/workflows/compiler-driven-edit.js @@ -43,7 +43,10 @@ // session-model inherit (which could route to an unintended tier, e.g. fable — // reserved for the four ratified frontmatter gates, never for workflow stages; // agent-template § model rule 1). Both phases here are mechanical -// (compiler-enumerated propagation, build/suite re-run), so both run sonnet. +// (compiler-enumerated propagation, build/suite re-run), so both run sonnet — +// including the implementer agentType: its opus frontmatter tier is for plan +// execution, not compiler-enumerated propagation (agent-template § model +// rule 3), so this arm pins sonnet explicitly. // Effort policy mirrors it: every call pins an explicit effort — the edit // (real code work) runs high, the build/suite verify (schema-bound check) // runs medium. See docs/agent-template.md § effort. diff --git a/implement/workflows/implement-loop.js b/implement/workflows/implement-loop.js index 0360dda..c0fce44 100644 --- a/implement/workflows/implement-loop.js +++ b/implement/workflows/implement-loop.js @@ -82,7 +82,9 @@ // session-model inherit (which could route to an unintended tier, e.g. // fable — reserved for the four ratified frontmatter gates, never for // workflow stages; agent-template § model rule 1). Mechanical and -// in-loop steps run sonnet; the quality gate runs opus by default (last +// in-loop check steps run sonnet; the implementer runs opus/high +// (owner-ratified 2026-07-20: judgment model for code authorship, kept at +// the in-loop effort tier); the quality gate runs opus by default (last // correctness review before the end-verify), TIERED to sonnet/high for a // small (<= 25 changed lines), non-contract diff as measured // independently by the spec-reviewer (issue #30) — the end-verify / @@ -792,7 +794,7 @@ async function runTask(task) { 'inner-loop discipline — add the failing test inline even if the task text forgot to script it). ' + 'Build and test must be green before you report DONE. Leave all edits UNSTAGED; never commit.\n\n' + `mode: ${mode}\niter_id: ${iter_id}\n\nTASK ${task.id} — ${task.title}\n${task.text}`, - { agentType: 'implementer', model: 'sonnet', effort: 'high', label: `impl:${task.id}`, phase: 'Per-task loop', schema: IMPL_SCHEMA }, + { agentType: 'implementer', model: 'opus', effort: 'high', label: `impl:${task.id}`, phase: 'Per-task loop', schema: IMPL_SCHEMA }, ) if (!impl) return { id: task.id, title: task.title, outcome: 'BLOCKED', reason: 'implementer agent did not return' } if (impl.status === 'BLOCKED') return { id: task.id, title: task.title, outcome: 'BLOCKED', reason: impl.reason || 'worker-blocked' } @@ -833,7 +835,7 @@ async function runTask(task) { `${STANDING}${ANCHOR}\n\nRepair task ${task.id} to satisfy the spec-compliance review. Address EACH finding; ` + 'do not widen scope. Keep build + tests green; leave edits unstaged.\n\n' + `TASK ${task.id} — ${task.title}\n${task.text}\n\nFINDINGS:\n- ${(spec?.findings || ['(none reported)']).join('\n- ')}`, - { agentType: 'implementer', model: 'sonnet', effort: 'high', label: `impl-fix-spec:${task.id}`, phase: 'Per-task loop', schema: IMPL_SCHEMA }, + { agentType: 'implementer', model: 'opus', effort: 'high', label: `impl-fix-spec:${task.id}`, phase: 'Per-task loop', schema: IMPL_SCHEMA }, ) if (specFix && specFix.applied_changes === true) wroteAnyFile = true } @@ -951,7 +953,7 @@ async function runTask(task) { 'into a deviation the next review flags.\n\n' + `TASK ${task.id} — ${task.title}\n${task.text}\n\nISSUES:\n- ` + (qual?.issues?.length ? qual.issues.map((i) => `[${i.severity}] ${i.text}`) : ['(none reported)']).join('\n- '), - { agentType: 'implementer', model: 'sonnet', effort: 'high', label: `impl-fix-qual:${task.id}`, phase: 'Per-task loop', schema: IMPL_SCHEMA }, + { agentType: 'implementer', model: 'opus', effort: 'high', label: `impl-fix-qual:${task.id}`, phase: 'Per-task loop', schema: IMPL_SCHEMA }, ) if (fix && fix.applied_changes === true) wroteAnyFile = true if (fix && (fix.status === 'BLOCKED' || fix.status === 'NEEDS_CONTEXT')) {