canon-ai 3.3.1 → 3.5.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 +39 -0
- package/README.md +1 -1
- package/dist/cli/index.js +68 -90
- package/dist/orchestrator/run-task.js +582 -236
- package/package.json +1 -1
- package/templates/.canon/templates/handoff.md +1 -1
- package/templates/.canon/templates/review.md +4 -0
- package/templates/.claude/agents/code-review-anchored.md +14 -0
- package/templates/.claude/agents/code-review-cold.md +5 -2
- package/templates/.claude/skills/canon-pipeline/recovery.md +21 -0
- package/templates/.claude/skills/canon-spec/SKILL.md +10 -1
- package/templates/.claude/skills/canon-spec-review/SKILL.md +7 -3
- package/templates/docs/pipeline-orchestrator.md +37 -32
- package/templates/scripts/docs-refs-check.mjs +6 -3
package/package.json
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
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
|
-
> **
|
|
15
|
+
> **Directory paths:** backticking a path to an existing directory is fine — `docs-refs-check` accepts it. A backticked directory path that no longer exists (for example, one this task deleted) can be reported as a missing reference, so describe removed directories in prose or use the markdown-link form above.
|
|
16
16
|
|
|
17
17
|
| File | What Changed |
|
|
18
18
|
|---|---|
|
|
@@ -8,6 +8,8 @@ Code review is synthesized by a foreman from three lenses: an anchored Claude le
|
|
|
8
8
|
|
|
9
9
|
The anchored review runs in two stages on the first round. **Stage 1 is a gate.** If it fails, skip Stage 2 entirely and send back — do not write code-quality findings against code that's about to change.
|
|
10
10
|
|
|
11
|
+
**Scope:** Full — base `<baseBranch>` — reason: Round 1 (initial review)
|
|
12
|
+
|
|
11
13
|
## Stage 1 — Spec Compliance (gate)
|
|
12
14
|
|
|
13
15
|
### Validation Gate
|
|
@@ -99,6 +101,8 @@ breaks routing. Administrative appends use a non-Round heading (e.g.
|
|
|
99
101
|
|
|
100
102
|
## Round N — verifying iteration N-1's response to round N-1
|
|
101
103
|
|
|
104
|
+
**Scope:** Full | Delta — base `<baseBranch or prevSHA>` — reason: <trigger reason, or "delta">
|
|
105
|
+
|
|
102
106
|
### Stage 1 — Acceptance Criteria Re-Check
|
|
103
107
|
|
|
104
108
|
Re-fill this table with every AC from spec.md against the latest code. Earlier AC tables were snapshots of earlier iterations, not reusable proof. ACs whose relevant code paths did not change may be marked `Met (unchanged from round N-1)` with a one-line evidence pointer.
|
|
@@ -8,6 +8,7 @@ You are the anchored code-review lens in canon's three-lens review pipeline. You
|
|
|
8
8
|
Do not write `review.md`.
|
|
9
9
|
Do not run `canon task phase`.
|
|
10
10
|
Do not ask the user for permission to edit files.
|
|
11
|
+
Do this review yourself: spawn no sub-agents and run no review skills or other reviewers.
|
|
11
12
|
|
|
12
13
|
This agent intentionally declares no model override; it inherits the foreman's selected `code_review` model and effort.
|
|
13
14
|
|
|
@@ -25,6 +26,8 @@ Apply canon's existing code-review charter to the diff, spec, handoff, and prior
|
|
|
25
26
|
|
|
26
27
|
Run Stage 2 only if Stage 1 passed.
|
|
27
28
|
|
|
29
|
+
**Project conventions — do this first.** Check whether `docs/patterns.md` exists. If it does, read it before reviewing: use its Trigger Table (or skim the file if it has none) to find the sections relevant to the files the diff changes, skipping unfilled placeholder content marked `TODO[canon]` and `_example_` rows. Check the changed code against those rules. Report each violation as a `risk/guardrail` finding that names the rule it breaks (or as a `correctness bug` when it causes incorrect behavior). A violation of a written project rule is not a nit. Apply this check in every round, limited to code changed by the diff.
|
|
30
|
+
|
|
28
31
|
Find correctness bugs, risk/guardrail issues, optional cleanup/nits, and spec gaps. A changed test that passes against broken behavior is a correctness bug. Reference findings by file:line and AC number where applicable.
|
|
29
32
|
|
|
30
33
|
Report every issue you find, including ones you are uncertain about or consider low-severity. Do not filter for importance or confidence here — the foreman does that downstream. Tag each Stage 2 finding with both a severity and a confidence so the foreman can rank and filter. Coverage is your job; filtering is not.
|
|
@@ -52,3 +55,14 @@ OVERALL_SIGNAL: approve | changes_requested
|
|
|
52
55
|
```
|
|
53
56
|
|
|
54
57
|
If Stage 1 failed, omit Stage 2 findings and set `OVERALL_SIGNAL: changes_requested`.
|
|
58
|
+
|
|
59
|
+
If Stage 1 passes and you find no Stage 2 issues, return:
|
|
60
|
+
|
|
61
|
+
```text
|
|
62
|
+
STAGE_1: pass
|
|
63
|
+
AC_TABLE:
|
|
64
|
+
| AC | Met/Partial/Not Met | note |
|
|
65
|
+
STAGE_1_GAPS: (none)
|
|
66
|
+
STAGE_2_FINDINGS: (none)
|
|
67
|
+
OVERALL_SIGNAL: approve
|
|
68
|
+
```
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: code-review-cold
|
|
3
|
-
description: Spec-blind adversarial code reviewer for canon code_review.
|
|
3
|
+
description: Spec-blind adversarial code reviewer for canon code_review. Receives the diff and base ref, searches code for sibling guards when needed, then returns findings to the foreman.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
You are the cold-Claude code-review lens in canon's three-lens review pipeline. You receive a code diff and base ref only: no spec, no acceptance criteria, no handoff rationale, and no canon docs.
|
|
@@ -8,6 +8,7 @@ You are the cold-Claude code-review lens in canon's three-lens review pipeline.
|
|
|
8
8
|
Do not write `review.md`.
|
|
9
9
|
Do not run `canon task phase`.
|
|
10
10
|
Do not ask the user for permission to edit files.
|
|
11
|
+
Do this review yourself: spawn no sub-agents and run no review skills or other reviewers.
|
|
11
12
|
|
|
12
13
|
This agent intentionally declares no model override; it inherits the foreman's selected `code_review` model and effort.
|
|
13
14
|
|
|
@@ -15,9 +16,11 @@ Review adversarially for bugs the diff introduces: race conditions, lifecycle is
|
|
|
15
16
|
|
|
16
17
|
**Diff-local pattern**: when you see the same safety check, guard, or invariant applied at multiple call sites but a new call site introduced by this diff is missing it, flag it — an inconsistently applied guard is a correctness gap regardless of intent.
|
|
17
18
|
|
|
19
|
+
**Sibling-site sweep**: when the diff adds or changes a guard, check, or invariant, search the repository for other sites that need the same treatment and report any missing site. Keep that search spec-blind; do not read task artifacts or canon docs.
|
|
20
|
+
|
|
18
21
|
Report every issue you find, including ones you are uncertain about or consider low-severity. Do not filter for importance or confidence here — the foreman does that downstream. Tag each finding with both a confidence and a severity so the foreman can rank and filter. Coverage is your job; filtering is not.
|
|
19
22
|
|
|
20
|
-
If the visible diff is truncated and you need more context, inspect
|
|
23
|
+
If the visible diff is truncated and you need more context, inspect changed files or run a diff against the provided base ref. The sibling-site sweep may search other code files. Do not read `spec.md`, `handoff.md`, `review.md`, canon docs, task notes, or acceptance criteria.
|
|
21
24
|
|
|
22
25
|
## Return Format
|
|
23
26
|
|
|
@@ -5,6 +5,7 @@ Patterns from real production use for when the pipeline gets stuck. Use them as
|
|
|
5
5
|
## Contents
|
|
6
6
|
|
|
7
7
|
- Auto-block on `spec_review` or `code_review` (loop cap hit)
|
|
8
|
+
- Code review stopped without a verdict (reviewer-set `blocked`)
|
|
8
9
|
- Findings keep clustering in the same mechanism (try a size bump before another loop)
|
|
9
10
|
- Phase mismatch — pipeline routes to `spec` when you expected `spec_review`
|
|
10
11
|
- `--ship` refuses: wrong phase
|
|
@@ -35,6 +36,26 @@ Stop the reviewer loop once findings turn wording-only. Self-grep for flagged ph
|
|
|
35
36
|
|
|
36
37
|
**Never reset the iteration counter** to bypass the cap. Counter is durable signal of how many review rounds the task has burned — losing it hides cost from future operators.
|
|
37
38
|
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## Code review stopped without a verdict (reviewer-set `blocked`)
|
|
42
|
+
|
|
43
|
+
**Symptom:** the run exits with `✋ CODE REVIEW STOPPED — the reviewer could not reach a verdict`, and `code_review` is `blocked` with an escalation for every task in the bundle. No verdict box is checked in `review.md`.
|
|
44
|
+
|
|
45
|
+
**Cause:** one of the Claude review lenses returned nothing usable twice (absent, truncated, or not in its return format), so the foreman stopped rather than approve on partial evidence or invent findings. The pipeline deliberately does not retry this stop.
|
|
46
|
+
|
|
47
|
+
**Fix:**
|
|
48
|
+
1. Read `tasks/<id>/review.md` — it names the missing lens and what came back. Look for the cause: a budget cap hit, a timeout, or a harness error in the run log. Raise `CLAUDE_BUDGET` if the lens ran out of budget.
|
|
49
|
+
2. Reset every task in the bundle, then run the whole bundle again. The stop's banner prints these exact commands:
|
|
50
|
+
```bash
|
|
51
|
+
canon task reset-code-review <task-id-1>
|
|
52
|
+
canon task reset-code-review <task-id-2>
|
|
53
|
+
canon run <task-id-1> <task-id-2>
|
|
54
|
+
```
|
|
55
|
+
Run all bundle members together. A `canon run` with only one ID reviews the shared branch against that one task's spec and Affected Files, so the siblings' changes look out of scope.
|
|
56
|
+
|
|
57
|
+
`reset-code-review` archives the stopped `review.md` and starts a fresh review pass. Re-running without the reset also starts a fresh pass, but without archiving the stopped review. Don't hand-check a verdict box or set a verdict with `canon task phase` to get past the stop: the review that would justify it never happened.
|
|
58
|
+
|
|
38
59
|
## Findings keep clustering in the same mechanism (try a size bump before another loop)
|
|
39
60
|
|
|
40
61
|
Most multi-round tasks are healthy — each round closes findings scattered across different, previously-untouched parts of the diff. That's normal iterative discovery, not a sizing problem; raising the loop cap (above) is the right tool for it.
|
|
@@ -165,7 +165,16 @@ Self-check before presenting:
|
|
|
165
165
|
### Phase 6 — After spec approval
|
|
166
166
|
|
|
167
167
|
**XS tasks:**
|
|
168
|
-
1. Write `tasks/TASK-ID/plan.md` using `.canon/templates/plan.md` as structure.
|
|
168
|
+
1. Write `tasks/TASK-ID/plan.md` using `.canon/templates/plan.md` as structure. Before writing steps, run the same feasibility check the pipeline's planner runs, and record the results in a few lines in the plan's Approach section. Mark an item that doesn't apply `N/A` in a word:
|
|
169
|
+
- **Exists:** the functions, files, and patterns the plan relies on exist — found by searching the code, not recalled.
|
|
170
|
+
- **Real path:** the actual runtime call path the change sits on, including any existing code on it that already does part of the work.
|
|
171
|
+
- **Callers:** every caller of each function whose behavior or error contract changes, found and accounted for.
|
|
172
|
+
- **Tests that can fail:** for each regression test the plan prescribes, the input or state that sends it through the changed path, so it fails without the change.
|
|
173
|
+
- **Predicates:** for each condition or state check the plan writes out, its boundary values (for example zero, empty, or non-numeric) and every state it must handle.
|
|
174
|
+
- **Scope:** every file the steps change is inside the spec's Affected Files.
|
|
175
|
+
- **Async/stateful:** for async or stateful changes — re-entry, cancellation or unmount, stale state, and ownership.
|
|
176
|
+
|
|
177
|
+
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.
|
|
169
178
|
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.
|
|
170
179
|
3. Advance the phases with the helpers (they rederive the top-level `status` pointer):
|
|
171
180
|
```bash
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: canon-spec-review
|
|
3
3
|
description: Use when a canon task spec is written and the human wants to surface BLOCKING issues before invoking `canon run <id>`. Triggers on "/canon-spec-review", "review the spec", "pre-flight the spec", "what would Codex catch", or before kicking off the pipeline. Useful for any spec with logic — for full-tier (S/M/L/XL/delicate) it pre-empts Codex spec_review iterations, for fast-tier (XS non-delicate) it's the only automated review layer since Codex spec_review auto-approves.
|
|
4
|
-
argument-hint: "
|
|
5
|
-
allowed-tools: Read Glob Grep Bash(canon task list*) Bash(git status*) Agent
|
|
4
|
+
argument-hint: "[task-id]"
|
|
5
|
+
allowed-tools: Read Glob Grep Bash(canon task list*) Bash(git status*) Agent AskUserQuestion
|
|
6
6
|
effort: high
|
|
7
7
|
---
|
|
8
8
|
|
|
@@ -23,7 +23,11 @@ Don't use for already-committed code review (use `codex review --commit <SHA>` o
|
|
|
23
23
|
|
|
24
24
|
### 1. Verify inputs
|
|
25
25
|
|
|
26
|
-
|
|
26
|
+
Resolve the task ID:
|
|
27
|
+
|
|
28
|
+
- **`$ARGUMENTS` names a task** → use it. This is how to review a task other than the one under discussion, or to run from a fresh session.
|
|
29
|
+
- **`$ARGUMENTS` is empty** → take the task from the conversation: the one just authored with `/canon-spec`, or the one the human has been discussing. Don't re-confirm an unambiguous pick.
|
|
30
|
+
- **Still unclear** (fresh session, or several tasks in play) → run `canon task list`, narrow to tasks at `spec` or `spec_review`, plus tasks at `plan` whose `status.json` shows `phases.plan.status: "pending"` (full tier waits at the human spec gate in that state). Use `AskUserQuestion` to pick one. If none match, say so rather than offering tasks already past the gate. Don't guess.
|
|
27
31
|
|
|
28
32
|
Verify `tasks/<id>/spec.md` exists and is filled out (no `<placeholder>` text or "TBD" stubs). If it's still a template, stop and say so.
|
|
29
33
|
|
|
@@ -168,8 +168,8 @@ Claude writes QA summary → Human tests
|
|
|
168
168
|
|
|
169
169
|
- Spec and plan are written in separate Claude sessions.
|
|
170
170
|
- 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.
|
|
171
|
-
- Codex model/effort
|
|
172
|
-
- **Optional pre-pipeline self-review**: before invoking `canon run`, the operator can run `/canon-spec-review
|
|
171
|
+
- Effective size selects both the Codex and Claude model/effort cells in the matrices below.
|
|
172
|
+
- **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.
|
|
173
173
|
|
|
174
174
|
**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.
|
|
175
175
|
|
|
@@ -183,8 +183,8 @@ Set in `status.json` at task creation:
|
|
|
183
183
|
|
|
184
184
|
| Field | Values | Purpose |
|
|
185
185
|
|---|---|---|
|
|
186
|
-
| `task_size` | `XS \| S \| M \| L \| XL` | Drives
|
|
187
|
-
| `delicate` | `true \| false` | Forces the XL bucket (full Codex model
|
|
186
|
+
| `task_size` | `XS \| S \| M \| L \| XL` | Drives the pipeline tier and effective-size model/effort cells for Codex and Claude. XS is fast-tier; S+ runs the full pipeline (including Codex spec review). |
|
|
187
|
+
| `delicate` | `true \| false` | Forces the XL bucket (full Codex model and high Claude spec/plan/code-review effort) 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. |
|
|
188
188
|
| `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`). The orchestrator flips it to `false` *at the moment it halts* — so `false` means "the gate already fired (or was pre-cleared)," not "review was skipped." See [Spec gate is a single-use latch](#spec-gate-is-a-single-use-latch). |
|
|
189
189
|
| `worktree` | `true \| false` | Worktree isolation. `canon task new` scaffolds this to `true`, so worktree mode is the effective default for every scaffolded task — set it to `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. |
|
|
190
190
|
| `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. |
|
|
@@ -223,7 +223,20 @@ Codex is tuned for token efficiency — the mini model handles most phases; the
|
|
|
223
223
|
|
|
224
224
|
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.
|
|
225
225
|
|
|
226
|
-
`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
|
|
226
|
+
`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.
|
|
227
|
+
|
|
228
|
+
## Claude Model/Effort Matrix
|
|
229
|
+
|
|
230
|
+
Claude model and effort scale with effective task size. The light tier defaults to `sonnet`, and the strong tier defaults to `opus`:
|
|
231
|
+
|
|
232
|
+
| Phase | XS | S | M | L | XL / delicate |
|
|
233
|
+
|---|---|---|---|---|---|
|
|
234
|
+
| `spec` | sonnet / medium | sonnet / medium | opus / medium | opus / medium | opus / high |
|
|
235
|
+
| `plan` | sonnet / medium | sonnet / medium | opus / medium | opus / medium | opus / high |
|
|
236
|
+
| `code_review` | sonnet / medium | sonnet / medium | opus / medium | opus / medium | opus / high |
|
|
237
|
+
| `qa` | sonnet / medium | sonnet / medium | sonnet / medium | sonnet / medium | sonnet / medium |
|
|
238
|
+
|
|
239
|
+
`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`.
|
|
227
240
|
|
|
228
241
|
## Claude Budget Matrix
|
|
229
242
|
|
|
@@ -238,15 +251,18 @@ Claude phase budgets scale with task size. `spec`/`plan`/`qa` are single-pass Cl
|
|
|
238
251
|
|
|
239
252
|
## Environment Variables
|
|
240
253
|
|
|
241
|
-
Claude
|
|
254
|
+
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.
|
|
242
255
|
|
|
243
256
|
| Variable | Default | Purpose |
|
|
244
257
|
|---|---|---|
|
|
245
|
-
| `
|
|
246
|
-
| `
|
|
247
|
-
| `
|
|
248
|
-
| `
|
|
249
|
-
| `
|
|
258
|
+
| `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. |
|
|
259
|
+
| `CLAUDE_MODEL_STRONG` | `opus` | Model for strong-tier cells: M and above `spec`/`plan`/`code_review`, unless a phase pin applies. |
|
|
260
|
+
| `CLAUDE_MODEL_SPEC` | _(unset)_ | Pins the `spec` model at every size, replacing the model only; effort still comes from the matrix. |
|
|
261
|
+
| `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. |
|
|
262
|
+
| `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. |
|
|
263
|
+
| `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. |
|
|
264
|
+
| `CLAUDE_MODEL_QA` | _(unset)_ | Pins the `qa` model at every size, replacing the model only; effort still comes from the matrix. |
|
|
265
|
+
| `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. |
|
|
250
266
|
| `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). |
|
|
251
267
|
| `CANON_PROJECT_NAME` | _(reads `package.json` "name" or "your project")_ | Name injected into agent prompts. |
|
|
252
268
|
| `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`. |
|
|
@@ -328,7 +344,9 @@ Non-managed Affected Files entries (source files, test files, fixtures) do not e
|
|
|
328
344
|
|
|
329
345
|
When a managed doc is committed via the Affected Files allow-list, an advisory warning fires per file inviting the operator to `git diff HEAD -- <path>` to verify the content before `--ship` (residual guard against same-file sibling-pipeline overlap).
|
|
330
346
|
|
|
331
|
-
Before that dirty-tree auto-commit path runs, the orchestrator also runs remote-boundary gates. First, the base-divergence check fetches `origin/<base>` and lists commits on local `<base>` that are not yet on `origin/<base>`. Those commits block `--push`, `--pr`, and `--ship` because they will collide when the base is pulled; bypass only this commit-divergence gate with `--allow-divergent-base`. Second, for `--pr` and `--push`, the base-drift check fetches `origin/<base>` and compares `origin/<base>` to `HEAD` with a two-dot tree diff (`git diff origin/<base> HEAD --name-status -M -z`). Any diff path outside the spec's `### Affected Files`, `tasks/<id>/`, `PIPELINE_TELEMETRY_FILES`, and — once `qa.status === 'done'` — any `PIPELINE_MANAGED_DOCS` entry is drift and aborts the run. This catches cross-pipeline contamination Mode 1 (the base branch advanced while the task branch was running) and wider Mode 2 (foreign content was already committed to the task branch before `--pr`). It is complementary to the dirty-tree gate: the dirty-tree gate stops bad content from being committed at `human_review`, while base-drift stops already-committed branch content from being pushed or PR'd.
|
|
347
|
+
Before that dirty-tree auto-commit path runs, the orchestrator also runs remote-boundary gates. First, the base-divergence check fetches `origin/<base>` and lists commits on local `<base>` that are not yet on `origin/<base>`. Those commits block `--push`, `--pr`, and `--ship` because they will collide when the base is pulled; bypass only this commit-divergence gate with `--allow-divergent-base`. Second, for `--pr` and `--push`, the base-drift check fetches `origin/<base>` and compares `origin/<base>` to `HEAD` with a two-dot tree diff (`git diff origin/<base> HEAD --name-status -M -z`). Any diff path outside the spec's `### Affected Files`, `tasks/<id>/`, `PIPELINE_TELEMETRY_FILES`, and — once `qa.status === 'done'` — any `PIPELINE_MANAGED_DOCS` entry is drift and aborts the run. The abort message distinguishes files changed only by base advancement (merge or rebase the base into the task branch) from task-changed files outside Affected Files. Both lists appear when both causes exist. This catches cross-pipeline contamination Mode 1 (the base branch advanced while the task branch was running) and wider Mode 2 (foreign content was already committed to the task branch before `--pr`). It is complementary to the dirty-tree gate: the dirty-tree gate stops bad content from being committed at `human_review`, while base-drift stops already-committed branch content from being pushed or PR'd.
|
|
348
|
+
|
|
349
|
+
At `code_review` entry, a pre-flight checks the task's own rename-aware three-dot diff against the same Affected Files allowlist, always admitting pipeline-managed docs that QA may update. In a normal run, an out-of-scope file blocks before either review agent runs and does not consume review-loop budget. Add the file to the affected task spec's `### Affected Files` table in the active checkout, then rerun `canon run <id>` (pass every bundle ID for a bundle); or reroute with a note to remove the file. In full-send mode, when every bundle member has `full_send: true`, the foreman judges each file as a scope expansion, spec miss, or change to remove. The first two outcomes require an `## Amendment (full-send scope)` section with an Affected Files row and reason (that heading feeds the Affected Files allowlist but never satisfies the human reroute-amendment gate); the last requires `changes_requested`. The foreman records each judgment in `review.md`, and an unjudged file auto-blocks before QA, including when a review finishes during recovery. Amend the spec and rerun `canon run <id>` to resume an unjudged-file block.
|
|
332
350
|
|
|
333
351
|
For renames, both the old path and the new path must appear in the spec's `### Affected Files` table. The two-dot diff uses rename detection and surfaces both sides; listing only one side leaves the other as drift. `--force` bypasses detected drift with a loud warning after the operator has verified it, but `--force` does not bypass diff-computation failure. `--force` and `--allow-divergent-base` are independent bypasses for distinct gates; pass both only when both the file-allow-list drift and the commit-divergence are intentional.
|
|
334
352
|
|
|
@@ -359,6 +377,7 @@ After `spec_review` or `code_review`, the orchestrator checks the verdict.
|
|
|
359
377
|
| `spec_review` | `changes_requested` | Loop back to `spec` automatically, except reroute-amendment reviews block for human revision. |
|
|
360
378
|
| `code_review` | `changes_requested` / `needs_re_review` | Loop back to `implement` automatically. |
|
|
361
379
|
| `code_review` | `spec_gap` | Block the whole `code_review` bundle with an escalation. Fix path: amend the `spec_gap` task specs and run `canon run <ids> --reroute`. Bless path: `canon task accept <ids> code_review --reason "<why>"`. |
|
|
380
|
+
| `code_review` | reviewer-set `blocked` (no verdict) | Block the whole `code_review` bundle with an escalation and exit `2`; no evidence recovery or retry is attempted. The stop names each task's `review.md`. Recovery: `canon task reset-code-review <id>` for each task, then `canon run <ids>`. |
|
|
362
381
|
| `spec_review` / `code_review` | `approved` / `approved_with_nits` / `sanctioned` | Continue to the next phase. `sanctioned` is status-only and written by `canon task accept`, not by review artifacts; `canon task accept` refuses a review phase with no recorded verdict unless `--force` is passed. |
|
|
363
382
|
|
|
364
383
|
**Auto-block on runaway loops**: When spec review or code review reaches or exceeds the size-aware cap (3 for XS/S/M, 5 for L/XL, or `MAX_REVIEW_LOOPS` if set), the orchestrator auto-blocks that review phase and appends an entry to `escalations` in `status.json`. The primary check runs at the next revision phase's entry, before a spec revision or re-implementation starts; the review phase retains the same check as a defense-in-depth backstop for unexpected state. For code review the cap check is the **sum** of `iterations_current_loop` and `preflight_rejections_current_loop` — handoffs bounced by the deterministic pre-flight gate count toward the same budget, and recovery requires resetting both counters (the block message says which). Separately, a pre-flight where every blocker is a `blocked` validation row auto-blocks immediately without consuming loop budget. On approval, `iterations_current_loop` resets to `0` while `iterations_total` (lifetime verdict count) and `auto_block_count` are preserved — history is never erased.
|
|
@@ -412,15 +431,17 @@ PIPELINE_STALL_TIMEOUT_MS=1800000 canon run <id>
|
|
|
412
431
|
|
|
413
432
|
## Code Review Diff Injection
|
|
414
433
|
|
|
415
|
-
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 <
|
|
434
|
+
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.
|
|
435
|
+
|
|
436
|
+
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. Every successful cold-Codex invocation adds `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.
|
|
416
437
|
|
|
417
438
|
If the cold-Codex review cannot be obtained (no captured findings output, spawn error, stall, or signal), `code_review` stops before any Claude session. Re-run `canon run <id>` when Codex is available; there is no two-Claude-lens fallback. Successful runs emit one duration line in the run log: `→ cold-codex review (<taskIds>): <n>s`.
|
|
418
439
|
|
|
419
|
-
After that, the
|
|
440
|
+
After that, the foreman receives the cold-Codex findings as the pre-obtained third lens input. On a full round, the prompt includes `git diff <baseBranch>...HEAD` from the active worktree, capped at 50,000 bytes. On a delta round, it includes `<prevSHA>..HEAD` for cold-Claude and explicitly tells anchored Claude to retrieve the full task diff by command. If the delta diff cannot be injected, the foreman is told to retrieve it by command before spawning cold-Claude. Anchored Claude also receives spec/handoff/prior review context; cold-Claude remains spec-blind. The prompt states the scope, base, and reason and tells the foreman to record them in `review.md`.
|
|
420
441
|
|
|
421
442
|
## Per-Iteration Prompt Slimming
|
|
422
443
|
|
|
423
|
-
Round 2+ of code review and implement do not re-inject the full task framing. Resumed sessions already have spec/plan/repo conventions in context
|
|
444
|
+
Round 2+ of code review and implement do not re-inject the full task framing. Resumed sessions already have spec/plan/repo conventions in context, and round-1 findings are durable in the artifacts. Code review selects a full or delta scope as described above, then re-obtains cold-Codex findings and re-runs the Claude lenses from scratch before the foreman synthesizes the new verdict.
|
|
424
445
|
|
|
425
446
|
**Cumulative artifacts.** `handoff.md` and `review.md` grow by section per round:
|
|
426
447
|
- Round 1 fills the existing template structure.
|
|
@@ -429,23 +450,7 @@ Round 2+ of code review and implement do not re-inject the full task framing. Re
|
|
|
429
450
|
|
|
430
451
|
The append-don't-rewrite convention is enforced both in the prompts and via a comment block at the bottom of each template showing the expected shape.
|
|
431
452
|
|
|
432
|
-
**
|
|
433
|
-
|
|
434
|
-
```
|
|
435
|
-
[REVIEW ROUND N — verifying iteration N-1's response to round N-1 findings]
|
|
436
|
-
|
|
437
|
-
Codex appended `## Iteration N-1` to handoff.md addressing your prior round's findings.
|
|
438
|
-
[Resumed session: framing in context. Cold start: re-read spec.md and earlier review.md sections.]
|
|
439
|
-
|
|
440
|
-
Tasks to re-review: <one-line per task pointing at the specific section>
|
|
441
|
-
|
|
442
|
-
For each task:
|
|
443
|
-
1. Read `## Iteration N-1` of handoff.md
|
|
444
|
-
2. Read git diff since prior review
|
|
445
|
-
3. Re-fill the Stage 1 AC table with every AC from spec.md against the latest code
|
|
446
|
-
4. Verify each prior finding addressed, cross-referencing the refreshed AC table; flag NEW issues
|
|
447
|
-
5. APPEND `## Round N` to review.md
|
|
448
|
-
```
|
|
453
|
+
**Resumed-session prompts.** The code-review foreman prompt renders the round number and prior handoff iteration, then asks the foreman to spawn both Claude lenses and synthesize their findings with newly obtained cold-Codex findings. Delta rounds inject only the capped fix diff for cold-Claude and direct anchored Claude to retrieve the full task diff by command. Full rounds inject the capped full task diff as before. The foreman re-fills the AC table and appends `## Round N` to `review.md`; implement revisions point at the prior round's findings.
|
|
449
454
|
|
|
450
455
|
The Stage 1 AC table is redone on round 2+. Earlier AC tables were snapshots of earlier code states; a revision can fix a cited finding while regressing an unrelated AC. Every AC appears every round. ACs whose relevant code paths did not change may be marked `Met (unchanged from round N-1)` with a one-line evidence pointer rather than re-derived evidence. Implement-revision prompts are composable: code-review reroutes point at the new `## Round N-1` of `review.md`.
|
|
451
456
|
|
|
@@ -483,7 +483,7 @@ function stripLineCitation(target) {
|
|
|
483
483
|
|
|
484
484
|
// `templates/` markdown is intentionally scanned — those files ship to
|
|
485
485
|
// adopters via `canon upgrade`, so broken refs there would propagate
|
|
486
|
-
// silently.
|
|
486
|
+
// silently. Exempt classes, each by named purpose (not by filename
|
|
487
487
|
// suffix alone):
|
|
488
488
|
//
|
|
489
489
|
// 1. `docs/BACKLOG.md` — deliberate forward-refs to unbuilt work.
|
|
@@ -517,7 +517,8 @@ function isNoisySourceFile(relPath, skipPaths = []) {
|
|
|
517
517
|
return (
|
|
518
518
|
relPath === 'docs/BACKLOG.md' ||
|
|
519
519
|
/(?:^|\/)templates\/(?:.*\/)?(spec|plan|notes|spec-review)\.md$/.test(relPath) ||
|
|
520
|
-
/^tasks\/[^/]+\/(spec|plan|notes|spec-review)\.md$/.test(relPath)
|
|
520
|
+
/^tasks\/[^/]+\/(spec|plan|notes|spec-review)\.md$/.test(relPath) ||
|
|
521
|
+
/^tasks\/[^/]+\/review-cold-codex-run-\d+\.md$/.test(relPath)
|
|
521
522
|
);
|
|
522
523
|
}
|
|
523
524
|
|
|
@@ -769,7 +770,9 @@ function findBrokenRefs(repoRoot, options = {}) {
|
|
|
769
770
|
if (gitIgnoredTargets.has(target)) continue;
|
|
770
771
|
|
|
771
772
|
const targetPath = resolveRepoRelative(repoRoot, target);
|
|
772
|
-
|
|
773
|
+
const targetExists = fs.existsSync(targetPath)
|
|
774
|
+
&& (fs.statSync(targetPath).isFile() || fs.statSync(targetPath).isDirectory());
|
|
775
|
+
if (!targetExists) {
|
|
773
776
|
addFinding(sourceFile, lineNumber, refText, 'missing file');
|
|
774
777
|
}
|
|
775
778
|
}
|