canon-ai 3.4.0 → 3.6.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,32 @@
4
4
 
5
5
  ## [Unreleased]
6
6
 
7
+ ## [3.6.0] — 2026-10-04
8
+
9
+ ### Changed
10
+
11
+ - **`/canon-spec-review` reviewers report every finding they can cite, and the final report does the filtering.** They now use Codex spec review's blocking and nit definitions, so behavior a spec explicitly excludes and verifies as unaffected is a nit at most. The Affected Files check now names the exact heading the pipeline reads, accepts any amendment section, and expects generated or rebuilt outputs to be listed. The reviewers now run as a read-only `spec-review-lens` agent that has no edit tools, can't launch other agents or skills, and is told to use the shell only for reading. Run `canon upgrade`, then start a new Claude Code session to pick it up.
12
+ - **Refactor specs must check that the behavior they preserve is actually correct.** Before a "preserve behavior" criterion, a refactor spec now records in *Problem* whether each preserved behavior is correct today, and gives one outcome for each: correct as-is, fixed deliberately with its own criterion, split into a separate task, or kept as a named quirk. A behavior confirmed correct needs only a one-line record. `/canon-spec`, the spec template, and the pipeline's spec phases all ask for it. Pipeline spec review treats a missing audit as blocking, while `/canon-spec-review` reports it as STRONG. Feature and bug-fix specs are unaffected.
13
+
14
+ ### Fixed
15
+
16
+ - **A task whose base branch can't be diffed now blocks instead of passing scope checks.** If git can't read a task's changes against its base (a stale or mistyped `base_branch`, a deleted base), the unreadable diff used to look like "nothing changed," so scope checks let the work through. Full-send review and the `code_review` pre-flight now block with git's error instead of advancing to QA or looping back to implement. `--pr` and `--push` report that the task's changed files couldn't be computed instead of advising a rebase, and `--force` does not bypass it. Implementation continues, with the full check matrix required.
17
+
18
+ ## [3.5.0] — 2026-09-29
19
+
20
+ ### Added
21
+
22
+ - **A reviewer that can't reach a verdict now stops the run for a human.** If a code-review lens returns nothing usable twice, the foreman sets `code_review` to `blocked` instead of being pushed to invent a verdict; recover with `canon task reset-code-review` for each bundle member, then `canon run`.
23
+ - **Plans include a short feasibility check.** Pipeline, reroute, and `/canon-spec` XS plans now confirm the code they rely on exists, trace the real call path and callers, and name the input that makes each new test fail without the fix.
24
+ - **`CLAUDE_MODEL_LIGHT` and `CLAUDE_MODEL_STRONG` override each tier's model.** Existing per-phase pins and `CLAUDE_MODEL` still work and take precedence in that order.
25
+
26
+ ### Changed
27
+
28
+ - **Claude defaults are re-baselined for the 5.5 generation.** Spec, plan, and code review run Sonnet `medium` for XS/S, Opus `medium` for M/L, and Opus `high` for XL/delicate; QA runs Sonnet `medium` at every size, and nothing runs at `xhigh`. A model pin now replaces only the model, so a Sonnet pin on M/L review or plan drops from `high` to `medium` effort.
29
+ - **Unattended Claude phases get an explicit unattended-session contract.** Like Codex's headless preamble, it says no one will answer questions, finishing means the artifact is written and the phase command has run, and the phase must not start other phases or reviewers.
30
+ - **The code-review foreman accepts clean lens results and bounds delegation.** A lens's empty result is now valid rather than "a bug in the lens," and the foreman spawns only its two lenses plus one re-spawn.
31
+ - **`/canon-spec-review` no longer requires a task ID.** With no argument it reviews the task under discussion, or asks which one when that's ambiguous.
32
+
7
33
  ## [3.4.0] — 2026-09-27
8
34
 
9
35
  ### Added
package/README.md CHANGED
@@ -86,7 +86,7 @@ The pipeline supports two tiers:
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 orchestrator's model and effort across every phase. 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 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.
90
90
 
91
91
  A single command runs a task end-to-end:
92
92
 
package/dist/cli/index.js CHANGED
@@ -555,20 +555,6 @@ import path4 from "path";
555
555
  import { fileURLToPath } from "url";
556
556
  var __filename = fileURLToPath(import.meta.url);
557
557
  var __dirname = path4.dirname(__filename);
558
- function parseMaxReviewLoops(raw) {
559
- if (!raw) return null;
560
- const trimmed = raw.trim();
561
- if (!/^-?\d+$/.test(trimmed)) {
562
- warn(`Invalid MAX_REVIEW_LOOPS value "${raw}"; using the size-aware default.`);
563
- return null;
564
- }
565
- const parsed = Number(trimmed);
566
- if (!Number.isInteger(parsed) || parsed < 0) {
567
- warn(`Invalid MAX_REVIEW_LOOPS value "${raw}"; using the size-aware default.`);
568
- return null;
569
- }
570
- return parsed;
571
- }
572
558
  function resolveRepoRoot() {
573
559
  try {
574
560
  const result = spawnSync("git", ["rev-parse", "--git-common-dir"], { encoding: "utf8" });
@@ -601,15 +587,6 @@ function resolveProjectName() {
601
587
  }
602
588
  var config = {
603
589
  projectName: resolveProjectName(),
604
- claudeBudget: process.env.CLAUDE_BUDGET ?? null,
605
- claudeModelSpec: process.env.CLAUDE_MODEL_SPEC ?? process.env.CLAUDE_MODEL ?? "opus",
606
- claudeModelPlan: process.env.CLAUDE_MODEL_PLAN ?? process.env.CLAUDE_MODEL ?? "sonnet",
607
- claudeModelReview: process.env.CLAUDE_MODEL_REVIEW ?? process.env.CLAUDE_MODEL ?? "sonnet",
608
- claudeModelReviewLarge: process.env.CLAUDE_MODEL_REVIEW_LARGE ?? process.env.CLAUDE_MODEL ?? "opus",
609
- claudeModelQa: process.env.CLAUDE_MODEL_QA ?? process.env.CLAUDE_MODEL ?? "sonnet",
610
- codexModelMini: process.env.CODEX_MODEL_MINI ?? process.env.CODEX_MODEL_DEFAULT ?? "gpt-6-luna",
611
- codexModelFull: process.env.CODEX_MODEL_FULL ?? process.env.CODEX_MODEL_DELICATE ?? "gpt-6-sol",
612
- maxReviewLoops: parseMaxReviewLoops(process.env.MAX_REVIEW_LOOPS),
613
590
  maxContextBytes: Number.parseInt(process.env.MAX_CONTEXT_BYTES ?? String(64 * 1024), 10)
614
591
  };
615
592
 
@@ -1247,7 +1224,7 @@ function checkTemplates(cwd) {
1247
1224
  }
1248
1225
  function checkCanonVersion(cwd) {
1249
1226
  const versionPath = join(cwd, ".canon", "version");
1250
- const installedVersion = "3.4.0";
1227
+ const installedVersion = "3.6.0";
1251
1228
  if (!existsSync(versionPath)) {
1252
1229
  return { label: ".canon/version", status: "warn", detail: "missing \u2014 run `canon upgrade`" };
1253
1230
  }
@@ -1744,7 +1721,7 @@ function initCmd(_args) {
1744
1721
  }
1745
1722
  function writeCanonVersion(cwd) {
1746
1723
  const versionPath = join2(cwd, ".canon", "version");
1747
- const version = "3.4.0";
1724
+ const version = "3.6.0";
1748
1725
  mkdirSync(dirname2(versionPath), { recursive: true });
1749
1726
  writeFileSync(versionPath, version + "\n");
1750
1727
  }
@@ -2754,7 +2731,7 @@ function isInstalledSourcePath(sourcePath) {
2754
2731
  return sourcePath.includes("/node_modules/") || sourcePath.includes("\\node_modules\\") || sourcePath.includes("/_npx/") || sourcePath.includes("\\_npx\\");
2755
2732
  }
2756
2733
  function resolveCanonVersion(explicit) {
2757
- return explicit ?? "3.4.0" ?? "dev";
2734
+ return explicit ?? "3.6.0" ?? "dev";
2758
2735
  }
2759
2736
  function resolveOrchestratorCommit(repoRoot, upstreamCommit, runGitAt) {
2760
2737
  const ownToplevel = captureGitOutput(repoRoot, ["rev-parse", "--show-toplevel"], runGitAt);
@@ -4880,7 +4857,7 @@ function currentPinFromManifest(manifest) {
4880
4857
  return "unknown";
4881
4858
  }
4882
4859
  function bakedVersion() {
4883
- return "3.4.0";
4860
+ return "3.6.0";
4884
4861
  }
4885
4862
  function ownPackageName(pkgDir) {
4886
4863
  try {
@@ -5096,6 +5073,7 @@ var CANON_OWNED = [
5096
5073
  ".claude/skills/canon-sweep/SKILL.md",
5097
5074
  ".claude/agents/code-review-anchored.md",
5098
5075
  ".claude/agents/code-review-cold.md",
5076
+ ".claude/agents/spec-review-lens.md",
5099
5077
  ".canon/templates/status.json",
5100
5078
  ".canon/templates/spec.md",
5101
5079
  ".canon/templates/plan.md",
@@ -5356,7 +5334,7 @@ function runUpgrade(cwd, pkgDir, options = {}) {
5356
5334
  cutoverWarnings.push(docsRefsCheckRel);
5357
5335
  }
5358
5336
  const versionPath = join5(cwd, ".canon", "version");
5359
- const newVersion = "3.4.0";
5337
+ const newVersion = "3.6.0";
5360
5338
  const currentVersion = existsSync5(versionPath) ? readFileSync4(versionPath, "utf8").trim() : null;
5361
5339
  if (currentVersion !== newVersion) {
5362
5340
  pending.push({ rel: ".canon/version", projectPath: versionPath, content: newVersion + "\n" });
@@ -5664,7 +5642,7 @@ Global:
5664
5642
  `);
5665
5643
  }
5666
5644
  function printVersion() {
5667
- console.log("3.4.0");
5645
+ console.log("3.6.0");
5668
5646
  }
5669
5647
  switch (command) {
5670
5648
  case "doctor":