canon-ai 3.17.0 → 3.18.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,12 @@
4
4
 
5
5
  ## [Unreleased]
6
6
 
7
+ ## [3.18.0] — 2026-11-04
8
+
9
+ ### Changed
10
+
11
+ - **The mixed-mode cold-Codex review lens uses the full Codex model on L and XL/delicate tasks.** XS, S and M tasks keep the mini model; effort stays `high` everywhere. `CODEX_MODEL_FULL` (or its deprecated fallback `CODEX_MODEL_DELICATE`) now also steers the lens on L, XL and delicate tasks, so a custom full-model override reaches it. A bundle containing an L, XL, or delicate member runs its single cold review on the full model. To restore the mini model at every size, install the previous canon release; there is no per-phase Codex override.
12
+
7
13
  ## [3.17.0] — 2026-11-02
8
14
 
9
15
  ### Added
package/README.md CHANGED
@@ -64,7 +64,7 @@ Canon is what happens when you treat those failure modes as engineering problems
64
64
  | Agent forgets a past mistake | **`docs/lessons-learned.md`** — distilled cross-task wisdom that promotes into permanent rules over time |
65
65
  | 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 |
66
66
  | Spec drift between conversation and pipeline | **File-based handoff protocol** — every artifact lives in `tasks/<id>/`, no copy-pasting |
67
- | Sensitive surfaces (auth, payments) get refactored on theoretical grounds | **`delicate: true` flag** that moves spec review and implementation to the XL cells (the full Codex model in mixed mode) and triggers extra review discipline |
67
+ | Sensitive surfaces (auth, payments) get refactored on theoretical grounds | **`delicate: true` flag** that moves spec review, implementation, and the mixed-mode cold-Codex review lens to the XL cells (the full Codex model in mixed mode) and triggers extra review discipline |
68
68
 
69
69
  ## How it works
70
70
 
@@ -97,7 +97,7 @@ The pipeline supports two tiers:
97
97
  - **Fast tier** (XS tasks): spec + plan in one Claude session; skip the `spec_review` phase, and the human gate replaces it.
98
98
  - **Full tier** (S / M / L / XL / delicate tasks): every phase runs separately, and the spec reviewer checks specs before they reach the human.
99
99
 
100
- `delicate: true` promotes any task to full tier and upgrades the model and effort for spec, plan, and code review, plus moves spec review and implementation to their XL cells (in mixed mode, the full Codex model); QA and, in mixed mode, the cold-Codex review lens stay on their regular cells. Set it with `canon task set <task-id> delicate true`. Use it for sensitive surfaces — auth, payments, persistent storage, anything where a regression has unbounded blast radius.
100
+ `delicate: true` promotes any task to full tier and upgrades the model and effort for spec, plan, and code review, plus moves spec review, implementation, and the mixed-mode cold-Codex review lens to their XL cells (in mixed mode, the full Codex model); QA stays on its regular cells. Set it with `canon task set <task-id> delicate true`. Use it for sensitive surfaces — auth, payments, persistent storage, anything where a regression has unbounded blast radius.
101
101
 
102
102
  A single command runs a task end-to-end:
103
103
 
package/dist/cli/index.js CHANGED
@@ -2025,7 +2025,7 @@ function checkTemplates(cwd) {
2025
2025
  }
2026
2026
  function checkCanonVersion(cwd) {
2027
2027
  const versionPath = join(cwd, ".canon", "version");
2028
- const installedVersion = "3.17.0";
2028
+ const installedVersion = "3.18.0";
2029
2029
  if (!existsSync(versionPath)) {
2030
2030
  return { label: ".canon/version", status: "warn", detail: "missing \u2014 run `canon upgrade`" };
2031
2031
  }
@@ -2569,7 +2569,7 @@ function initCmd(args2) {
2569
2569
  }
2570
2570
  function writeCanonVersion(cwd) {
2571
2571
  const versionPath = join2(cwd, ".canon", "version");
2572
- const version = "3.17.0";
2572
+ const version = "3.18.0";
2573
2573
  mkdirSync(dirname2(versionPath), { recursive: true });
2574
2574
  writeFileSync(versionPath, version + "\n");
2575
2575
  }
@@ -3649,7 +3649,7 @@ function isInstalledSourcePath(sourcePath) {
3649
3649
  return sourcePath.includes("/node_modules/") || sourcePath.includes("\\node_modules\\") || sourcePath.includes("/_npx/") || sourcePath.includes("\\_npx\\");
3650
3650
  }
3651
3651
  function resolveCanonVersion(explicit) {
3652
- return explicit ?? "3.17.0" ?? "dev";
3652
+ return explicit ?? "3.18.0" ?? "dev";
3653
3653
  }
3654
3654
  function resolveOrchestratorCommit(repoRoot, upstreamCommit, runGitAt) {
3655
3655
  const ownToplevel = captureGitOutput(repoRoot, ["rev-parse", "--show-toplevel"], runGitAt);
@@ -5957,7 +5957,7 @@ function currentPinFromManifest(manifest) {
5957
5957
  return "unknown";
5958
5958
  }
5959
5959
  function bakedVersion() {
5960
- return "3.17.0";
5960
+ return "3.18.0";
5961
5961
  }
5962
5962
  function ownPackageName(pkgDir) {
5963
5963
  try {
@@ -6454,7 +6454,7 @@ function runUpgrade(cwd, pkgDir, options = {}) {
6454
6454
  cutoverWarnings.push(docsRefsCheckRel);
6455
6455
  }
6456
6456
  const versionPath = join5(cwd, ".canon", "version");
6457
- const newVersion = "3.17.0";
6457
+ const newVersion = "3.18.0";
6458
6458
  const currentVersion = existsSync5(versionPath) ? readFileSync4(versionPath, "utf8").trim() : null;
6459
6459
  if (currentVersion !== newVersion) {
6460
6460
  pending.push({ rel: ".canon/version", projectPath: versionPath, content: newVersion + "\n" });
@@ -7073,7 +7073,7 @@ Global:
7073
7073
  Run 'canon <command> --help' for details.`);
7074
7074
  }
7075
7075
  function printVersion() {
7076
- console.log("3.17.0");
7076
+ console.log("3.18.0");
7077
7077
  }
7078
7078
  switch (command) {
7079
7079
  case "doctor":
@@ -762,8 +762,8 @@ function codexMatrix(config3) {
762
762
  XS: { model: config3.codexModelMini, effort: "high" },
763
763
  S: { model: config3.codexModelMini, effort: "high" },
764
764
  M: { model: config3.codexModelMini, effort: "high" },
765
- L: { model: config3.codexModelMini, effort: "high" },
766
- XL: { model: config3.codexModelMini, effort: "high" }
765
+ L: { model: config3.codexModelFull, effort: "high" },
766
+ XL: { model: config3.codexModelFull, effort: "high" }
767
767
  }
768
768
  };
769
769
  }
@@ -6315,7 +6315,7 @@ function isInstalledSourcePath(sourcePath) {
6315
6315
  return sourcePath.includes("/node_modules/") || sourcePath.includes("\\node_modules\\") || sourcePath.includes("/_npx/") || sourcePath.includes("\\_npx\\");
6316
6316
  }
6317
6317
  function resolveCanonVersion(explicit) {
6318
- return explicit ?? "3.17.0" ?? "dev";
6318
+ return explicit ?? "3.18.0" ?? "dev";
6319
6319
  }
6320
6320
  function resolveOrchestratorCommit(repoRoot, upstreamCommit, runGitAt) {
6321
6321
  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.17.0",
3
+ "version": "3.18.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": {
@@ -81,7 +81,7 @@ Most multi-round tasks are healthy — each round closes findings scattered acro
81
81
 
82
82
  Watch for a different shape instead: two or more consecutive rounds keep producing new findings clustered in the same file or mechanism, rather than scattered across the diff. That's a plausible sign the model assigned to this task has hit a capability ceiling on this specific piece of code, not that the task simply has more work in it than expected.
83
83
 
84
- When that happens, before spending another expensive review round at the same tier: let the smaller, cheaper model keep trying at low sizes, but bring in the big guns once a mechanism keeps generating findings at it. Bump `task_size` up a step and resume, using the normal `canon task set` / `canon run` mechanics. A one-step bump often raises effort or budget rather than swapping the underlying model — only reaching the top tier guarantees that — and it interacts with the loop cap, `delicate`, `--full-send`, and bundle membership in ways worth checking in `docs/pipeline-orchestrator.md` rather than assuming.
84
+ When that happens, before spending another expensive review round at the same tier: let the smaller, cheaper model keep trying at low sizes, but bring in the big guns once a mechanism keeps generating findings at it. Bump `task_size` up a step and resume, using the normal `canon task set` / `canon run` mechanics. A one-step bump often raises effort or budget rather than swapping the underlying model — only reaching the top tier guarantees that (in mixed mode, an M→L bump does swap the cold-Codex review lens to the full model) — and it interacts with the loop cap, `delicate`, `--full-send`, and bundle membership in ways worth checking in `docs/pipeline-orchestrator.md` rather than assuming.
85
85
 
86
86
  **If the task is already auto-blocked, the bump alone may not clear it** — the cap only widens on some steps, so if the count is still at or past the new cap, raise `MAX_REVIEW_LOOPS` above it in the same run too, same as a plain cap-raise above. And if the task is already at the top tier and still clustering, that's a stronger signal worth treating as a probable spec or architecture problem rather than a capability one.
87
87
 
@@ -197,7 +197,7 @@ Set by `canon task new` at task creation; change them with `canon task set <id>
197
197
  | Field | Values | Purpose |
198
198
  |---|---|---|
199
199
  | `task_size` | `XS \| S \| M \| L \| XL` | Drives the pipeline tier and effective-size model/effort cells for Codex-held roles in mixed mode and Claude roles. XS is fast-tier; S+ runs the full pipeline (including spec review). |
200
- | `delicate` | `true \| false` | Forces the XL bucket (in mixed mode the full Codex model; high Claude spec/plan/code-review effort, and in `claude` mode high Claude spec-review and implement effort too) regardless of nominal size. Set when an undetected bug has materially harder-to-recover blast radius than a normal bug — common examples: auth, payments, premium gating, persistent storage migrations, security-sensitive cryptography. Project-specific surfaces also qualify (medical PHI, scientific reproducibility, regulated data). The bar is *blast radius*, not difficulty. |
200
+ | `delicate` | `true \| false` | Forces the XL bucket (in mixed mode the full Codex model, including the cold-Codex review lens; high Claude spec/plan/code-review effort, and in `claude` mode high Claude spec-review and implement effort too) regardless of nominal size. Set when an undetected bug has materially harder-to-recover blast radius than a normal bug — common examples: auth, payments, premium gating, persistent storage migrations, security-sensitive cryptography. Project-specific surfaces also qualify (medical PHI, scientific reproducibility, regulated data). The bar is *blast radius*, not difficulty. |
201
201
  | `human_spec_gate` | `true \| false` | **Single-use latch**, not a persistent toggle. `true` arms a one-time halt after `spec_review`, before planning (default: `true`). A human can re-arm it with `canon task set <id> human_spec_gate true`; setting it `false` on a delicate task is refused. The orchestrator flips it to `false` *at the moment it halts*, but a human can also disarm it, so `false` means "fired, pre-cleared, or disarmed," not proof the gate fired. See [Spec gate is a single-use latch](#spec-gate-is-a-single-use-latch). |
202
202
  | `worktree` | `true \| false` | Worktree isolation. `canon task new` scaffolds this to `true`, so worktree mode is the effective default for every scaffolded task — run `canon task set <id> worktree false` to opt out and run in the main checkout. The orchestrator treats an *absent* field as `false`, but that fallback only applies to hand-rolled `status.json` files. See Worktree Isolation below. |
203
203
  | `base_branch` | string (default `"main"`) | Branch the task branches off and PRs against. Auto-set by `canon task new` from the current git checkout at task creation. |
@@ -218,7 +218,7 @@ Set by `canon task new` at task creation; change them with `canon task set <id>
218
218
 
219
219
  Canon's recommended starting policy for when to scope a task as `delicate: true`: *require evidence of real production impact, not theoretical correctness.*
220
220
 
221
- A `delicate` task carries meaningful regression risk on a security-sensitive surface and promotes spec review and implementation to their XL cells (in mixed mode, the full Codex model), alongside stronger Claude review. In `claude` mode it also raises Claude spec-review and implementation effort. "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.
221
+ A `delicate` task carries meaningful regression risk on a security-sensitive surface and promotes spec review, implementation, and, in mixed mode, the cold-Codex review lens to their XL cells (in mixed mode, the full Codex model), alongside stronger Claude review. In `claude` mode it also raises Claude spec-review and implementation effort. "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.
222
222
 
223
223
  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).
224
224
 
@@ -232,11 +232,11 @@ Codex model and effort come from a fixed phase × size matrix:
232
232
  |---|---|---|---|---|---|
233
233
  | `spec_review` | — (skipped) | mini / high | mini / high | mini / high | full / high |
234
234
  | `implement` | mini / xhigh | mini / xhigh | mini / max | mini / max | full / high |
235
- | `code_review` (cold lens) | mini / high | mini / high | mini / high | mini / high | mini / high |
235
+ | `code_review` (cold lens) | mini / high | mini / high | mini / high | full / high | full / high |
236
236
 
237
- 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`.
237
+ Codex is tuned for token efficiency — the mini model handles most phases; the full model runs XL or delicate work and the cold-Codex review lens at L. 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`.
238
238
 
239
- In mixed mode 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.
239
+ In mixed mode the mandatory cold-Codex `code_review` lens runs at `high` effort at every size: the mini model at XS, S and M, and the full model at L and XL/delicate. `CODEX_MODEL_FULL` therefore also steers the lens at L and XL/delicate, and `CODEX_MODEL_MINI` steers it at XS–M. A bundle runs one cold review at its effective size, so one L, XL, or delicate member puts the whole bundle's cold review on the full model.
240
240
 
241
241
  `spec_review` runs at `high` at every size that runs it; `xhigh` and `max` cost 3–5× `high`'s output tokens for marginal gain.
242
242
 
@@ -307,8 +307,8 @@ Codex model overrides:
307
307
 
308
308
  | Variable | Default | Purpose |
309
309
  |---|---|---|
310
- | `CODEX_MODEL_MINI` | `gpt-6-luna` | Codex model for XS/S/M/L non-delicate phases. |
311
- | `CODEX_MODEL_FULL` | `gpt-6.1-sol` | Codex model for XL or delicate phases. |
310
+ | `CODEX_MODEL_MINI` | `gpt-6-luna` | Codex model for non-delicate `spec_review` and `implement` at XS–L, and the non-delicate cold-Codex review lens at XS–M. |
311
+ | `CODEX_MODEL_FULL` | `gpt-6.1-sol` | Codex model for XL or delicate phases, and the cold-Codex review lens at L. |
312
312
  | `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 by nominal `task_size` (`delicate` does not raise it). A non-integer or negative value warns and falls back to the default. |
313
313
 
314
314
  `CODEX_MODEL_DEFAULT` and `CODEX_MODEL_DELICATE` are deprecated fallbacks for `CODEX_MODEL_MINI` / `CODEX_MODEL_FULL`; canon warns when they are set. `CODEX_EFFORT_DEFAULT` / `CODEX_EFFORT_DELICATE` are ignored with a warning.
@@ -498,7 +498,7 @@ PIPELINE_STALL_TIMEOUT_MS=1800000 canon run <id>
498
498
 
499
499
  ## Code Review Diff Injection
500
500
 
501
- In mixed mode, code review starts with a cold-Codex diff review run by the orchestrator in the active worktree: `codex exec review --json -c model_reasoning_effort=<effort> --base <reviewBase> -m <miniModel>`, where canon resolves `<miniModel>` and `<effort>` through the `code_review` policy row (mini / `high` at every size). The captured findings are written verbatim to `tasks/<id>/review-cold-codex.md` for every task in the invocation. Bundles run this once over the combined branch diff, and the same findings reach every member.
501
+ In mixed mode, code review starts with a cold-Codex diff review run by the orchestrator in the active worktree: `codex exec review --json -c model_reasoning_effort=<effort> --base <reviewBase> -m <model>`, where canon resolves `<model>` and `<effort>` through the `code_review` policy row (mini / `high` at XS–M, full / `high` at L and XL/delicate). The captured findings are written verbatim to `tasks/<id>/review-cold-codex.md` for every task in the invocation. Bundles run this once over the combined branch diff, and the same findings reach every member.
502
502
 
503
503
  The review base is the task's base branch for Round 1, effective XL or delicate tasks, an absent or unusable previous-round archive, a delta touching a path outside the previous change set, or a delta over 400 added and deleted lines. Otherwise the base is the reviewed SHA in the newest archive for round N−1. Both path sets include both sides of renames; task artifacts and telemetry do not count toward the path or line triggers. Missing, unresolvable, non-ancestor, or HEAD-equal previous SHAs force a full review, as does a failed delta path or line-stat probe. Mixed-mode invocations add `tasks/<id>/review-cold-codex-run-N.md` with a header recording round, reviewed SHA, scope, base, and reason; the unnumbered file retains only the latest verbatim findings. Claude-mode invocations add `tasks/<id>/review-round-N.md` with the same header and no lens findings, and remove any stale unnumbered findings file. Each family numbers independently using numeric max+1, regardless of the header's round. The round N−1 lookup reads both families, choosing the highest-numbered matching header within each family. If both families match but disagree on the reviewed SHA, the next round runs full scope; if the SHAs agree, either record is usable. The malformed-record check also reads both families.
504
504