canon-ai 3.1.1 → 3.1.3
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 +16 -0
- package/dist/cli/index.js +6 -6
- package/dist/orchestrator/run-task.js +1 -1
- package/package.json +1 -1
- package/templates/.canon/templates/handoff.md +3 -0
- package/templates/.claude/agents/code-review-anchored.md +1 -0
- package/templates/.claude/skills/canon-inline-review/SKILL.md +9 -9
- package/templates/.claude/skills/canon-spec/SKILL.md +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,22 @@
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
## [3.1.3] — 2026-09-08
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- **Code review and handoff guidance now cover three adopter-hit traps.** The anchored code-review lens checks sibling call sites sharing a fixed cross-cutting invariant before closing a round; the handoff template warns against backticking bare directory paths and against citing in-diff files in an out-of-diff `Fail` row's notes.
|
|
12
|
+
|
|
13
|
+
### Changed
|
|
14
|
+
|
|
15
|
+
- **Spec-authoring guidance now calls out ACs that pin exact operation counts across an unrelated async subsystem's own activity**, extending the existing "behavioral contracts, not mechanics" rule of thumb.
|
|
16
|
+
|
|
17
|
+
## [3.1.2] — 2026-09-08
|
|
18
|
+
|
|
19
|
+
### Fixed
|
|
20
|
+
|
|
21
|
+
- **`canon-inline-review` no longer misreads Codex's review output.** The skill now captures Codex's final response directly instead of scanning its full execution transcript, and treats a missing or empty result as a failed review rather than silently reporting "no issues found."
|
|
22
|
+
|
|
7
23
|
## [3.1.1] — 2026-09-07
|
|
8
24
|
|
|
9
25
|
### Fixed
|
package/dist/cli/index.js
CHANGED
|
@@ -1245,7 +1245,7 @@ function checkTemplates(cwd) {
|
|
|
1245
1245
|
}
|
|
1246
1246
|
function checkCanonVersion(cwd) {
|
|
1247
1247
|
const versionPath = join(cwd, ".canon", "version");
|
|
1248
|
-
const installedVersion = "3.1.
|
|
1248
|
+
const installedVersion = "3.1.3";
|
|
1249
1249
|
if (!existsSync(versionPath)) {
|
|
1250
1250
|
return { label: ".canon/version", status: "warn", detail: "missing \u2014 run `canon upgrade`" };
|
|
1251
1251
|
}
|
|
@@ -1742,7 +1742,7 @@ function initCmd(_args) {
|
|
|
1742
1742
|
}
|
|
1743
1743
|
function writeCanonVersion(cwd) {
|
|
1744
1744
|
const versionPath = join2(cwd, ".canon", "version");
|
|
1745
|
-
const version = "3.1.
|
|
1745
|
+
const version = "3.1.3";
|
|
1746
1746
|
mkdirSync(dirname2(versionPath), { recursive: true });
|
|
1747
1747
|
writeFileSync(versionPath, version + "\n");
|
|
1748
1748
|
}
|
|
@@ -2751,7 +2751,7 @@ function isInstalledSourcePath(sourcePath) {
|
|
|
2751
2751
|
return sourcePath.includes("/node_modules/") || sourcePath.includes("\\node_modules\\") || sourcePath.includes("/_npx/") || sourcePath.includes("\\_npx\\");
|
|
2752
2752
|
}
|
|
2753
2753
|
function resolveCanonVersion(explicit) {
|
|
2754
|
-
return explicit ?? "3.1.
|
|
2754
|
+
return explicit ?? "3.1.3" ?? "dev";
|
|
2755
2755
|
}
|
|
2756
2756
|
function resolveOrchestratorCommit(repoRoot, upstreamCommit, runGitAt) {
|
|
2757
2757
|
const ownToplevel = captureGitOutput(repoRoot, ["rev-parse", "--show-toplevel"], runGitAt);
|
|
@@ -4877,7 +4877,7 @@ function currentPinFromManifest(manifest) {
|
|
|
4877
4877
|
return "unknown";
|
|
4878
4878
|
}
|
|
4879
4879
|
function bakedVersion() {
|
|
4880
|
-
return "3.1.
|
|
4880
|
+
return "3.1.3";
|
|
4881
4881
|
}
|
|
4882
4882
|
function ownPackageName(pkgDir) {
|
|
4883
4883
|
try {
|
|
@@ -5351,7 +5351,7 @@ function runUpgrade(cwd, pkgDir, options = {}) {
|
|
|
5351
5351
|
cutoverWarnings.push(docsRefsCheckRel);
|
|
5352
5352
|
}
|
|
5353
5353
|
const versionPath = join5(cwd, ".canon", "version");
|
|
5354
|
-
const newVersion = "3.1.
|
|
5354
|
+
const newVersion = "3.1.3";
|
|
5355
5355
|
const currentVersion = existsSync5(versionPath) ? readFileSync4(versionPath, "utf8").trim() : null;
|
|
5356
5356
|
if (currentVersion !== newVersion) {
|
|
5357
5357
|
pending.push({ rel: ".canon/version", projectPath: versionPath, content: newVersion + "\n" });
|
|
@@ -5659,7 +5659,7 @@ Global:
|
|
|
5659
5659
|
`);
|
|
5660
5660
|
}
|
|
5661
5661
|
function printVersion() {
|
|
5662
|
-
console.log("3.1.
|
|
5662
|
+
console.log("3.1.3");
|
|
5663
5663
|
}
|
|
5664
5664
|
switch (command) {
|
|
5665
5665
|
case "doctor":
|
|
@@ -4961,7 +4961,7 @@ function isInstalledSourcePath(sourcePath) {
|
|
|
4961
4961
|
return sourcePath.includes("/node_modules/") || sourcePath.includes("\\node_modules\\") || sourcePath.includes("/_npx/") || sourcePath.includes("\\_npx\\");
|
|
4962
4962
|
}
|
|
4963
4963
|
function resolveCanonVersion(explicit) {
|
|
4964
|
-
return explicit ?? "3.1.
|
|
4964
|
+
return explicit ?? "3.1.3" ?? "dev";
|
|
4965
4965
|
}
|
|
4966
4966
|
function resolveOrchestratorCommit(repoRoot, upstreamCommit, runGitAt) {
|
|
4967
4967
|
const ownToplevel = captureGitOutput(repoRoot, ["rev-parse", "--show-toplevel"], runGitAt);
|
package/package.json
CHANGED
|
@@ -12,6 +12,8 @@
|
|
|
12
12
|
>
|
|
13
13
|
> **Deleting a file?** In this table use the `[path/to/file.ext](path/to/file.ext)` markdown-link form — **not** backticks and **not** bare prose. Backticks trip `docs-refs-check` (a backtick path-ref to a now-missing path under a `validDirs` dir reads as broken); bare prose fails this table's path parse (the first column must be a backtick-path or a markdown-link). The markdown-link is the one form that satisfies both.
|
|
14
14
|
|
|
15
|
+
> **Never backtick a bare directory path** anywhere in this file (e.g. write "the app source tree (apps/app/src)", not `` `apps/app/src` ``). `docs-refs-check` treats a backticked path as a file reference and aborts the pipeline's auto-commit with "missing file" when it is a directory. Backtick only real files.
|
|
16
|
+
|
|
15
17
|
| File | What Changed |
|
|
16
18
|
|---|---|
|
|
17
19
|
|
|
@@ -71,6 +73,7 @@ Cross-reference each Acceptance Criterion from spec.md and confirm it is met. AC
|
|
|
71
73
|
> | `deferred_by_spec` | Explicitly out of scope per spec. Requires a spec citation in Notes (e.g., `Spec: §Non-Goals — explicitly defers this`). |
|
|
72
74
|
> | `blocked` | Check would have run but infrastructure was unavailable (CI down, network out). Triage required — distinct from `Fail`. |
|
|
73
75
|
>
|
|
76
|
+
> A `Fail` row whose cause lies outside this task's diff: name the result `Fail – unrelated` explicitly, and Notes must cite a specific file reference outside this task's affected files (a sibling worktree path, a fixed-port test's own file, an unrelated spec's path) — the code reviewer only accepts `Fail – unrelated` when Notes names such a reference credibly. Pre-flight's own check is textual — naming a changed file in that row, even to say it passed, can reclassify the whole row as task-owned and reject the handoff. Don't rely on an unqualified filename escaping the check; keep Notes free of any path from this task's diff.
|
|
74
77
|
> Record every check in spec.md's Validation Required section here, plus any extra checks you ran. Required checks should not be marked `N/A` or `not_configured` — run the check or adjust the spec; the code reviewer verifies coverage against the spec. The `Check` cell is for human readability (the pre-flight gate no longer string-matches it against the spec), so write whatever names the check clearly — but keep a check's label identical across a baseline row and any later `### Re-run validation` row so its result updates in place.
|
|
75
78
|
|
|
76
79
|
| Check | Result | Notes |
|
|
@@ -34,6 +34,7 @@ Report every issue you find, including ones you are uncertain about or consider
|
|
|
34
34
|
- **Verify handoff claims by running `git diff HEAD -- <file>`**: the auto-commit step can silently drop edits not in the handoff Changes table — don't trust the handoff claim; diff the working tree to confirm fixes landed.
|
|
35
35
|
- **Delicate-task review must audit cross-cutting guards at every mutation entry point**: when a `delicate: true` task refactors a state/data layer, explicitly verify that auth, gating, and payment guards still hold at every mutation chokepoint — not just at the call sites the spec called out.
|
|
36
36
|
- **Don't infer one git invariant from another**: `git status --porcelain` empty ≠ origin matches HEAD; `origin/<branch>` exists ≠ origin matches HEAD. Do the actual check directly, not a proxy.
|
|
37
|
+
- **A cross-cutting invariant fix isn't verified until every sibling call site with the same shape has been checked**: a captured reference that must be revalidated, or a guard that must run, because an intervening `await` can invalidate it, is the kind of defect that recurs wherever the same shape appears. Once a round establishes the violation is real at one call site, a byte-identical-to-base sibling branch elsewhere in the same file is not "out of scope" — it shares the same shape. Grep the file for every other site matching the pattern before accepting the fix and closing the round.
|
|
37
38
|
|
|
38
39
|
## Return Format
|
|
39
40
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: canon-inline-review
|
|
3
3
|
description: Use when the human wants an independent cross-review of below-pipeline work - uncommitted changes, one commit, or a whole branch before a PR. Triggers on "/canon-inline-review", "review my uncommitted changes", "codex review this", "cross-review before I commit", "review the last commit", and "review my branch". Not for spec compliance (use canon run / --reroute) or the pipeline's own code_review phase.
|
|
4
|
-
allowed-tools: Bash(git status *) Bash(git log *) Bash(git rev-parse *) Bash(git symbolic-ref *) Bash(codex review *) Bash(codex exec review *)
|
|
4
|
+
allowed-tools: Read Bash(git status *) Bash(git log *) Bash(git rev-parse *) Bash(git symbolic-ref *) Bash(mktemp) Bash(mktemp *) Bash(codex review *) Bash(codex exec review *)
|
|
5
5
|
effort: medium
|
|
6
6
|
---
|
|
7
7
|
|
|
@@ -49,20 +49,20 @@ The practical consequence: **the only target you can steer is the uncommitted tr
|
|
|
49
49
|
|
|
50
50
|
`codex review` is the shorthand for `codex exec review`. Use the shorthand when you want the concise form; treat `codex exec review` as the documented form.
|
|
51
51
|
|
|
52
|
-
Run non-interactively with the chosen selector:
|
|
52
|
+
Run non-interactively with the chosen selector, and always add `-o <path>` (a fresh path from `mktemp`) to capture the agent's final response in isolation. Raw stdout is the full exec transcript — a session banner, config block, and every shell command the review itself runs, with the actual review text buried somewhere inside — not just the final message. `-o` writes only that final message, cleanly:
|
|
53
53
|
|
|
54
|
-
- `codex exec review --uncommitted
|
|
55
|
-
- `codex exec review --commit <SHA>`
|
|
56
|
-
- `codex exec review --base <branch>`
|
|
57
|
-
- `codex exec review "<PROMPT>"
|
|
54
|
+
- `codex exec review --uncommitted -o <path>`
|
|
55
|
+
- `codex exec review --commit <SHA> -o <path>`
|
|
56
|
+
- `codex exec review --base <branch> -o <path>`
|
|
57
|
+
- `codex exec review "<PROMPT>" -o <path>` to steer an uncommitted review (prompt-only; do not add `--uncommitted`)
|
|
58
58
|
|
|
59
|
-
Do not pipe stdin into the command. The `review` subcommand already runs non-interactively.
|
|
59
|
+
Do not pipe stdin into the command. The `review` subcommand already runs non-interactively. A successful review always produces text in the `-o` file — even a clean result is a summary sentence, never nothing. Treat an empty or missing `-o` file as a failed invocation, regardless of exit code: check exit status and stderr for the cause, surface the failure, and stop. Never read empty output as "no findings."
|
|
60
60
|
|
|
61
61
|
## Reporting findings
|
|
62
62
|
|
|
63
|
-
|
|
63
|
+
Only proceed here if `<path>` (the `-o` file) is non-empty. Read it for the review content. Do not grep, parse, or otherwise inspect raw stdout — it's the full transcript, not the result, and findings can be anywhere inside it. Summarize the file's content concisely. Group findings by severity, then stop. Do not dump raw output into the session.
|
|
64
64
|
|
|
65
|
-
-
|
|
65
|
+
- The `-o` file is already formatted prose, not JSON: a summary line, then each finding as `Review comment: - [P0]`-`[P3] <title> — <file>:<line>` with its body below. Relay the existing `[P0]`-`[P3]` tags and summary line as-is — don't re-derive severity from wording.
|
|
66
66
|
- If there are findings, report the important ones first and keep the summary short.
|
|
67
67
|
- If there are no findings, say so in one line.
|
|
68
68
|
|
|
@@ -152,7 +152,7 @@ Self-check before presenting:
|
|
|
152
152
|
- **Prefer positive or structural assertions** over prose negations for load-bearing constraints. Back a "must not" with a grep AC or positive reframe.
|
|
153
153
|
- **Symbols in ACs must exist** — grep for every named function or symbol; verify return shape matches the spec's assumed data contract.
|
|
154
154
|
- **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.
|
|
155
|
-
- **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.
|
|
155
|
+
- **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.
|
|
156
156
|
- **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.
|
|
157
157
|
- **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.
|
|
158
158
|
- **Refactor specs need hard structural caps**: size cap, explicit deletion expectations per symbol, grep AC for disappeared symbols.
|