feat(agents): pin explicit model on every agent and workflow call

Agents and workflows previously carried no model field, so every
dispatch inherited the session model — including fable, which is
banned for all plugin agents and workflows by owner decree. Every
dispatch now pins opus or sonnet explicitly.

- opus (low-volume judgment gates whose misses silently poison
  downstream work): architect, bencher, debugger, fieldtester,
  grounding-check, plan-recon, quality-reviewer, spec-skeptic,
  tdd-author
- sonnet (mechanical scope, in-loop or fanned out): docwriter,
  glossary-extractor, implementer, spec-reviewer, synthetic-user,
  tester
- workflows: all 13 agent() call sites pin a model — sonnet
  everywhere except the quality-reviewer gate in implement-loop,
  the loop's last correctness check (spec-reviewer only gates
  task-text correspondence; real-bug finding is the documented
  opus strength)
- docs/agent-template.md: model is now a mandatory frontmatter
  field, with the assignment rule and the fable ban recorded
This commit is contained in:
2026-07-02 12:14:36 +02:00
parent ea54be6535
commit 6bfec9655c
18 changed files with 72 additions and 13 deletions
+7 -2
View File
@@ -39,6 +39,11 @@
// stay unstaged for the orchestrator. The script has no shell of its own —
// the edit and the verify both run inside agent() calls.
//
// Model policy: every agent() call pins an explicit model — never the
// session-model inherit (which could route to an unintended tier, e.g. fable —
// banned for all plugin agents and workflows). Both phases here are mechanical
// (compiler-enumerated propagation, build/suite re-run), so both run sonnet.
//
// Carrier — Workflow `args`:
// edit_description: what type/signature change to make
// def_site: the definition site (file + symbol) it originates from
@@ -120,7 +125,7 @@ const edit = await agent(
'Then run the project build and report: is the build clean, does any hole need a decision (with detail + bounce_to), ' +
'how many sites were touched, and — checked against `git status --porcelain` / `git diff HEAD`, NOT from memory — ' +
'whether the working tree actually changed (applied_changes).',
{ agentType: 'implementer', label: 'edit+propagate', phase: 'Edit + propagate', schema: EDIT_SCHEMA },
{ agentType: 'implementer', model: 'sonnet', label: 'edit+propagate', phase: 'Edit + propagate', schema: EDIT_SCHEMA },
)
if (!edit) return { status: 'BLOCKED', reason: 'edit agent did not return' }
if (edit.hole_needs_decision) {
@@ -162,7 +167,7 @@ const verify = await agent(
'every test passes and NO test was added, removed, or weakened versus the pre-edit baseline (compare `git diff ' +
'HEAD` for test-file changes). Also report working_tree_dirty — whether `git status --porcelain` / `git diff HEAD` ' +
'shows ANY change at all (a clean tree means no edit landed). Do NOT edit anything; do NOT commit.',
{ label: 'verify(build+suite)', phase: 'Verify', schema: VERIFY_SCHEMA },
{ model: 'sonnet', label: 'verify(build+suite)', phase: 'Verify', schema: VERIFY_SCHEMA },
)
// ---- Phase 3 — verdict --------------------------------------------------