canon-ai 3.14.0 → 3.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -4,6 +4,25 @@
4
4
 
5
5
  ## [Unreleased]
6
6
 
7
+ ## [3.16.0] — 2026-10-28
8
+
9
+ ### Changed
10
+
11
+ - **Codex spec review for small tasks runs at `high` effort instead of `medium`.** Small (`S`) tasks now get the same spec-review effort as medium and large ones. In a replay of past specs, `high` caught more spec gaps that later forced a reroute and did not block more clean specs; a review takes about 20 seconds longer. Effort has no environment override, so restoring `medium` means installing the previous release.
12
+ - **Spec authoring and `/canon-spec-review` look for sibling implementations, not just callers.** The spec prompts and `/canon-spec` tell the author to search for every other place that implements the behavior being changed (a second listener or entry path, a parallel component, or a test or doc asserting the current behavior) before settling Affected Files. The `/canon-spec-review` factual lens now searches for them too and flags an unnamed one the change must reach as blocking.
13
+ - **`/canon-spec` now strongly recommends `/canon-spec-review` after every spec with logic to vet, before you approve it.** It was optional and pitched mainly at larger tasks. In full-send mode, `/canon-spec` now runs `/canon-spec-review` itself before launching: it applies every blocking finding, applies other findings at its own judgment, and stops to ask only when a fix would change the approved scope. The pipeline guide and README say the same.
14
+
15
+ ## [3.15.0] — 2026-10-27
16
+
17
+ ### Changed
18
+
19
+ - **Codex implementation runs at higher reasoning effort, and the full Codex model defaults to GPT-6.1 Sol.** Extra-small and small tasks now implement with the mini model at `xhigh` effort (was `medium`), and medium and large tasks at `max` (was `high`). Extra-large and delicate tasks stay on the full model at `high`, so `delicate` now means a stronger model rather than more effort. The full-model default moves from `gpt-6-sol` to `gpt-6.1-sol`; set `CODEX_MODEL_FULL=gpt-6-sol` to keep the old one. Effort has no environment override, so restoring the old effort settings means installing the previous release. `max` needs a Codex CLI and mini model that accept it; the pipeline guide names the verified pair and what to do if Codex rejects it.
20
+ - **QA defaults to Claude Haiku 5.5 instead of Sonnet.** The QA step runs `claude-haiku-5-5` at medium effort at every task size, including delicate tasks; spec, plan, and code review keep their models. QA no longer follows the light-tier model, so `CLAUDE_MODEL_LIGHT` stops reaching it: if you set only `CLAUDE_MODEL_LIGHT`, QA moves to Haiku, and if you also set `CLAUDE_MODEL`, QA uses that value. `CLAUDE_MODEL_QA` still wins over both. To keep your previous QA model, set `CLAUDE_MODEL_QA` to it (`sonnet` if you set nothing).
21
+
22
+ ### Fixed
23
+
24
+ - **`canon run --interactive` applies the selected reasoning effort.** Interactive Codex sessions used to ignore it and run at your Codex config's or the model's default effort, even though canon logged the selected value.
25
+
7
26
  ## [3.14.0] — 2026-10-25
8
27
 
9
28
  ### Added
package/README.md CHANGED
@@ -62,7 +62,7 @@ Canon is what happens when you treat those failure modes as engineering problems
62
62
  | Agent forgets a past mistake | **`docs/lessons-learned.md`** — distilled cross-task wisdom that promotes into permanent rules over time |
63
63
  | Reviews loop forever on the same disagreement | **Auto-block on runaway review loops** — per-size caps (see `pipeline-policy.ts`), escalation to human after the cap |
64
64
  | Spec drift between conversation and pipeline | **File-based handoff protocol** — every artifact lives in `tasks/<id>/`, no copy-pasting |
65
- | Sensitive surfaces (auth, payments) get refactored on theoretical grounds | **`delicate: true` flag** that promotes the task to highest-effort tier and triggers extra review discipline |
65
+ | Sensitive surfaces (auth, payments) get refactored on theoretical grounds | **`delicate: true` flag** that moves Codex spec review and implementation to the full model and triggers extra review discipline |
66
66
 
67
67
  ## How it works
68
68
 
@@ -79,14 +79,14 @@ The orchestrator drives this. For each task:
79
79
  spec → spec_review → plan → implement → code_review → qa → human_review
80
80
  ```
81
81
 
82
- The human gates between `spec_review` and `plan` — they approve the spec before the pipeline advances to implementation. Within the chain: automatic loops on `changes_requested` verdicts, model/effort scaling by task size, optional git-worktree isolation, session resumption across phases, and auto-block on runaway loops. See `docs/pipeline-orchestrator.md` for the full mechanics.
82
+ The human gates between `spec_review` and `plan` — they approve the spec before the pipeline advances to implementation. Within the chain: automatic loops on `changes_requested` verdicts, model selection by task size, optional git-worktree isolation, session resumption across phases, and auto-block on runaway loops. See `docs/pipeline-orchestrator.md` for the full mechanics.
83
83
 
84
84
  The pipeline supports two tiers:
85
85
 
86
86
  - **Fast tier** (XS tasks): spec + plan in one Claude session, skip Codex spec review, human gate replaces it.
87
87
  - **Full tier** (S / M / L / XL / delicate tasks): every phase runs separately, Codex reviews specs before they reach the human.
88
88
 
89
- `delicate: true` (set in `status.json` at task creation) promotes any task to full tier and upgrades the model and effort for spec, plan, and code review, plus Codex spec review and implementation; QA and the cold-Codex review lens stay on their regular cells. Use it for sensitive surfaces — auth, payments, persistent storage, anything where a regression has unbounded blast radius.
89
+ `delicate: true` (set in `status.json` at task creation) promotes any task to full tier and upgrades the model and effort for spec, plan, and code review, plus moves Codex spec review and implementation to the full model; QA and the cold-Codex review lens stay on their regular cells. Use it for sensitive surfaces — auth, payments, persistent storage, anything where a regression has unbounded blast radius.
90
90
 
91
91
  A single command runs a task end-to-end:
92
92
 
@@ -161,7 +161,7 @@ The other installed skills (auto-trigger on natural-language phrases — see eac
161
161
  | Skill | When it fires |
162
162
  |---|---|
163
163
  | `/canon-spec` | Authoring a new task — "let's add X", "start a task for…" |
164
- | `/canon-spec-review` | Pre-flighting a spec before invoking the pipeline |
164
+ | `/canon-spec-review` | Pre-flighting a spec before invoking the pipeline; strongly recommended after authoring any spec with logic to vet |
165
165
  | `/canon-inline-review` | Independent cross-review of inline or below-pipeline changes before commit or PR |
166
166
  | `/canon-sweep` | Triaging `docs/lessons-learned.md` — promote, keep, or prune, human-approved |
167
167
  | `/canon-pipeline` | Driving an existing task forward (`canon run`, `--pr`, `--ship`, recovery) |
package/dist/cli/index.js CHANGED
@@ -1694,7 +1694,7 @@ function checkTemplates(cwd) {
1694
1694
  }
1695
1695
  function checkCanonVersion(cwd) {
1696
1696
  const versionPath = join(cwd, ".canon", "version");
1697
- const installedVersion = "3.14.0";
1697
+ const installedVersion = "3.16.0";
1698
1698
  if (!existsSync(versionPath)) {
1699
1699
  return { label: ".canon/version", status: "warn", detail: "missing \u2014 run `canon upgrade`" };
1700
1700
  }
@@ -2223,7 +2223,7 @@ function initCmd(args2) {
2223
2223
  }
2224
2224
  function writeCanonVersion(cwd) {
2225
2225
  const versionPath = join2(cwd, ".canon", "version");
2226
- const version = "3.14.0";
2226
+ const version = "3.16.0";
2227
2227
  mkdirSync(dirname2(versionPath), { recursive: true });
2228
2228
  writeFileSync(versionPath, version + "\n");
2229
2229
  }
@@ -3303,7 +3303,7 @@ function isInstalledSourcePath(sourcePath) {
3303
3303
  return sourcePath.includes("/node_modules/") || sourcePath.includes("\\node_modules\\") || sourcePath.includes("/_npx/") || sourcePath.includes("\\_npx\\");
3304
3304
  }
3305
3305
  function resolveCanonVersion(explicit) {
3306
- return explicit ?? "3.14.0" ?? "dev";
3306
+ return explicit ?? "3.16.0" ?? "dev";
3307
3307
  }
3308
3308
  function resolveOrchestratorCommit(repoRoot, upstreamCommit, runGitAt) {
3309
3309
  const ownToplevel = captureGitOutput(repoRoot, ["rev-parse", "--show-toplevel"], runGitAt);
@@ -5548,7 +5548,7 @@ function currentPinFromManifest(manifest) {
5548
5548
  return "unknown";
5549
5549
  }
5550
5550
  function bakedVersion() {
5551
- return "3.14.0";
5551
+ return "3.16.0";
5552
5552
  }
5553
5553
  function ownPackageName(pkgDir) {
5554
5554
  try {
@@ -6035,7 +6035,7 @@ function runUpgrade(cwd, pkgDir, options = {}) {
6035
6035
  cutoverWarnings.push(docsRefsCheckRel);
6036
6036
  }
6037
6037
  const versionPath = join5(cwd, ".canon", "version");
6038
- const newVersion = "3.14.0";
6038
+ const newVersion = "3.16.0";
6039
6039
  const currentVersion = existsSync5(versionPath) ? readFileSync4(versionPath, "utf8").trim() : null;
6040
6040
  if (currentVersion !== newVersion) {
6041
6041
  pending.push({ rel: ".canon/version", projectPath: versionPath, content: newVersion + "\n" });
@@ -6651,7 +6651,7 @@ Global:
6651
6651
  Run 'canon <command> --help' for details.`);
6652
6652
  }
6653
6653
  function printVersion() {
6654
- console.log("3.14.0");
6654
+ console.log("3.16.0");
6655
6655
  }
6656
6656
  switch (command) {
6657
6657
  case "doctor":
@@ -482,7 +482,7 @@ var WORKTREES_ROOT = process.env.CANON_WORKTREES_ROOT ? path2.resolve(REPO_ROOT,
482
482
  var STALL_TIMEOUT_MS = Number(process.env.PIPELINE_STALL_TIMEOUT_MS) || 10 * 60 * 1e3;
483
483
  var STALL_KILL_GRACE_MS = 3e3;
484
484
  var LEGACY_FALLBACK_ENV_VARS = [
485
- { old: "CLAUDE_MODEL", replacement: "CLAUDE_MODEL_LIGHT / CLAUDE_MODEL_STRONG (still honored as a fallback for every Claude phase, including qa)" },
485
+ { old: "CLAUDE_MODEL", replacement: "CLAUDE_MODEL_LIGHT / CLAUDE_MODEL_STRONG, and CLAUDE_MODEL_QA to keep a legacy model on qa (still honored as a fallback for every Claude phase, including qa)" },
486
486
  { old: "CODEX_MODEL_DEFAULT", replacement: "CODEX_MODEL_MINI (still honored as fallback)" },
487
487
  { old: "CODEX_MODEL_DELICATE", replacement: "CODEX_MODEL_FULL (still honored as fallback)" }
488
488
  ];
@@ -2172,16 +2172,16 @@ function codexMatrix(config3) {
2172
2172
  return {
2173
2173
  spec_review: {
2174
2174
  XS: { model: config3.codexModelMini, effort: "medium" },
2175
- S: { model: config3.codexModelMini, effort: "medium" },
2175
+ S: { model: config3.codexModelMini, effort: "high" },
2176
2176
  M: { model: config3.codexModelMini, effort: "high" },
2177
2177
  L: { model: config3.codexModelMini, effort: "high" },
2178
2178
  XL: { model: config3.codexModelFull, effort: "high" }
2179
2179
  },
2180
2180
  implement: {
2181
- XS: { model: config3.codexModelMini, effort: "medium" },
2182
- S: { model: config3.codexModelMini, effort: "medium" },
2183
- M: { model: config3.codexModelMini, effort: "high" },
2184
- L: { model: config3.codexModelMini, effort: "high" },
2181
+ XS: { model: config3.codexModelMini, effort: "xhigh" },
2182
+ S: { model: config3.codexModelMini, effort: "xhigh" },
2183
+ M: { model: config3.codexModelMini, effort: "max" },
2184
+ L: { model: config3.codexModelMini, effort: "max" },
2185
2185
  XL: { model: config3.codexModelFull, effort: "high" }
2186
2186
  },
2187
2187
  code_review: {
@@ -2193,6 +2193,7 @@ function codexMatrix(config3) {
2193
2193
  }
2194
2194
  };
2195
2195
  }
2196
+ var QA_DEFAULT_MODEL = "claude-haiku-5-5";
2196
2197
  var CLAUDE_CELLS = {
2197
2198
  spec: {
2198
2199
  XS: { tier: "light", effort: "medium" },
@@ -2216,11 +2217,11 @@ var CLAUDE_CELLS = {
2216
2217
  XL: { tier: "strong", effort: "high" }
2217
2218
  },
2218
2219
  qa: {
2219
- XS: { tier: "light", effort: "medium" },
2220
- S: { tier: "light", effort: "medium" },
2221
- M: { tier: "light", effort: "medium" },
2222
- L: { tier: "light", effort: "medium" },
2223
- XL: { tier: "light", effort: "medium" }
2220
+ XS: { tier: "qa", effort: "medium" },
2221
+ S: { tier: "qa", effort: "medium" },
2222
+ M: { tier: "qa", effort: "medium" },
2223
+ L: { tier: "qa", effort: "medium" },
2224
+ XL: { tier: "qa", effort: "medium" }
2224
2225
  }
2225
2226
  };
2226
2227
  function claudePinFor(phase, size, config3) {
@@ -2239,6 +2240,7 @@ function resolveClaudeModel(phase, size, config3) {
2239
2240
  const pin = claudePinFor(phase, size, config3);
2240
2241
  if (pin !== null) return pin;
2241
2242
  const tier = CLAUDE_CELLS[phase][size].tier;
2243
+ if (tier === "qa") return config3.claudeModelLegacy ?? QA_DEFAULT_MODEL;
2242
2244
  const tierModel = tier === "light" ? config3.claudeModelLight : config3.claudeModelStrong;
2243
2245
  if (tierModel !== null) return tierModel;
2244
2246
  if (config3.claudeModelLegacy !== null) return config3.claudeModelLegacy;
@@ -2295,7 +2297,7 @@ var config2 = {
2295
2297
  claudeModelStrong: process.env.CLAUDE_MODEL_STRONG || null,
2296
2298
  claudeModelLegacy: process.env.CLAUDE_MODEL ?? null,
2297
2299
  codexModelMini: process.env.CODEX_MODEL_MINI ?? process.env.CODEX_MODEL_DEFAULT ?? "gpt-6-luna",
2298
- codexModelFull: process.env.CODEX_MODEL_FULL ?? process.env.CODEX_MODEL_DELICATE ?? "gpt-6-sol",
2300
+ codexModelFull: process.env.CODEX_MODEL_FULL ?? process.env.CODEX_MODEL_DELICATE ?? "gpt-6.1-sol",
2299
2301
  maxReviewLoops: parseMaxReviewLoops(process.env.MAX_REVIEW_LOOPS),
2300
2302
  claudeBudget: process.env.CLAUDE_BUDGET ?? null
2301
2303
  };
@@ -2814,7 +2816,7 @@ ${renderedPrompt}`;
2814
2816
  }
2815
2817
 
2816
2818
  // src/orchestrator/agents/codex.ts
2817
- var VALID_CODEX_EFFORTS = ["none", "minimal", "low", "medium", "high", "xhigh"];
2819
+ var VALID_CODEX_EFFORTS = ["none", "minimal", "low", "medium", "high", "xhigh", "max"];
2818
2820
  function invalidCodexEffortMessage(effort) {
2819
2821
  if (VALID_CODEX_EFFORTS.includes(effort)) return null;
2820
2822
  return `Invalid Codex reasoning effort "${effort}" \u2014 canon resolved this value for the current phase/size and passes it via \`-c model_reasoning_effort=${effort}\`, but the Codex CLI only accepts: ${VALID_CODEX_EFFORTS.join("|")}. Effort is not configurable \u2014 canon resolves it from a fixed phase/size matrix, so this is a bug in canon rather than in your setup. Editing model_reasoning_effort in ~/.codex/config.toml will not help: the per-invocation override supersedes it. Please report this along with the task size and phase.`;
@@ -2832,6 +2834,7 @@ ${renderedPrompt}`;
2832
2834
  let status = "ok";
2833
2835
  let tokens;
2834
2836
  let sessionId = null;
2837
+ const effortFlag = ["-c", `model_reasoning_effort=${effort}`];
2835
2838
  try {
2836
2839
  if (interactive) {
2837
2840
  console.log("");
@@ -2839,7 +2842,7 @@ ${renderedPrompt}`;
2839
2842
  console.log("Prompt loaded. You're in the driver's seat.");
2840
2843
  console.log("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
2841
2844
  console.log("");
2842
- runCommandOrDie("codex", ["-m", model, "-C", cwd, effectivePrompt], { cwd });
2845
+ runCommandOrDie("codex", ["-m", model, ...effortFlag, "-C", cwd, effectivePrompt], { cwd });
2843
2846
  return {
2844
2847
  exitCode: 0,
2845
2848
  signal: null,
@@ -2850,7 +2853,6 @@ ${renderedPrompt}`;
2850
2853
  sessionId: null
2851
2854
  };
2852
2855
  }
2853
- const effortFlag = ["-c", `model_reasoning_effort=${effort}`];
2854
2856
  const sandboxFlags = resumeId ? [] : ["--sandbox", "workspace-write"];
2855
2857
  const args = resumeId ? ["exec", "resume", resumeId, "--json", ...effortFlag, effectivePrompt, "-m", model] : ["exec", "--json", ...effortFlag, ...sandboxFlags, effectivePrompt, "-m", model, "-C", cwd];
2856
2858
  const displayChunks = [];
@@ -5353,10 +5355,10 @@ var plan_default = "You are writing implementation plans for {{taskScope}} for {
5353
5355
  var qa_default = 'You are writing QA summaries for {{taskScope}} for {{projectName}}.\n\n{{{startup}}}\n\nTasks:\n{{{taskLines}}}\n\nFor each task:\n1. **Use the Write tool** to create tasks/<id>/done.md \u2014 plain-English summary for the human. Include: what changed, files changed, how to test, test results, human verification required, decisions made, open questions.\n \u26A0\uFE0F CRITICAL: Use the `Write` tool \u2014 do NOT simply output the done.md content as text in your response. Content in your chat reply does not get saved to disk. The pipeline validates that done.md contains real content (not the template) before advancing. Write the file.\n2. Read the latest `## Validation Outcomes` table in `tasks/<id>/handoff.md`, including any later iteration `### Re-run validation` tables. If any check\'s latest result is `human_pending`, include a **Human Verification Required** section in done.md that lists each pending check and its Notes. If none remain, write `None.` in that section. Do not hide `human_pending` checks inside the generic Test Results table.\n - If the human chooses to waive or defer a pending check later, the waiver line in done.md must begin with `Acknowledged:`. The `human_review` gate only treats that explicit prefix as a waiver.\n - Preserve `deferred_by_spec` rows in Test Results with the spec citation from Notes; do not translate them to `Pass`.\n3. Include a **Proposed Changelog** section in done.md \u2014 the changelog **entry text only**. Do **NOT** include a version number, a "Proposed version" line, or a bump tier (Patch/Minor/Major): the version is decided at the release step (`/canon-changelog` + human), never by QA.\n - **Canon release rules (non-negotiable)**: (1) Agents do not bump versions or land changelog edits without explicit scope authorization. (2) The QA step proposes a draft changelog entry text only \u2014 not the version number. (3) Changelog + version bump are committed separately from code changes (when a project versions its releases). (4) No major versioning surprises: if a task introduces a breaking change the spec didn\'t flag, raise it before shipping.\n - Read `docs/decisions.md` \xA7"Versioning and release policy" for this project\'s changelog **scope** (which changes warrant an entry). The bump tier / SemVer interpretation is the release step\'s call, not QA\'s \u2014 do not propose one.\n - If CHANGELOG.md exists, read the top of it (the most recent version section) to calibrate on scope and voice.\n - Apply the "would a user notice" test to every candidate bullet (or the project\'s equivalent scope test): if a candidate falls outside the project\'s defined changelog scope, omit it. If a task is entirely out of scope, say so explicitly ("no user-facing change \u2014 omit from changelog") rather than inventing a bullet.\n - Implementation mechanics belong in the "What Changed" section above \u2014 not in the proposed changelog.\n The human finalizes the changelog entry.\n4. **For single tasks only \u2014 use the Write tool** to create `tasks/<id>/pr-body.md` \u2014 the outward-facing PR body draft for `--pr`. Write it as if a human wrote it after doing the work.\n {{#prTemplate}}\n The repo has this PR template. Fill every section with specifics from what shipped. Keep the headings; replace every placeholder:\n\n {{{prTemplate}}}\n {{/prTemplate}}\n\n {{^prTemplate}}\n No PR template found. Use this structure:\n\n ## Summary\n 1\u20133 bullets: what changed and why.\n\n ## Changes\n Key files or areas touched, described for a reviewer.\n\n ## How to Test\n Steps a reviewer can follow to verify the change.\n\n ## Notes for Reviewer\n Any context, caveats, or follow-up items.\n {{/prTemplate}}\n \u26A0\uFE0F Write as the human engineer who did the work \u2014 not as the AI or tool that produced it (Claude, Codex, canon, an LLM). \u2705 e.g. "Fix the pagination off-by-one that dropped the last row." \u274C e.g. "\u{1F916} Generated with Claude Code."\n Skip this step entirely for bundle tasks \u2014 per-task bodies are not combined for bundle PRs.\n\n**Citing files in task artifacts:** cite every repository file by its full path from the repository root, including any workspace or package prefix \u2014 never a path relative to a package or to the current directory. For a file that does not exist \u2014 one this task deleted, or one only planned \u2014 use the markdown-link form `[path](path)` instead of backticks, because `docs-refs-check` reports a backticked path to a missing file as a broken reference.\n\nAfter writing all done.md files:\n- Read tasks/<id>/notes.md for each task. For each insight, ask: "would this have changed how a *different* task was approached?" If yes, **append** one new entry for *this* task to its own lessons fragment, using the entry format in docs/lessons-learned.md. The fragment holds only `###` entries: create it if it doesn\'t exist, and give it no title or other `#`/`##` heading. Do not edit docs/lessons-learned.md; `--ship` folds the fragments into it. If no, the detail stays in notes.md only. Append-only: never edit, prune, promote, reorganize, or delete existing entries \u2014 not this task\'s earlier entries, and never another task\'s. Promoting entries into permanent docs (patterns.md / decisions.md) and pruning the buffer is a **human-initiated, human-approved** action \u2014 never perform it during QA, and no entry count ever triggers it. (See docs/lessons-learned.md \u2192 "How to use this doc".)\n\n{{{lessonsFragments}}}\n\n- Add a `## Quality Log` section to done.md with the five judgment cells the pipeline cannot derive from status.json. Do **not** edit docs/task-quality-log.md directly \u2014 the qa \u2192 done transition writes or upserts that row automatically into each task\'s quality-log.md fragment from status.json plus this section. `--ship` folds it into the shared log.\n\n ```markdown\n ## Quality Log\n - Spec verdict: <the FIRST spec_review verdict this task ever received \u2014 approved / approved_with_nits / changes_requested; leave blank only if truly unknown>\n - Human reroute?: <Yes/No \u2014 did the human reroute this task at any phase, forcing rework? Do not infer this from any reroute counter; if this is a fresh QA pass with no human reroute yet, answer No>\n - Dropped ACs: <count of ACs the implementation missed, caught in code review>\n - Validation gaps: <count of validation checks that should have run but did not>\n - Notes: <one-line summary of anything notable \u2014 single line, no embedded line breaks>\n ```\n\n On a re-upsert after a reroute, only fill in cells that changed or that you are correcting \u2014 an omitted cell keeps its previously recorded value. In particular, do not overwrite an already-recorded `Spec verdict` with the current status.json verdict; that column means the first spec_review verdict, and status.json only retains the latest.\n\n**Handoff Validation pre-merge checklist** (include in `done.md` Human Verification section if any item cannot be confirmed):\n- [ ] Version correct (per project policy; skip if unversioned)\n- [ ] Changelog updated if needed (per project policy; skip if unversioned)\n- [ ] PR body current\n- [ ] Final CI/CD checks green\n- [ ] Final diff matches spec intent\n\n**Output Format for Human** \u2014 `done.md` must contain:\n1. One-paragraph plain-English summary\n2. Files changed\n3. How to test (product-level steps, not code)\n4. Test results table\n5. Decisions made during implementation\n6. Open questions needing human input\n\n**Code is Canonical; Docs Reference Symbols**: Code is the source of truth for anything derivable from code: numbers, thresholds, file locations, function signatures, type shapes, observable behavior. Docs that restate these facts inline rot silently \u2014 reference the symbol or path; do not restate the value.\n\n**Commit Ownership** \u2014 three change categories:\n- Code changes \u2192 task branch, committed by the orchestrator after Codex static validation.\n- Pre-implement scaffold \u2192 base branch, committed by the orchestrator before first implement.\n- Changelog + version bump (if versioned) \u2192 separate commit, human + Claude, after human_review.\n\n- **Docs freshness \u2014 Two-checkpoint**: {{#docsEditable}}scan these docs ({{{docsEditable}}}) for references that {{docsScope}} *contradicts* \u2014 a renamed symbol, a moved file, a behavior this task changed \u2014 and correct those stale references. That is the only edit QA makes to permanent docs.{{/docsEditable}}{{^docsEditable}}no docs are configured for correction in this project, so QA makes no edits to permanent docs under this step.{{/docsEditable}} Do not add new lessons, pitfalls, or decisions here, and do not promote buffer entries \u2014 promotion is the human sweep, not Docs freshness.\n{{#docsFlagged}}\n- **Docs drift (flag only)**: also check {{{docsFlagged}}} for statements {{docsScope}} makes stale. Do **not** edit these docs. Report each stale statement in a `## Docs drift` section of done.md (doc, the stale statement, and what is now true). Omit the section if nothing is stale.\n{{/docsFlagged}}\n- **Buffer signal** (not an action): after appending, if docs/lessons-learned.md plus this task\'s lessons fragment now hold more than ~15 entries, add one line to this task\'s done.md \u2014 `Maintenance: lessons-learned.md plus this task\u2019s fragment have N entries; a human lessons sweep is due (see docs/lessons-learned.md \u2192 "How to use this doc").` Do not perform the sweep yourself.\n\nWhen done, run (use the Bash tool \u2014 do not just output the command as text):\n{{{phaseCommands}}}\n';
5354
5356
 
5355
5357
  // src/orchestrator/prompts/templates/spec.md
5356
- var spec_default = '{{{header}}}\n\n{{{startup}}}\n\n{{{instructions}}}\n{{{bundleNote}}}\n{{#doneNote}}\nNote: {{{doneNote}}}{{/doneNote}}\n\n{{#docsMapBlock}}\n{{{docsMapBlock}}}\n\n{{/docsMapBlock}}\n**Spec-writing rules of thumb** \u2014 apply when writing each spec:\n\n- **Name effects to DELETE** \u2014 frame supersession as replacement, not add-plus-remove. State: "replace `oldFn` with `newFn`; `oldFn` must not exist after" \u2014 not separate "Add" and "Remove" bullets.\n- **Prefer positive or structural assertions** over prose negations for load-bearing constraints. Back a "must not" with a grep AC or positive reframe; bare prose negation is fragile.\n- **Symbols named in ACs must exist** \u2014 grep for every function or symbol an AC names; verify its return shape matches the spec\'s assumed data contract before marking spec done.\n- **Enumerate every caller and classify its execution context before asserting how a mechanism works** \u2014 "counter X only increments on event Y" or "this writer can only be invoked from surface Z" is a control-flow claim, and control-flow claims need every caller traced. A helper named for one trigger routinely serves several, and whether a call site runs inside an agent\'s own session or in the orchestrator\'s process is invisible from the function body. Grep every caller of the function or counter \u2014 including skills and docs, not just source \u2014 and classify each site as in-agent-session or orchestrator-context before the spec asserts anything about it.\n- **Behavioral contracts, not mechanics** \u2014 ACs describe observable behavior; defer implementation mechanics (signatures, constant names, precise algorithms) to plan/implement. If review rounds start re-litigating literal regexes, exact file layouts, or verbatim command strings, the spec has dropped to code altitude and each round\'s "underspecified" finding just adds more reviewable surface: move the mechanics into a clearly-labeled non-binding *Implementation Notes* subsection owned by plan/implement, rather than wording the same mechanism more precisely.\n- **Bug and flake-fix specs need a confirmed mechanism and red-first test** \u2014 For a bug or flake fix, the spec author must state, in *Problem*, both the confirmed mechanism and how it was confirmed \u2014 not merely a plausible cause. Evidence must match the mechanism class: a deterministic mechanism (fixed inputs hit the same wrong branch every run) may cite a trace with the verified trigger values; a runtime-dependent mechanism (race, timing, environment/config interaction) needs executed confirmation \u2014 a throwaway prototype-fix spike that makes the symptom vanish, or a deterministic forced repro. The author must satisfy that checkpoint before the spec is marked done; on fast-tier (XS, non-delicate) tasks the `spec_review` checkpoint is skipped, so no reviewer will catch an unverified mechanism. The *Acceptance Criteria* must include a red-first regression-test AC: a test that fails on the pre-fix code for the stated reason and passes after the fix. If the mechanism is environment-bound and a faithful repro is impractical, *Problem* must say so and name a deterministic alternative (integration fixture or documented manual repro) instead of skipping verification silently.\n- **At \u22653 spec_review iterations, read the rounds\' content, not the count** \u2014 label each round *edge-fine-tune* (it narrows an already-identified case \u2014 converging, safe to finish once the remaining findings are addressed) or *scope-expansion* (it names a **new** structural case the spec hadn\'t covered \u2014 a real design gap). Round count alone is not the signal; a 6-round run of successive fine-tunes is convergence, not churn. On genuine scope-expansion, don\'t refine again: check whether the last two rejections share one structural root, and whether an existing layer already owns the concern. Dropping the whole mechanism class \u2014 or deleting the mechanism in favor of the layer that already owns it \u2014 is usually cheaper than another round of hardening.\n- **Refactor specs need structural caps** \u2014 provide hard size caps, explicit deletion expectations per symbol, and an allow-list grep AC for any symbol that must disappear.\n- **Refactor specs need a correctness audit** \u2014 For each behavior a refactor declares preserved, record in Problem whether it is correct today, and state one outcome: correct as-is, fixed deliberately (with its own AC), split into a separate task, or kept as a named quirk. A behavior confirmed correct needs only a one-line record. N/A for features and bug fixes.\n- **UI spatial / gesture tasks** \u2014 flag "visual positioning \u2014 expect human iteration" or "runtime debugging required" in *Known Risks*.\n- **Sensitive-surface escalation** \u2014 flag these categories as `delicate: true` in `status.json` and call them out in *Known Risks*: auth, billing / payments, privacy / data handling, destructive operations, schema / data-model migrations, analytics-event changes. The human spec gate is where such tasks stop for review.\n\n{{{selfCheck}}}\n\nWhen done, run (one per task):\n{{{phaseCommands}}}\n';
5358
+ var spec_default = '{{{header}}}\n\n{{{startup}}}\n\n{{{instructions}}}\n{{{bundleNote}}}\n{{#doneNote}}\nNote: {{{doneNote}}}{{/doneNote}}\n\n{{#docsMapBlock}}\n{{{docsMapBlock}}}\n\n{{/docsMapBlock}}\n**Spec-writing rules of thumb** \u2014 apply when writing each spec:\n\n- **Name effects to DELETE** \u2014 frame supersession as replacement, not add-plus-remove. State: "replace `oldFn` with `newFn`; `oldFn` must not exist after" \u2014 not separate "Add" and "Remove" bullets.\n- **Prefer positive or structural assertions** over prose negations for load-bearing constraints. Back a "must not" with a grep AC or positive reframe; bare prose negation is fragile.\n- **Symbols named in ACs must exist** \u2014 grep for every function or symbol an AC names; verify its return shape matches the spec\'s assumed data contract before marking spec done.\n- **Enumerate every caller and classify its execution context before asserting how a mechanism works** \u2014 "counter X only increments on event Y" or "this writer can only be invoked from surface Z" is a control-flow claim, and control-flow claims need every caller traced. A helper named for one trigger routinely serves several, and whether a call site runs inside an agent\'s own session or in the orchestrator\'s process is invisible from the function body. Grep every caller of the function or counter \u2014 including skills and docs, not just source \u2014 and classify each site as in-agent-session or orchestrator-context before the spec asserts anything about it.\n- **Find sibling implementations, not just callers** \u2014 before settling Affected Files, search for every other place that implements the behavior the spec changes: a second listener or entry path, a parallel component, or a test or doc that asserts the current behavior. A change that reaches one of two implementations ships half-done, and the gap usually surfaces only at code review.\n- **Behavioral contracts, not mechanics** \u2014 ACs describe observable behavior; defer implementation mechanics (signatures, constant names, precise algorithms) to plan/implement. If review rounds start re-litigating literal regexes, exact file layouts, or verbatim command strings, the spec has dropped to code altitude and each round\'s "underspecified" finding just adds more reviewable surface: move the mechanics into a clearly-labeled non-binding *Implementation Notes* subsection owned by plan/implement, rather than wording the same mechanism more precisely.\n- **Bug and flake-fix specs need a confirmed mechanism and red-first test** \u2014 For a bug or flake fix, the spec author must state, in *Problem*, both the confirmed mechanism and how it was confirmed \u2014 not merely a plausible cause. Evidence must match the mechanism class: a deterministic mechanism (fixed inputs hit the same wrong branch every run) may cite a trace with the verified trigger values; a runtime-dependent mechanism (race, timing, environment/config interaction) needs executed confirmation \u2014 a throwaway prototype-fix spike that makes the symptom vanish, or a deterministic forced repro. The author must satisfy that checkpoint before the spec is marked done; on fast-tier (XS, non-delicate) tasks the `spec_review` checkpoint is skipped, so no reviewer will catch an unverified mechanism. The *Acceptance Criteria* must include a red-first regression-test AC: a test that fails on the pre-fix code for the stated reason and passes after the fix. If the mechanism is environment-bound and a faithful repro is impractical, *Problem* must say so and name a deterministic alternative (integration fixture or documented manual repro) instead of skipping verification silently.\n- **At \u22653 spec_review iterations, read the rounds\' content, not the count** \u2014 label each round *edge-fine-tune* (it narrows an already-identified case \u2014 converging, safe to finish once the remaining findings are addressed) or *scope-expansion* (it names a **new** structural case the spec hadn\'t covered \u2014 a real design gap). Round count alone is not the signal; a 6-round run of successive fine-tunes is convergence, not churn. On genuine scope-expansion, don\'t refine again: check whether the last two rejections share one structural root, and whether an existing layer already owns the concern. Dropping the whole mechanism class \u2014 or deleting the mechanism in favor of the layer that already owns it \u2014 is usually cheaper than another round of hardening.\n- **Refactor specs need structural caps** \u2014 provide hard size caps, explicit deletion expectations per symbol, and an allow-list grep AC for any symbol that must disappear.\n- **Refactor specs need a correctness audit** \u2014 For each behavior a refactor declares preserved, record in Problem whether it is correct today, and state one outcome: correct as-is, fixed deliberately (with its own AC), split into a separate task, or kept as a named quirk. A behavior confirmed correct needs only a one-line record. N/A for features and bug fixes.\n- **UI spatial / gesture tasks** \u2014 flag "visual positioning \u2014 expect human iteration" or "runtime debugging required" in *Known Risks*.\n- **Sensitive-surface escalation** \u2014 flag these categories as `delicate: true` in `status.json` and call them out in *Known Risks*: auth, billing / payments, privacy / data handling, destructive operations, schema / data-model migrations, analytics-event changes. The human spec gate is where such tasks stop for review.\n\n{{{selfCheck}}}\n\nWhen done, run (one per task):\n{{{phaseCommands}}}\n';
5357
5359
 
5358
5360
  // src/orchestrator/prompts/templates/spec-revision.md
5359
- var spec_revision_default = 'You are revising specs for {{taskScope}} for {{projectName}}.\n\n{{{startup}}}\n\nTasks with review feedback:\n{{{reviewLines}}}\n\nAddress every `changes_requested` finding in each spec.md.{{#combined}}\nAlso update plan.md if spec changes affect the implementation approach.{{/combined}}\n\n{{#docsMapBlock}}\n{{{docsMapBlock}}}\n\n{{/docsMapBlock}}\n**Spec-writing rules of thumb** \u2014 apply when revising each spec:\n\n- **Name effects to DELETE** \u2014 frame supersession as replacement, not add-plus-remove. State: "replace `oldFn` with `newFn`; `oldFn` must not exist after" \u2014 not separate "Add" and "Remove" bullets.\n- **Prefer positive or structural assertions** over prose negations for load-bearing constraints. Back a "must not" with a grep AC or positive reframe; bare prose negation is fragile.\n- **Symbols named in ACs must exist** \u2014 grep for every function or symbol an AC names; verify its return shape matches the spec\'s assumed data contract before marking spec done.\n- **Enumerate every caller and classify its execution context before asserting how a mechanism works** \u2014 "counter X only increments on event Y" or "this writer can only be invoked from surface Z" is a control-flow claim, and control-flow claims need every caller traced. A helper named for one trigger routinely serves several, and whether a call site runs inside an agent\'s own session or in the orchestrator\'s process is invisible from the function body. Grep every caller of the function or counter \u2014 including skills and docs, not just source \u2014 and classify each site as in-agent-session or orchestrator-context before the spec asserts anything about it.\n- **Behavioral contracts, not mechanics** \u2014 ACs describe observable behavior; defer implementation mechanics (signatures, constant names, precise algorithms) to plan/implement. If review rounds start re-litigating literal regexes, exact file layouts, or verbatim command strings, the spec has dropped to code altitude and each round\'s "underspecified" finding just adds more reviewable surface: move the mechanics into a clearly-labeled non-binding *Implementation Notes* subsection owned by plan/implement, rather than wording the same mechanism more precisely.\n- **Bug and flake-fix specs need a confirmed mechanism and red-first test** \u2014 For a bug or flake fix, the spec author must state, in *Problem*, both the confirmed mechanism and how it was confirmed \u2014 not merely a plausible cause. Evidence must match the mechanism class: a deterministic mechanism (fixed inputs hit the same wrong branch every run) may cite a trace with the verified trigger values; a runtime-dependent mechanism (race, timing, environment/config interaction) needs executed confirmation \u2014 a throwaway prototype-fix spike that makes the symptom vanish, or a deterministic forced repro. The author must satisfy that checkpoint before the spec is marked done; on fast-tier (XS, non-delicate) tasks the `spec_review` checkpoint is skipped, so no reviewer will catch an unverified mechanism. The *Acceptance Criteria* must include a red-first regression-test AC: a test that fails on the pre-fix code for the stated reason and passes after the fix. If the mechanism is environment-bound and a faithful repro is impractical, *Problem* must say so and name a deterministic alternative (integration fixture or documented manual repro) instead of skipping verification silently.\n- **At \u22653 spec_review iterations, read the rounds\' content, not the count** \u2014 label each round *edge-fine-tune* (it narrows an already-identified case \u2014 converging, safe to finish once the remaining findings are addressed) or *scope-expansion* (it names a **new** structural case the spec hadn\'t covered \u2014 a real design gap). Round count alone is not the signal; a 6-round run of successive fine-tunes is convergence, not churn. On genuine scope-expansion, don\'t refine again: check whether the last two rejections share one structural root, and whether an existing layer already owns the concern. Dropping the whole mechanism class \u2014 or deleting the mechanism in favor of the layer that already owns it \u2014 is usually cheaper than another round of hardening.\n- **Refactor specs need structural caps** \u2014 provide hard size caps, explicit deletion expectations per symbol, and an allow-list grep AC for any symbol that must disappear.\n- **Refactor specs need a correctness audit** \u2014 For each behavior a refactor declares preserved, record in Problem whether it is correct today, and state one outcome: correct as-is, fixed deliberately (with its own AC), split into a separate task, or kept as a named quirk. A behavior confirmed correct needs only a one-line record. N/A for features and bug fixes.\n- **UI spatial / gesture tasks** \u2014 flag "visual positioning \u2014 expect human iteration" or "runtime debugging required" in *Known Risks*.\n- **Sensitive-surface escalation** \u2014 flag these categories as `delicate: true` in `status.json` and call them out in *Known Risks*: auth, billing / payments, privacy / data handling, destructive operations, schema / data-model migrations, analytics-event changes. The human spec gate is where such tasks stop for review.\n\nWhen done, run:\n{{{phaseCommands}}}\n';
5361
+ var spec_revision_default = 'You are revising specs for {{taskScope}} for {{projectName}}.\n\n{{{startup}}}\n\nTasks with review feedback:\n{{{reviewLines}}}\n\nAddress every `changes_requested` finding in each spec.md.{{#combined}}\nAlso update plan.md if spec changes affect the implementation approach.{{/combined}}\n\n{{#docsMapBlock}}\n{{{docsMapBlock}}}\n\n{{/docsMapBlock}}\n**Spec-writing rules of thumb** \u2014 apply when revising each spec:\n\n- **Name effects to DELETE** \u2014 frame supersession as replacement, not add-plus-remove. State: "replace `oldFn` with `newFn`; `oldFn` must not exist after" \u2014 not separate "Add" and "Remove" bullets.\n- **Prefer positive or structural assertions** over prose negations for load-bearing constraints. Back a "must not" with a grep AC or positive reframe; bare prose negation is fragile.\n- **Symbols named in ACs must exist** \u2014 grep for every function or symbol an AC names; verify its return shape matches the spec\'s assumed data contract before marking spec done.\n- **Enumerate every caller and classify its execution context before asserting how a mechanism works** \u2014 "counter X only increments on event Y" or "this writer can only be invoked from surface Z" is a control-flow claim, and control-flow claims need every caller traced. A helper named for one trigger routinely serves several, and whether a call site runs inside an agent\'s own session or in the orchestrator\'s process is invisible from the function body. Grep every caller of the function or counter \u2014 including skills and docs, not just source \u2014 and classify each site as in-agent-session or orchestrator-context before the spec asserts anything about it.\n- **Find sibling implementations, not just callers** \u2014 before settling Affected Files, search for every other place that implements the behavior the spec changes: a second listener or entry path, a parallel component, or a test or doc that asserts the current behavior. A change that reaches one of two implementations ships half-done, and the gap usually surfaces only at code review.\n- **Behavioral contracts, not mechanics** \u2014 ACs describe observable behavior; defer implementation mechanics (signatures, constant names, precise algorithms) to plan/implement. If review rounds start re-litigating literal regexes, exact file layouts, or verbatim command strings, the spec has dropped to code altitude and each round\'s "underspecified" finding just adds more reviewable surface: move the mechanics into a clearly-labeled non-binding *Implementation Notes* subsection owned by plan/implement, rather than wording the same mechanism more precisely.\n- **Bug and flake-fix specs need a confirmed mechanism and red-first test** \u2014 For a bug or flake fix, the spec author must state, in *Problem*, both the confirmed mechanism and how it was confirmed \u2014 not merely a plausible cause. Evidence must match the mechanism class: a deterministic mechanism (fixed inputs hit the same wrong branch every run) may cite a trace with the verified trigger values; a runtime-dependent mechanism (race, timing, environment/config interaction) needs executed confirmation \u2014 a throwaway prototype-fix spike that makes the symptom vanish, or a deterministic forced repro. The author must satisfy that checkpoint before the spec is marked done; on fast-tier (XS, non-delicate) tasks the `spec_review` checkpoint is skipped, so no reviewer will catch an unverified mechanism. The *Acceptance Criteria* must include a red-first regression-test AC: a test that fails on the pre-fix code for the stated reason and passes after the fix. If the mechanism is environment-bound and a faithful repro is impractical, *Problem* must say so and name a deterministic alternative (integration fixture or documented manual repro) instead of skipping verification silently.\n- **At \u22653 spec_review iterations, read the rounds\' content, not the count** \u2014 label each round *edge-fine-tune* (it narrows an already-identified case \u2014 converging, safe to finish once the remaining findings are addressed) or *scope-expansion* (it names a **new** structural case the spec hadn\'t covered \u2014 a real design gap). Round count alone is not the signal; a 6-round run of successive fine-tunes is convergence, not churn. On genuine scope-expansion, don\'t refine again: check whether the last two rejections share one structural root, and whether an existing layer already owns the concern. Dropping the whole mechanism class \u2014 or deleting the mechanism in favor of the layer that already owns it \u2014 is usually cheaper than another round of hardening.\n- **Refactor specs need structural caps** \u2014 provide hard size caps, explicit deletion expectations per symbol, and an allow-list grep AC for any symbol that must disappear.\n- **Refactor specs need a correctness audit** \u2014 For each behavior a refactor declares preserved, record in Problem whether it is correct today, and state one outcome: correct as-is, fixed deliberately (with its own AC), split into a separate task, or kept as a named quirk. A behavior confirmed correct needs only a one-line record. N/A for features and bug fixes.\n- **UI spatial / gesture tasks** \u2014 flag "visual positioning \u2014 expect human iteration" or "runtime debugging required" in *Known Risks*.\n- **Sensitive-surface escalation** \u2014 flag these categories as `delicate: true` in `status.json` and call them out in *Known Risks*: auth, billing / payments, privacy / data handling, destructive operations, schema / data-model migrations, analytics-event changes. The human spec gate is where such tasks stop for review.\n\nWhen done, run:\n{{{phaseCommands}}}\n';
5360
5362
 
5361
5363
  // src/orchestrator/prompts/templates/spec-review-reroute.md
5362
5364
  var spec_review_reroute_default = "You are reviewing {{taskScope}} for {{projectName}}.\n\n{{{startup}}}\n\nA human amended the spec and rerouted this task after implementation began. The original spec was already reviewed and approved. Your job is to review **the amendment and its integration** with the already-approved spec, not to re-litigate settled findings.\n\n{{{roundBanner}}}Tasks with amendments to review:\n{{{taskLines}}}\n\n**Amendment review scope** (for each task \u2014 EXCEPT tasks whose line above marks them EXEMPT: those have no amendment and require no review work this round; do not request changes for a missing Amendment heading on them):\n1. Read `tasks/<id>/spec.md` from your current directory. Locate the exact amendment heading named above: `## Amendment` for round 1, or `## Amendment Round N` for round 2+.\n2. Read `tasks/<id>/spec-review.md` so you know what was already reviewed and do not re-raise settled findings.\n3. Review the amendment itself: is it implementable as written, are ACs verifiable, and are edge cases handled?\n4. Review integration with approved ACs: does the amendment contradict, weaken, duplicate, or leave gaps against previously approved requirements?\n5. Review overall shape: with the amendment included, is the spec still coherent and in scope?\n6. Do **not** read or audit `handoff.md`, `review.md`, or `done.md`. This phase stays in the spec domain.\n\nGrounding rule: if a finding depends on a symbol or file, re-open it before claiming it exists.\n\n**Cross-review rule**: No agent reviews its own output. Claude writes specs \u2192 Codex reviews specs. Codex writes code \u2192 Claude reviews code.\n\n**Diagnose before you fix \u2014 3-role checkpoint**: For any amendment addressing a bug or flake fix, each role owns a checkpoint: the spec author states the confirmed mechanism and its evidence in *Problem* (a deterministic mechanism \u2014 fixed inputs hit the same wrong branch every run \u2014 may cite a trace with the verified trigger values; a runtime-dependent mechanism \u2014 race, timing, environment/config interaction \u2014 needs executed confirmation: a throwaway prototype-fix spike that makes the symptom vanish, or a deterministic forced repro); the reviewer challenges whether that evidence establishes the root cause; the implementer runs the spec's red-first test against the pre-fix code (or, under the environment-bound-and-impractical escape, its named deterministic alternative) and reports the result in the handoff. A mechanism with no confirmation evidence, or evidence below its class's required rung, is a blocking Shape Check concern \u2014 as is a bug/flake amendment carrying neither a red-first regression-test AC nor the explicit environment-bound-and-impractical escape with a named deterministic alternative.\n\n**Verdict rules** (same as normal spec review):\n- `changes_requested` \u2014 one or more blocking findings. The human must revise the amendment and re-run.\n- `approved_with_nits` \u2014 no blockers; non-blocking observations only. Loop exits immediately.\n- `approved` \u2014 no findings.\n\nFor each task, append a new amendment-review section to `tasks/<id>/spec-review.md`; do not overwrite the prior review. Use this exact heading for the section (the orchestrator's evidence gate requires it before advancing):\n - Round 1: `## Amendment Review`\n - Round N >= 2: `## Amendment Review Round N`\nRecord your verdict **inside that section** as a checked box \u2014 the evidence gate reads the verdict from this section, not from the original review above it. Check exactly one of:\n`- [x] **Approved**` / `- [x] **Approved with nits**` / `- [x] **Changes requested**`.\n\nWhen done, run (one per task with actual verdict):\n{{{phaseCommands}}}\n\n<!-- per-round append shape (round 1 omits the round suffix):\n## Amendment Review (round 1)\n## Amendment Review Round N (round N >= 2)\n- [x] **Approved** (check exactly one verdict box)\n> Findings: ...\n-->\n";
@@ -5845,7 +5847,7 @@ function isInstalledSourcePath(sourcePath) {
5845
5847
  return sourcePath.includes("/node_modules/") || sourcePath.includes("\\node_modules\\") || sourcePath.includes("/_npx/") || sourcePath.includes("\\_npx\\");
5846
5848
  }
5847
5849
  function resolveCanonVersion(explicit) {
5848
- return explicit ?? "3.14.0" ?? "dev";
5850
+ return explicit ?? "3.16.0" ?? "dev";
5849
5851
  }
5850
5852
  function resolveOrchestratorCommit(repoRoot, upstreamCommit, runGitAt) {
5851
5853
  const ownToplevel = captureGitOutput(repoRoot, ["rev-parse", "--show-toplevel"], runGitAt);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "canon-ai",
3
- "version": "3.14.0",
3
+ "version": "3.16.0",
4
4
  "type": "module",
5
5
  "description": "A spec-first, multi-agent AI coding pipeline you drop into any repo. The law your agents work under.",
6
6
  "engines": {
@@ -2,7 +2,7 @@
2
2
  name: canon-spec
3
3
  description: Use when the human describes a new feature, bug fix, or refactor that doesn't yet have a canon task — phrases like "let's add X", "I want to fix Y", "we should refactor Z", "start a task for...", or explicit `/canon-spec` invocation. Also triggers on "full send" / "full-send" / "yolo it" which carries through to pipeline launch without further interrupts. Don't use for existing in-progress tasks (use `/canon-pipeline`) or status checks (use `/canon-status`).
4
4
  argument-hint: "[task description or title]"
5
- allowed-tools: Read Glob Grep Write Edit Agent Bash(canon task *) Bash(canon run *) Bash(git branch *) Bash(git status *) Bash(git log *)
5
+ allowed-tools: Read Glob Grep Write Edit Agent Skill Bash(canon task *) Bash(canon run *) Bash(git branch *) Bash(git status *) Bash(git log *)
6
6
  effort: high
7
7
  ---
8
8
 
@@ -66,7 +66,7 @@ Synthesize the sub-agent's findings before proceeding.
66
66
 
67
67
  Before grilling, detect full-send intent from `$ARGUMENTS`: if it contains an explicit `--full-send` flag or the phrase `full send` / `full-send` (case-insensitive), enter full-send mode, print the acknowledgment line below, and carry that mode through the rest of the workflow.
68
68
 
69
- `Full-send mode detected. I'll grill, write the spec, and run the pipeline through to a draft PR without further interrupts.`
69
+ `Full-send mode detected. I'll grill, write and review the spec, and run the pipeline through to a draft PR without further interrupts unless the review raises a scope question.`
70
70
 
71
71
  Assess task size from the description and exploration findings:
72
72
  - **XS**: more than a trivial one-file inline change (>1 file, or real logic), but little-to-no spec premise worth reviewing. This is the smallest way into the pipeline: choosing it over inline buys the pipeline's cross-review direction (Codex implements against written ACs, Claude reviews), a plan, and a real `code_review`.
@@ -154,6 +154,7 @@ Self-check before presenting:
154
154
  - **Prefer positive or structural assertions** over prose negations for load-bearing constraints. Back a "must not" with a grep AC or positive reframe.
155
155
  - **Symbols in ACs must exist** — grep for every named function or symbol; verify return shape matches the spec's assumed data contract.
156
156
  - **Enumerate every caller and classify its execution context before asserting how a mechanism works.** "Counter X only increments on event Y" or "this writer can only be invoked from surface Z" is a control-flow claim, and control-flow claims need every caller traced — a helper named for one trigger routinely serves several, and whether a call site runs inside an agent's own session or in the orchestrator's process is invisible from the function body. Grep every caller of the function or counter (including skills and docs, not just source) and classify each site as in-agent-session or orchestrator-context before the spec asserts anything about it.
157
+ - **Find sibling implementations, not just callers** — before settling Affected Files, search for every other place that implements the behavior the spec changes: a second listener or entry path, a parallel component, or a test or doc that asserts the current behavior. A change that reaches one of two implementations ships half-done, and the gap usually surfaces only at code review.
157
158
  - **Behavioral contracts, not mechanics** — ACs describe observable behavior; defer implementation mechanics to plan/implement. If review rounds start re-litigating literal regexes, exact file layouts, or verbatim command strings, the spec has dropped to code altitude and each round's "underspecified" finding just adds more reviewable surface: move the mechanics into a clearly-labeled non-binding *Implementation Notes* subsection owned by plan/implement, rather than wording the same mechanism more precisely. The recurring form of this trap is an AC that pins exact operation counts over a fixture in which an unchanged async subsystem (a generation queue, a worker pool) performs its own reads inside the measurement window — an adopter spec took five review rounds on one such AC because each revision worded the same brittle count more precisely instead of removing the dependency; make the side effects inert or assert the discriminating deltas, do not itemize the subsystem's arithmetic.
158
159
  - **Bug and flake-fix specs need a confirmed mechanism and red-first test**: For a bug or flake fix, the spec author must state, in *Problem*, both the confirmed mechanism and how it was confirmed — not merely a plausible cause. Evidence must match the mechanism class: a deterministic mechanism (fixed inputs hit the same wrong branch every run) may cite a trace with the verified trigger values; a runtime-dependent mechanism (race, timing, environment/config interaction) needs executed confirmation — a throwaway prototype-fix spike that makes the symptom vanish, or a deterministic forced repro. The author must satisfy that checkpoint before the spec is marked done; on fast-tier (XS, non-delicate) tasks the `spec_review` checkpoint is skipped, so no reviewer will catch an unverified mechanism. The *Acceptance Criteria* must include a red-first regression-test AC: a test that fails on the pre-fix code for the stated reason and passes after the fix. If the mechanism is environment-bound and a faithful repro is impractical, *Problem* must say so and name a deterministic alternative (integration fixture or documented manual repro) instead of skipping verification silently.
159
160
  - **At ≥3 spec_review iterations, read the rounds' content, not the count.** Label each round *edge-fine-tune* (it narrows an already-identified case — converging, safe to finish once the findings are addressed) or *scope-expansion* (it names a **new** structural case the spec hadn't covered — a real design gap). Round count alone is not the signal; a 6-round run of successive fine-tunes is convergence. On genuine scope-expansion, don't refine again: check whether the last two rejections share one structural root, and whether an existing layer already owns the concern. Dropping the whole mechanism class — or deleting the mechanism in favor of the layer that already owns it — is usually cheaper than another round of hardening.
@@ -161,7 +162,7 @@ Self-check before presenting:
161
162
  - **Refactor specs need a correctness audit before "preserve behavior"**: For each behavior a refactor declares preserved, record in Problem whether it is correct today, and state one outcome: correct as-is, fixed deliberately (with its own AC), split into a separate task, or kept as a named quirk. A behavior confirmed correct needs only a one-line record. N/A for features and bug fixes.
162
163
  - **Codebase-wide term renames: gate per string family, not per enumerated hit.** Hand-enumerated grep lists cause round-over-round scope expansion — each review round surfaces a new label *family*, not a missed instance. Decompose the stale term into its distinct string families up front via a structural sweep, then express each family's post-change invariant as a zero-result grep AC with a word-bounded pattern that catches all separator variants and cannot match the new term. Where the old string is a substring of the new one, zero-result gating is structurally impossible — verify those surfaces with positive targeted ACs instead, and document the asymmetry in the spec so reviewers don't read it as a gap. Two checks before finalizing any zero-result grep AC: (1) on prose surfaces (`docs/`, templates, backlog), read every affected file for *paraphrases* of the retired claim — an exact-string grep proves only that the literal phrase is gone, not that the retired behavior is undocumented everywhere; (2) scan the spec's own other ACs, Docs Impact, and Non-Goals for hits they already tolerate (historical docs, a release-time step, incidental test fixtures) and fold those into an explicit permitted-to-remain bucket inside the same AC, so a later section doesn't quietly contradict the gate.
163
164
 
164
- **⛔ STOP — present the spec and wait for approval.**
165
+ **⛔ STOP — present the spec, strongly recommend running `/canon-spec-review` on it before approving, and wait for approval.** The recommendation applies at every size except an XS change with no logic to vet (a typo fix or version bump), which `/canon-spec-review` itself skips: on full tier it catches what Codex `spec_review` tends to miss, such as an unnamed second implementation of the changed behavior, and on fast tier (XS, non-delicate) it is the only automated spec review. If the human runs it, fold the findings they accept into the spec and re-present it. In full-send mode there is no approval stop, so run `/canon-spec-review` on the spec yourself instead, under the same exception. Apply every BLOCKING finding. For each STRONG or NIT finding, use your judgment as the spec author: apply it if it improves the spec, otherwise leave it. If a fix would change the scope the human approved during grilling, such as adding a surface or dropping a Non-Goal, stop and ask the human before applying it; that is the only interrupt full-send keeps. Then continue to Phase 6.
165
166
 
166
167
  ---
167
168
 
@@ -178,7 +179,7 @@ Self-check before presenting:
178
179
  - **Async/stateful:** for async or stateful changes — re-entry, cancellation or unmount, stale state, and ownership.
179
180
 
180
181
  A finding that changes a step goes in that step. If the check contradicts the spec, don't work around it in the plan — on the fast tier no Codex `spec_review` will catch the mismatch. Amend the spec, re-present the change to the human, and wait for renewed approval. The earlier Phase 5 approval does not cover a spec changed after it, so don't record approval in step 2 until the human has approved the amended spec.
181
- 2. Record the human's approval in `tasks/TASK-ID/spec-review.md`: check the **Approved** box and add a one-line note ("Fast tier — human conversational spec approval; Codex spec review skipped"). The phase gate reads this artifact before letting `spec_review` advance.
182
+ 2. Record the human's approval in `tasks/TASK-ID/spec-review.md`: check the **Approved** box and add a one-line note ("Fast tier — human conversational spec approval; Codex spec review skipped"). In full-send mode no human approved the spec, so the note says that instead ("Fast tier full-send — no human spec approval; `/canon-spec-review` run before launch; Codex spec review skipped"), or "review skipped" when the no-logic exception applied. The phase gate reads this artifact before letting `spec_review` advance.
182
183
  3. Advance the phases with the helpers (they rederive the top-level `status` pointer):
183
184
  ```bash
184
185
  canon task phase TASK-ID spec done
@@ -210,7 +211,7 @@ canon run --full-send [--force] TASK-ID
210
211
 
211
212
  ## Related
212
213
 
213
- - `/canon-spec-review` — adversarial pre-pipeline review of the spec. Recommended for S/M/L/XL or delicate tasks before invoking the pipeline.
214
+ - `/canon-spec-review` — adversarial pre-pipeline review of the spec. Strongly recommended after authoring any spec with logic to vet, before approving it and invoking the pipeline.
214
215
  - `/canon-pipeline` — drive the pipeline after spec approval.
215
216
  - `/canon-status` — check what other canon tasks are in flight before committing to scope.
216
217
  - `docs/pipeline-orchestrator.md` — pipeline internals, sizing guide, model/effort matrix. The Validation Matrix is now inline in `.canon/templates/spec.md`.
@@ -49,7 +49,7 @@ Constraints: don't audit factual claims (Agent B's job); don't audit completenes
49
49
 
50
50
  #### Agent B — Factual / ground-truth
51
51
 
52
- Scope: verify every factual claim in the spec against the actual codebase. For each named symbol, file path, function, or behavior: does it exist, is the description correct, and are Affected Files entries real paths? For symbols in ACs, verify signatures, return shapes, and call sites.
52
+ Scope: verify every factual claim in the spec against the actual codebase. For each named symbol, file path, function, or behavior: does it exist, is the description correct, and are Affected Files entries real paths? For symbols in ACs, verify signatures, return shapes, and call sites. Then search for other implementations of the behavior the spec changes (a parallel listener, entry path, or component, or a test or doc that asserts the current behavior) that Affected Files doesn't name; one the change must also reach is BLOCKING.
53
53
 
54
54
  Read whole functions and call sites, not excerpts, when verifying return shapes. Cite `file:line` for every claim. Don't audit shape (Agent A) or stylistic completeness (Agent C).
55
55
 
@@ -2,7 +2,7 @@
2
2
 
3
3
  Reference for driving canon's pipeline: CLI surface, flags, task-management subcommands, pipeline tiers, the model/effort matrix, environment variables, worktree mechanics, session resumption, auto-block thresholds, and recovery patterns. Read on demand when you need to know which flag to use, why canon picked a particular model, or how to recover from a stuck phase.
4
4
 
5
- For **command patterns and snag-recovery flows**, see the `/canon-pipeline` skill at `.claude/skills/canon-pipeline/SKILL.md` (installed by `canon init`). For **adversarial pre-pipeline spec review** on S/M/L/XL or delicate tasks, see `/canon-spec-review`.
5
+ For **command patterns and snag-recovery flows**, see the `/canon-pipeline` skill at `.claude/skills/canon-pipeline/SKILL.md` (installed by `canon init`). For **adversarial pre-pipeline spec review**, strongly recommended for any spec with logic to vet, see `/canon-spec-review`.
6
6
 
7
7
  Canon's phase prompts, skills, and templates deliver reusable roles, implementation rules, validation, git, and release guidance just in time. This file is the source of truth for *how to operate the pipeline*.
8
8
 
@@ -173,7 +173,7 @@ Claude writes QA summary → Human tests
173
173
  - Spec and plan are written in separate Claude sessions.
174
174
  - Codex runs a real spec review before the gate. Spec review starts with a **Shape Check** (is the problem real? is the framing right? is there a materially simpler solution? is the AC decomposition right?) before the implementability probe.
175
175
  - Effective size selects both the Codex and Claude model/effort cells in the matrices below.
176
- - **Optional pre-pipeline self-review**: before invoking `canon run`, the operator can run `/canon-spec-review` (task ID optional — it defaults to the task under discussion) to dispatch three parallel sub-agents (structural / factual / spec-quality) at the spec and surface BLOCKING / STRONG / NIT findings inline. Catches the class of issues Codex's spec_review would surface across 2-3 iterations in one ~15-min pass. Opt-in; most valuable when iteration cost is real.
176
+ - **Recommended pre-pipeline self-review**: after the spec is written and before invoking `canon run`, run `/canon-spec-review` (task ID optional — it defaults to the task under discussion) to dispatch three parallel sub-agents (structural / factual / spec-quality) at the spec and surface BLOCKING / STRONG / NIT findings inline. Catches the class of issues Codex's spec_review would surface across 2-3 iterations in one ~15-min pass, and its factual lens also finds gaps Codex's spec_review tends to miss, such as a second implementation of the changed behavior that the spec does not name. Strongly recommended for every spec with logic to vet; on the fast tier it is the only automated spec review.
177
177
 
178
178
  **Where validation happens**: Project-specific checks (lint, type-check, unit tests, e2e, etc.) run inside agent phases — Codex runs them during `implement` and records outcomes in the handoff; Claude verifies the outcomes table in Stage 1 code review and re-runs selectively when anything looks off. There is no separate orchestrator-run validation phase.
179
179
 
@@ -209,38 +209,38 @@ Set in `status.json` at task creation:
209
209
 
210
210
  Canon's recommended starting policy for when to scope a task as `delicate: true`: *require evidence of real production impact, not theoretical correctness.*
211
211
 
212
- A `delicate` task carries meaningful regression risk on a security-sensitive surface and runs at the most expensive model+effort tier. "It's technically wrong" is not sufficient justification on its own — a hardening pass on auth/payments/storage that no user has ever hit can take significant pipeline time, introduce regression risk, and produce zero customer-visible value. Before scoping any task as `delicate: true`, require at least one confirmed production incident or a concrete upcoming flow that depends on the fix. Note the incident in the spec's *Problem* section.
212
+ A `delicate` task carries meaningful regression risk on a security-sensitive surface and promotes Codex spec review and implementation to the full model, alongside stronger Claude review. "It's technically wrong" is not sufficient justification on its own — a hardening pass on auth/payments/storage that no user has ever hit can take significant pipeline time, introduce regression risk, and produce zero customer-visible value. Before scoping any task as `delicate: true`, require at least one confirmed production incident or a concrete upcoming flow that depends on the fix. Note the incident in the spec's *Problem* section.
213
213
 
214
214
  This is a default — your project can adopt a stricter or looser bar in `docs/decisions.md` if it has different risk tolerance (e.g., a medical-data project with regulatory exposure may want proactive hardening).
215
215
 
216
216
  ## Codex Model/Effort Matrix
217
217
 
218
- Codex model and effort scale with task size:
218
+ Codex model and effort come from a fixed phase × size matrix:
219
219
 
220
220
  | Phase | XS | S | M | L | XL / delicate |
221
221
  |---|---|---|---|---|---|
222
- | `spec_review` | — (skipped) | mini / medium | mini / high | mini / high | full / high |
223
- | `implement` | mini / medium | mini / medium | mini / high | mini / high | full / high |
222
+ | `spec_review` | — (skipped) | mini / high | mini / high | mini / high | full / high |
223
+ | `implement` | mini / xhigh | mini / xhigh | mini / max | mini / max | full / high |
224
224
  | `code_review` (cold lens) | mini / high | mini / high | mini / high | mini / high | mini / high |
225
225
 
226
- Codex is tuned for token efficiency — the mini model handles most phases; the full model only comes out for XL or delicate work. XL/delicate implement runs at `high`, not `xhigh`: reasoning models have shown overthinking at `xhigh` with open-ended tool access — cost and latency without a quality gain. Raise via env only if your own evaluation shows under-reasoning.
226
+ Codex is tuned for token efficiency — the mini model handles most phases; the full model only comes out for XL or delicate work. Effort comes only from this matrix; there is no Codex effort environment override. M/L implement runs use Luna at `max`, while XL/delicate runs use Sol at `high`: delicate work already gets the stronger model, and Sol at `xhigh` costs about 1.5–2× Sol at `high`. Re-evaluating the full model's effort curve remains a separate follow-up.
227
227
 
228
- The cold-Codex `code_review` lens is the exception to size scaling: as a mandatory hard-fail gate, it runs at flat `high` effort and stays on the mini model at every size, including XL/delicate.
228
+ The cold-Codex `code_review` lens is deliberately flat: as a mandatory hard-fail gate, it runs at `high` effort and stays on the mini model at every size, including XL/delicate.
229
229
 
230
- `spec_review` runs at `high` from M upward. Task-history analysis found M's excess code_review iterations weren't an implement-quality gap (non-rerouted M and L tasks ran at nearly identical iteration counts) but a reroute-severity gap, with M's lighter spec_review effort the leading hypothesis. M and L also differ on loop cap and budget, so this isn't a proven sole cause; if you retune the matrix, re-measure your own M vs. L reroute rate.
230
+ `spec_review` runs at `high` at every size that runs it. S moved up from `medium` after a replay of approved specs whose tasks were later rerouted: `high` caught more of the known spec gaps than `medium` and blocked no more clean specs, for about twice the tokens of a short review. `xhigh` and `max` caught slightly more again but cost 3–5× `high`'s output tokens. M moved up earlier: task-history analysis found M's excess code_review iterations weren't an implement-quality gap (non-rerouted M and L tasks ran at nearly identical iteration counts) but a reroute-severity gap, with M's lighter spec_review effort the leading hypothesis. M and L also differ on loop cap and budget, so this isn't a proven sole cause; if you retune the matrix, re-measure your own M vs. L reroute rate.
231
231
 
232
232
  ## Claude Model/Effort Matrix
233
233
 
234
- Claude model and effort scale with effective task size. The light tier defaults to `sonnet`, and the strong tier defaults to `opus`:
234
+ Claude model and effort scale with effective task size. The light tier defaults to `sonnet`, the strong tier defaults to `opus`, and `qa` uses its own default model, `claude-haiku-5-5`:
235
235
 
236
236
  | Phase | XS | S | M | L | XL / delicate |
237
237
  |---|---|---|---|---|---|
238
238
  | `spec` | sonnet / medium | sonnet / medium | opus / medium | opus / medium | opus / high |
239
239
  | `plan` | sonnet / medium | sonnet / medium | opus / medium | opus / medium | opus / high |
240
240
  | `code_review` | sonnet / medium | sonnet / medium | opus / medium | opus / medium | opus / high |
241
- | `qa` | sonnet / medium | sonnet / medium | sonnet / medium | sonnet / medium | sonnet / medium |
241
+ | `qa` | claude-haiku-5-5 / medium | claude-haiku-5-5 / medium | claude-haiku-5-5 / medium | claude-haiku-5-5 / medium | claude-haiku-5-5 / medium |
242
242
 
243
- `spec`, `plan`, and `code_review` move to the strong tier at M, where sustained judgment matters; only XL/delicate raises effort to `high`. `qa` writes completion artifacts and cannot block or reroute a task, so it stays light/medium at every size. No Claude cell runs above `high`.
243
+ `spec`, `plan`, and `code_review` move to the strong tier at M, where sustained judgment matters; only XL/delicate raises effort to `high`. QA writes completion artifacts and cannot block or reroute a task, so it uses its own default model rather than the light tier. The operator reports an uncontrolled field sample of about 37 tasks across canon-ai and an adopter with no QA failures, budget-cap hits, or retries; the 20 canon-ai `claude-haiku-5-5` QA invocation rows are all `ok` and took 44–214 seconds. Set `CLAUDE_MODEL_QA=sonnet` to restore the previous QA model. No Claude cell runs above `high`.
244
244
 
245
245
  ## Claude Budget Matrix
246
246
 
@@ -255,18 +255,18 @@ Claude phase budgets scale with task size. `spec`/`plan`/`qa` are single-pass Cl
255
255
 
256
256
  ## Environment Variables
257
257
 
258
- Claude uses the light tier for XS/S spec, plan, and code review and for QA at every size. M and above use the strong tier for spec, plan, and code review. A phase pin changes only the model; effort comes from the matrix.
258
+ Claude uses the light tier for XS/S spec, plan, and code review only. QA uses its own default model. M and above use the strong tier for spec, plan, and code review. A phase pin changes only the model; effort comes from the matrix.
259
259
 
260
260
  | Variable | Default | Purpose |
261
261
  |---|---|---|
262
- | `CLAUDE_MODEL_LIGHT` | `sonnet` | Model for light-tier cells: XS/S `spec`/`plan`/`code_review`, and `qa` at every size, unless a phase pin applies. |
262
+ | `CLAUDE_MODEL_LIGHT` | `sonnet` | Model for light-tier cells: XS/S `spec`/`plan`/`code_review`, unless a phase pin applies. |
263
263
  | `CLAUDE_MODEL_STRONG` | `opus` | Model for strong-tier cells: M and above `spec`/`plan`/`code_review`, unless a phase pin applies. |
264
264
  | `CLAUDE_MODEL_SPEC` | _(unset)_ | Pins the `spec` model at every size, replacing the model only; effort still comes from the matrix. |
265
265
  | `CLAUDE_MODEL_PLAN` | _(unset)_ | Pins the `plan` model at every size where a separate plan phase runs (XS fast tier combines spec and plan under the `spec` cell), replacing the model only; effort still comes from the matrix. |
266
266
  | `CLAUDE_MODEL_REVIEW` | _(unset)_ | Pins the `code_review` model for XS/S/M/L only, replacing the model only; effort still comes from the matrix. Does not reach XL/delicate. |
267
267
  | `CLAUDE_MODEL_REVIEW_LARGE` | _(unset)_ | Pins the `code_review` model for XL/delicate only, replacing the model only; effort still comes from the matrix. `_LARGE` means task size, not model tier. Does not reach XS–L. |
268
- | `CLAUDE_MODEL_QA` | _(unset)_ | Pins the `qa` model at every size, replacing the model only; effort still comes from the matrix. |
269
- | `CLAUDE_MODEL` | _(unset; deprecated)_ | Legacy catch-all model for every Claude phase, including `qa`, only where no phase pin or light/strong tier variable is set. Phase pin → tier variable → this fallback → tier default. |
268
+ | `CLAUDE_MODEL_QA` | _(unset; QA default is `claude-haiku-5-5`)_ | Pins the `qa` model at every size, replacing the model only; effort still comes from the matrix. This is QA's only override besides legacy `CLAUDE_MODEL`. Set it to `sonnet` (or the previous light-tier model) to restore the pre-change QA model. |
269
+ | `CLAUDE_MODEL` | _(unset; deprecated)_ | Legacy catch-all model for every Claude phase, including `qa`, only where no phase pin or tier variable applies. Phase pin → tier variable (tiered cells only) → this fallback → default (`sonnet` light, `opus` strong, `claude-haiku-5-5` for `qa`). For QA, this fallback applies when `CLAUDE_MODEL_QA` is unset. |
270
270
  | `CLAUDE_BUDGET` | _(phase- and size-aware)_ | Max spend per Claude phase (USD). Unset → resolved from the Claude Budget Matrix above (phase × size). Set → flat cap applied uniformly across every phase and size (e.g. `CLAUDE_BUDGET=15.00` overrides every phase to $15). |
271
271
  | `CANON_PROJECT_NAME` | _(reads `package.json` "name" or "your project")_ | Name injected into agent prompts. |
272
272
  | `CANON_WORKTREES_ROOT` | .canon/worktrees | Where task worktrees are created. When overridden, the orchestrator warns if the path isn't in `.claude/settings*.json` `additionalDirectories`. |
@@ -279,9 +279,15 @@ Codex model overrides:
279
279
  | Variable | Default | Purpose |
280
280
  |---|---|---|
281
281
  | `CODEX_MODEL_MINI` | `gpt-6-luna` | Codex model for XS/S/M/L non-delicate phases. |
282
- | `CODEX_MODEL_FULL` | `gpt-6-sol` | Codex model for XL or delicate phases. |
282
+ | `CODEX_MODEL_FULL` | `gpt-6.1-sol` | Codex model for XL or delicate phases. |
283
283
  | `MAX_REVIEW_LOOPS` | _size-aware_ | Max `spec_review` and `code_review` iterations before auto-block. Unset → 3 for XS/S/M, 5 for L/XL. |
284
284
 
285
+ M/L implementation uses `max`, which requires a Codex CLI and mini model
286
+ that accept that effort. This was verified only with Codex CLI 0.164.0 and
287
+ `gpt-6-luna`. If an M/L implement call fails on `max`, upgrade the Codex CLI,
288
+ point `CODEX_MODEL_MINI` at a model that accepts `max` (or unset it), or
289
+ install the previous canon release. There is no effort environment override.
290
+
285
291
  These are canon's shipped defaults at the time of release and may lag
286
292
  behind what your local `codex` CLI accepts. **Verify they resolve on your
287
293
  install before relying on the pipeline** — a stale identifier produces a
@@ -549,4 +555,4 @@ Task templates are managed by canon — `canon upgrade` overwrites `.canon/templ
549
555
  - `docs/patterns.md` — implementation patterns and Known Pitfalls.
550
556
  - `docs/decisions.md` — settled architectural decisions.
551
557
  - `/canon-pipeline` — command patterns and snag-recovery flows for operating the pipeline.
552
- - `/canon-spec-review` — adversarial pre-pipeline spec review (multi-agent fan-out) for S/M/L/XL or delicate tasks.
558
+ - `/canon-spec-review` — adversarial pre-pipeline spec review (multi-agent fan-out); strongly recommended after authoring any spec with logic to vet.