canon-ai 3.5.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 +11 -0
- package/dist/cli/index.js +7 -6
- package/dist/orchestrator/run-task.js +94 -32
- package/package.json +1 -1
- package/templates/.canon/templates/spec.md +2 -0
- package/templates/.claude/agents/spec-review-lens.md +33 -0
- package/templates/.claude/skills/canon-spec/SKILL.md +2 -0
- package/templates/.claude/skills/canon-spec-review/SKILL.md +31 -43
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,17 @@
|
|
|
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
|
+
|
|
7
18
|
## [3.5.0] — 2026-09-29
|
|
8
19
|
|
|
9
20
|
### Added
|
package/dist/cli/index.js
CHANGED
|
@@ -1224,7 +1224,7 @@ function checkTemplates(cwd) {
|
|
|
1224
1224
|
}
|
|
1225
1225
|
function checkCanonVersion(cwd) {
|
|
1226
1226
|
const versionPath = join(cwd, ".canon", "version");
|
|
1227
|
-
const installedVersion = "3.
|
|
1227
|
+
const installedVersion = "3.6.0";
|
|
1228
1228
|
if (!existsSync(versionPath)) {
|
|
1229
1229
|
return { label: ".canon/version", status: "warn", detail: "missing \u2014 run `canon upgrade`" };
|
|
1230
1230
|
}
|
|
@@ -1721,7 +1721,7 @@ function initCmd(_args) {
|
|
|
1721
1721
|
}
|
|
1722
1722
|
function writeCanonVersion(cwd) {
|
|
1723
1723
|
const versionPath = join2(cwd, ".canon", "version");
|
|
1724
|
-
const version = "3.
|
|
1724
|
+
const version = "3.6.0";
|
|
1725
1725
|
mkdirSync(dirname2(versionPath), { recursive: true });
|
|
1726
1726
|
writeFileSync(versionPath, version + "\n");
|
|
1727
1727
|
}
|
|
@@ -2731,7 +2731,7 @@ function isInstalledSourcePath(sourcePath) {
|
|
|
2731
2731
|
return sourcePath.includes("/node_modules/") || sourcePath.includes("\\node_modules\\") || sourcePath.includes("/_npx/") || sourcePath.includes("\\_npx\\");
|
|
2732
2732
|
}
|
|
2733
2733
|
function resolveCanonVersion(explicit) {
|
|
2734
|
-
return explicit ?? "3.
|
|
2734
|
+
return explicit ?? "3.6.0" ?? "dev";
|
|
2735
2735
|
}
|
|
2736
2736
|
function resolveOrchestratorCommit(repoRoot, upstreamCommit, runGitAt) {
|
|
2737
2737
|
const ownToplevel = captureGitOutput(repoRoot, ["rev-parse", "--show-toplevel"], runGitAt);
|
|
@@ -4857,7 +4857,7 @@ function currentPinFromManifest(manifest) {
|
|
|
4857
4857
|
return "unknown";
|
|
4858
4858
|
}
|
|
4859
4859
|
function bakedVersion() {
|
|
4860
|
-
return "3.
|
|
4860
|
+
return "3.6.0";
|
|
4861
4861
|
}
|
|
4862
4862
|
function ownPackageName(pkgDir) {
|
|
4863
4863
|
try {
|
|
@@ -5073,6 +5073,7 @@ var CANON_OWNED = [
|
|
|
5073
5073
|
".claude/skills/canon-sweep/SKILL.md",
|
|
5074
5074
|
".claude/agents/code-review-anchored.md",
|
|
5075
5075
|
".claude/agents/code-review-cold.md",
|
|
5076
|
+
".claude/agents/spec-review-lens.md",
|
|
5076
5077
|
".canon/templates/status.json",
|
|
5077
5078
|
".canon/templates/spec.md",
|
|
5078
5079
|
".canon/templates/plan.md",
|
|
@@ -5333,7 +5334,7 @@ function runUpgrade(cwd, pkgDir, options = {}) {
|
|
|
5333
5334
|
cutoverWarnings.push(docsRefsCheckRel);
|
|
5334
5335
|
}
|
|
5335
5336
|
const versionPath = join5(cwd, ".canon", "version");
|
|
5336
|
-
const newVersion = "3.
|
|
5337
|
+
const newVersion = "3.6.0";
|
|
5337
5338
|
const currentVersion = existsSync5(versionPath) ? readFileSync4(versionPath, "utf8").trim() : null;
|
|
5338
5339
|
if (currentVersion !== newVersion) {
|
|
5339
5340
|
pending.push({ rel: ".canon/version", projectPath: versionPath, content: newVersion + "\n" });
|
|
@@ -5641,7 +5642,7 @@ Global:
|
|
|
5641
5642
|
`);
|
|
5642
5643
|
}
|
|
5643
5644
|
function printVersion() {
|
|
5644
|
-
console.log("3.
|
|
5645
|
+
console.log("3.6.0");
|
|
5645
5646
|
}
|
|
5646
5647
|
switch (command) {
|
|
5647
5648
|
case "doctor":
|
|
@@ -1586,7 +1586,9 @@ function parseNameStatusOutput(raw) {
|
|
|
1586
1586
|
return [...paths].sort();
|
|
1587
1587
|
}
|
|
1588
1588
|
function getAffectedFiles(baseRef, cwd) {
|
|
1589
|
-
|
|
1589
|
+
const result = gitSafeAtRaw(cwd, "diff", `${baseRef}...HEAD`, "--name-status", "-M", "-z");
|
|
1590
|
+
if (!result.ok) return { ok: false, stderr: result.stderr };
|
|
1591
|
+
return { ok: true, files: parseNameStatusOutput(result.stdout) };
|
|
1590
1592
|
}
|
|
1591
1593
|
function getPathsInRange(rangeExpr, cwd) {
|
|
1592
1594
|
const result = gitSafeAtRaw(cwd, "diff", rangeExpr, "--name-status", "-M", "-z");
|
|
@@ -4756,16 +4758,16 @@ var plan_default = "You are writing implementation plans for {{taskScope}} for {
|
|
|
4756
4758
|
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 {{^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\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 docs/lessons-learned.md. 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- 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 from status.json plus this section.\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 reject at human_review and force a re-implement? Do not infer this from any reroute counter; if this is a fresh QA pass with no human_review rejection 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**: scan the five protected docs (`docs/architecture.md`, `docs/codebase-map.md`, `docs/patterns.md`, `docs/product-context.md`, `docs/decisions.md`) 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. 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- **Buffer signal** (not an action): after appending, if docs/lessons-learned.md now holds more than ~15 entries, add one line to this task\'s done.md \u2014 `Maintenance: lessons-learned.md has 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';
|
|
4757
4759
|
|
|
4758
4760
|
// src/orchestrator/prompts/templates/spec.md
|
|
4759
|
-
var spec_default = '{{{header}}}\n\n{{{startup}}}\n\n{{{instructions}}}\n{{{bundleNote}}}\n{{#doneNote}}\nNote: {{{doneNote}}}{{/doneNote}}\n\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- **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';
|
|
4761
|
+
var spec_default = '{{{header}}}\n\n{{{startup}}}\n\n{{{instructions}}}\n{{{bundleNote}}}\n{{#doneNote}}\nNote: {{{doneNote}}}{{/doneNote}}\n\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';
|
|
4760
4762
|
|
|
4761
4763
|
// src/orchestrator/prompts/templates/spec-revision.md
|
|
4762
|
-
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**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- **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';
|
|
4764
|
+
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**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';
|
|
4763
4765
|
|
|
4764
4766
|
// src/orchestrator/prompts/templates/spec-review-reroute.md
|
|
4765
4767
|
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 a completed implementation round. 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";
|
|
4766
4768
|
|
|
4767
4769
|
// src/orchestrator/prompts/templates/spec-review.md
|
|
4768
|
-
var spec_review_default = "You are reviewing {{taskScope}} for {{projectName}}.\n\n{{{startup}}}\n\nTasks to review:\n{{{taskLines}}}\n\n{{#fullSendActive}}\n**Full-send mode active**: The human grilled Claude to resolve the decision tree but did not read this spec before pipeline execution, so your review is the primary rigor layer before implementation. The silence default, scope boundary, and verdict rules this prompt sets out still apply \u2014 don't manufacture findings. What full-send changes is *where you look*, not how much you flag: give extra attention to (1) missed cases the spec's ACs might overlook, (2) scope drift between the Decision section and the ACs, and (3) ambiguity in AC verification steps, since no human will catch a gap there before implementation.\n{{/fullSendActive}}\nGrounding rule: if a finding depends on code, a symbol, or a validation result, verify the current file or diff before you claim it exists. If you did not re-open it, do not infer it from memory.\n\n**Your objective: catch genuine blocking problems, precisely.** Read the spec as the implementer would: what would break, be ambiguous, or be missing? A spec with no blocking findings is a valid, expected result \u2014 approving a clean spec is not a shortfall in your review.\n\n**First, a strategic read of the spec itself \u2014 shape before implementability.** Ask:\n- Is the problem real? (Would doing nothing be fine? Is this a symptom of something else?)\n- For a bug or flake fix: does the stated evidence actually establish the targeted mechanism, at the rung its class requires? A deterministic mechanism (fixed inputs hit the same wrong branch every run) may be confirmed by a trace with the verified trigger values; a runtime-dependent mechanism (race, timing, environment/config interaction) requires executed confirmation \u2014 a throwaway prototype-fix spike that makes the symptom vanish, or a deterministic forced repro (fault injection, forced race, targeted repro). A paper argument can rule out a wrong hypothesis but doesn't by itself verify a runtime-dependent cause. Blocking Shape Check concerns: no confirmation evidence at all, evidence below the mechanism class's required rung, or a missing red-first regression-test AC without the environment-bound-and-impractical escape. Each role owns a checkpoint: the spec author states the confirmed mechanism and its evidence in *Problem*; the reviewer (Codex) 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.\n- Is the framing right? (Does the spec solve the stated problem, or one adjacent to it?)\n- Is there a materially simpler solution that changes the shape of the work?\n- Is the AC decomposition right? (Compound ACs, missing ACs, ACs solving symptoms not causes?)\n\n**Silence is the default \u2014 for this whole review, Shape Check and implementability alike.** Only write a finding where something is actually off; do not manufacture one to fill a section or satisfy an obligation. A real shape concern becomes the lead reason for a `changes_requested` verdict; write it under the Shape Check section in spec-review.md. If none, leave that section as \"no concerns\" and proceed.\n\nThen for each task, apply that same default while probing implementability: Can this be implemented as written? Are ACs testable and unambiguous? Are edge cases handled? Are there type safety gaps? Are there file/interaction dependencies Claude missed? Does this conflict with existing patterns in the codebase? An empty list here is a valid result, not a gap in your review.{{#isBundle}}\nAlso probe for cross-task conflicts or missing dependencies between tasks.{{/isBundle}}\n{{#combined}}\nReview plan.md for each task as well \u2014 flag if the approach is unsound.{{/combined}}\n\n**Scope boundary.** Pre-existing behavior the task's spec *explicitly excludes and verifies as unaffected* (for example, named in Non-Goals) is out of scope for this review \u2014 a nit at most, never blocking. This carve-out does not cover: a change the spec *should* make but omitted (a required caller, parser, migration, or test surface), a transitive effect of the change, or an internal contradiction between spec sections \u2014 those remain **blocking** implementability findings even though the affected code is pre-existing. The test is not \"can I reach this code\" \u2014 it's whether the spec named it out of scope and showed it stays unaffected.\n\n**Classify every finding before deciding your verdict:**\n- **Blocking**: would cause wrong behavior, a silent bug, or make an AC unimplementable as written. Requires `changes_requested`.\n- **Non-blocking (nit)**: an implementation detail Codex can resolve by reading the codebase (prop flow, state threading, naming); a minor ambiguity with an obvious default; a question the plan phase should address. Does NOT require `changes_requested`.\n- *Example*: an under-specification whose intended value the surrounding task context makes obvious (e.g. a field name implied by an adjacent example or existing convention) is a nit for the plan phase, not Blocking.\n\n**Verdict rules:**\n- `changes_requested` \u2014 one or more blocking findings. Spec must be revised before the plan phase.\n- `approved_with_nits` \u2014 no blocking findings, but non-blocking nits worth passing forward. **Loop exits immediately.** Nits are written to spec-review.md and the plan phase picks them up.\n- `approved` \u2014 no findings worth noting.\n\n**Batch related nits.** If you have multiple non-blocking observations, include them all in one `approved_with_nits` verdict rather than raising one per round.\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\nIf you encounter surprising codebase behavior, append to tasks/<id>/notes.md (prefix: [spec_review]).\n\nFor each task, write tasks/<id>/spec-review.md using the template. Set your verdict: approved, approved_with_nits, or changes_requested.\n\nWhen done, run (one per task with actual verdict):\n{{{phaseCommands}}}\n";
|
|
4770
|
+
var spec_review_default = "You are reviewing {{taskScope}} for {{projectName}}.\n\n{{{startup}}}\n\nTasks to review:\n{{{taskLines}}}\n\n{{#fullSendActive}}\n**Full-send mode active**: The human grilled Claude to resolve the decision tree but did not read this spec before pipeline execution, so your review is the primary rigor layer before implementation. The silence default, scope boundary, and verdict rules this prompt sets out still apply \u2014 don't manufacture findings. What full-send changes is *where you look*, not how much you flag: give extra attention to (1) missed cases the spec's ACs might overlook, (2) scope drift between the Decision section and the ACs, and (3) ambiguity in AC verification steps, since no human will catch a gap there before implementation.\n{{/fullSendActive}}\nGrounding rule: if a finding depends on code, a symbol, or a validation result, verify the current file or diff before you claim it exists. If you did not re-open it, do not infer it from memory.\n\n**Your objective: catch genuine blocking problems, precisely.** Read the spec as the implementer would: what would break, be ambiguous, or be missing? A spec with no blocking findings is a valid, expected result \u2014 approving a clean spec is not a shortfall in your review.\n\n**First, a strategic read of the spec itself \u2014 shape before implementability.** Ask:\n- Is the problem real? (Would doing nothing be fine? Is this a symptom of something else?)\n- For a bug or flake fix: does the stated evidence actually establish the targeted mechanism, at the rung its class requires? A deterministic mechanism (fixed inputs hit the same wrong branch every run) may be confirmed by a trace with the verified trigger values; a runtime-dependent mechanism (race, timing, environment/config interaction) requires executed confirmation \u2014 a throwaway prototype-fix spike that makes the symptom vanish, or a deterministic forced repro (fault injection, forced race, targeted repro). A paper argument can rule out a wrong hypothesis but doesn't by itself verify a runtime-dependent cause. Blocking Shape Check concerns: no confirmation evidence at all, evidence below the mechanism class's required rung, or a missing red-first regression-test AC without the environment-bound-and-impractical escape. Each role owns a checkpoint: the spec author states the confirmed mechanism and its evidence in *Problem*; the reviewer (Codex) 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.\n- For a refactor: correctness audit \u2014 does the spec record, for each behavior it declares preserved, whether it is correct today and 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. A missing audit is a Blocking Shape Check concern (requires a changes_requested verdict), matching the bug-fix evidence bullet above.\n- Is the framing right? (Does the spec solve the stated problem, or one adjacent to it?)\n- Is there a materially simpler solution that changes the shape of the work?\n- Is the AC decomposition right? (Compound ACs, missing ACs, ACs solving symptoms not causes?)\n\n**Silence is the default \u2014 for this whole review, Shape Check and implementability alike.** Only write a finding where something is actually off; do not manufacture one to fill a section or satisfy an obligation. A real shape concern becomes the lead reason for a `changes_requested` verdict; write it under the Shape Check section in spec-review.md. If none, leave that section as \"no concerns\" and proceed.\n\nThen for each task, apply that same default while probing implementability: Can this be implemented as written? Are ACs testable and unambiguous? Are edge cases handled? Are there type safety gaps? Are there file/interaction dependencies Claude missed? Does this conflict with existing patterns in the codebase? An empty list here is a valid result, not a gap in your review.{{#isBundle}}\nAlso probe for cross-task conflicts or missing dependencies between tasks.{{/isBundle}}\n{{#combined}}\nReview plan.md for each task as well \u2014 flag if the approach is unsound.{{/combined}}\n\n**Scope boundary.** Pre-existing behavior the task's spec *explicitly excludes and verifies as unaffected* (for example, named in Non-Goals) is out of scope for this review \u2014 a nit at most, never blocking. This carve-out does not cover: a change the spec *should* make but omitted (a required caller, parser, migration, or test surface), a transitive effect of the change, or an internal contradiction between spec sections \u2014 those remain **blocking** implementability findings even though the affected code is pre-existing. The test is not \"can I reach this code\" \u2014 it's whether the spec named it out of scope and showed it stays unaffected.\n\n**Classify every finding before deciding your verdict:**\n- **Blocking**: would cause wrong behavior, a silent bug, or make an AC unimplementable as written. Requires `changes_requested`.\n- **Non-blocking (nit)**: an implementation detail Codex can resolve by reading the codebase (prop flow, state threading, naming); a minor ambiguity with an obvious default; a question the plan phase should address. Does NOT require `changes_requested`.\n- *Example*: an under-specification whose intended value the surrounding task context makes obvious (e.g. a field name implied by an adjacent example or existing convention) is a nit for the plan phase, not Blocking.\n\n**Verdict rules:**\n- `changes_requested` \u2014 one or more blocking findings. Spec must be revised before the plan phase.\n- `approved_with_nits` \u2014 no blocking findings, but non-blocking nits worth passing forward. **Loop exits immediately.** Nits are written to spec-review.md and the plan phase picks them up.\n- `approved` \u2014 no findings worth noting.\n\n**Batch related nits.** If you have multiple non-blocking observations, include them all in one `approved_with_nits` verdict rather than raising one per round.\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\nIf you encounter surprising codebase behavior, append to tasks/<id>/notes.md (prefix: [spec_review]).\n\nFor each task, write tasks/<id>/spec-review.md using the template. Set your verdict: approved, approved_with_nits, or changes_requested.\n\nWhen done, run (one per task with actual verdict):\n{{{phaseCommands}}}\n";
|
|
4769
4771
|
|
|
4770
4772
|
// src/orchestrator/prompts/index.ts
|
|
4771
4773
|
var TEMPLATES = {
|
|
@@ -4790,7 +4792,15 @@ function render3(name, view) {
|
|
|
4790
4792
|
return renderTemplate(loadTemplate(name), view);
|
|
4791
4793
|
}
|
|
4792
4794
|
function buildAffectedFilesBlock(affectedFiles, baseBranch) {
|
|
4793
|
-
if (
|
|
4795
|
+
if (affectedFiles === void 0) return "";
|
|
4796
|
+
if (affectedFiles === null) {
|
|
4797
|
+
return [
|
|
4798
|
+
"## Committed diff vs base branch",
|
|
4799
|
+
"",
|
|
4800
|
+
"The committed diff vs the base branch could not be determined (the git diff failed). Apply the full default check matrix from the spec's *Validation Required* section \u2014 run every check unconditionally; do not evaluate predicate gates against an assumed file set.",
|
|
4801
|
+
""
|
|
4802
|
+
].join("\n");
|
|
4803
|
+
}
|
|
4794
4804
|
if (affectedFiles.length === 0) {
|
|
4795
4805
|
return [
|
|
4796
4806
|
"## Committed diff vs base branch",
|
|
@@ -4858,7 +4868,8 @@ ${planFeasibilityCheck("in plan.md's Approach section")}` : ""),
|
|
|
4858
4868
|
"- Known Risks covers failure modes for the trickiest ACs",
|
|
4859
4869
|
"- Human Test Plan describes product behavior only (no code, no file names, no TypeScript)",
|
|
4860
4870
|
'- Validation Required has at least one entry checked (or explicitly "None" with a reason)',
|
|
4861
|
-
"- (Bug/flake fixes; N/A for features/refactors) *Problem* states the confirmed mechanism and how it was confirmed, with evidence matching the mechanism class (runtime-dependent mechanisms need executed confirmation), not merely a plausible cause; *Acceptance Criteria* includes a red-first regression-test AC or an explicit environment-bound and faithful-repro-impractical escape with a deterministic alternative"
|
|
4871
|
+
"- (Bug/flake fixes; N/A for features/refactors) *Problem* states the confirmed mechanism and how it was confirmed, with evidence matching the mechanism class (runtime-dependent mechanisms need executed confirmation), not merely a plausible cause; *Acceptance Criteria* includes a red-first regression-test AC or an explicit environment-bound and faithful-repro-impractical escape with a deterministic alternative",
|
|
4872
|
+
"- (Refactors; N/A for features/bug fixes) the spec records a correctness audit of each behavior declared preserved, with one outcome each: correct as-is, fixed deliberately (with its own AC), split into a separate task, or kept as a named quirk"
|
|
4862
4873
|
].filter(Boolean).join("\n"),
|
|
4863
4874
|
phaseCommands: phaseCommands(tasks.map((t) => t.taskId), "spec", "done")
|
|
4864
4875
|
});
|
|
@@ -5194,7 +5205,7 @@ function isInstalledSourcePath(sourcePath) {
|
|
|
5194
5205
|
return sourcePath.includes("/node_modules/") || sourcePath.includes("\\node_modules\\") || sourcePath.includes("/_npx/") || sourcePath.includes("\\_npx\\");
|
|
5195
5206
|
}
|
|
5196
5207
|
function resolveCanonVersion(explicit) {
|
|
5197
|
-
return explicit ?? "3.
|
|
5208
|
+
return explicit ?? "3.6.0" ?? "dev";
|
|
5198
5209
|
}
|
|
5199
5210
|
function resolveOrchestratorCommit(repoRoot, upstreamCommit, runGitAt) {
|
|
5200
5211
|
const ownToplevel = captureGitOutput(repoRoot, ["rev-parse", "--show-toplevel"], runGitAt);
|
|
@@ -5779,9 +5790,18 @@ function taskPhasePreflightRejected(id, phaseArg) {
|
|
|
5779
5790
|
|
|
5780
5791
|
// src/orchestrator/phases/code-review.ts
|
|
5781
5792
|
function findUnjudgedFullSendFilesFromData(changedFiles, allowlist, taskIds, verdicts) {
|
|
5782
|
-
if (verdicts
|
|
5793
|
+
if (isFullSendScopeCheckExempt(verdicts)) return [];
|
|
5783
5794
|
return verifyBaseDriftFromData(changedFiles, allowlist.paths, taskIds, allowlist.prefixes);
|
|
5784
5795
|
}
|
|
5796
|
+
function isFullSendScopeCheckExempt(verdicts) {
|
|
5797
|
+
return verdicts.some((verdict) => verdict === "changes_requested" || verdict === "needs_re_review" || verdict === "spec_gap");
|
|
5798
|
+
}
|
|
5799
|
+
function scopeUnverifiedReason(context, taskIds, baseBranch, stderr, verdictNote = "") {
|
|
5800
|
+
return `${context} could not verify scope for ${taskIds.join(", ")}: the committed diff against base branch '${baseBranch}' could not be read.
|
|
5801
|
+
Git error: ${stderr || "unknown error"}
|
|
5802
|
+
Check that the task's base branch exists and shares history with HEAD; resolve the git error above.${verdictNote}
|
|
5803
|
+
Then re-run \`canon run ${taskIds.join(" ")}\`.`;
|
|
5804
|
+
}
|
|
5785
5805
|
function fullSendScopeBlockReason(taskIds, files, recordedVerdicts) {
|
|
5786
5806
|
const verdictNote = recordedVerdicts.length > 0 ? ` Recorded code-review verdict(s): ${recordedVerdicts.join(", ")}; inspect the review before resuming.` : "";
|
|
5787
5807
|
return `Full-send code review left out-of-scope files unjudged: ${files.join(", ")}. Add them to a task spec's ### Affected Files table, then re-run \`canon run ${taskIds.join(" ")}\`; or reroute with a note telling the implementer to remove them.${verdictNote}`;
|
|
@@ -5974,7 +5994,14 @@ async function runCodeReviewPhase(state, interactive, resumeId, deps = defaultDe
|
|
|
5974
5994
|
autoBlockPhase(taskIds, "code_review", codeReviewCheck.count, codeReviewCheck.reason);
|
|
5975
5995
|
process.exit(2);
|
|
5976
5996
|
}
|
|
5977
|
-
const
|
|
5997
|
+
const affected = deps.getAffectedFiles(baseBranch, activeCwd);
|
|
5998
|
+
if (!affected.ok) {
|
|
5999
|
+
const reason = scopeUnverifiedReason("Code review pre-flight", taskIds, baseBranch, affected.stderr);
|
|
6000
|
+
warn(reason);
|
|
6001
|
+
autoBlockPhase(taskIds, "code_review", codeReviewCheck.count, reason);
|
|
6002
|
+
process.exit(2);
|
|
6003
|
+
}
|
|
6004
|
+
const changedFiles = new Set(affected.files);
|
|
5978
6005
|
const bundleIssues = deps.verifyHandoffAgainstDiff(taskIds, baseBranch);
|
|
5979
6006
|
const preflightFailed = [];
|
|
5980
6007
|
for (const t of tasks) {
|
|
@@ -6140,7 +6167,11 @@ async function runImplementPhase(state, interactive, resumeId, force = false) {
|
|
|
6140
6167
|
ensureBranch(taskIds, { force });
|
|
6141
6168
|
const activeCwd = getActiveCwd(taskIds);
|
|
6142
6169
|
const baseBranch = getBaseBranch(taskIds);
|
|
6143
|
-
const
|
|
6170
|
+
const affected = getAffectedFiles(baseBranch, activeCwd);
|
|
6171
|
+
if (!affected.ok) {
|
|
6172
|
+
warn(`Could not compute the committed diff vs ${baseBranch} (${affected.stderr || "unknown error"}); the full check matrix applies.`);
|
|
6173
|
+
}
|
|
6174
|
+
const affectedFiles = affected.ok ? affected.files : null;
|
|
6144
6175
|
const isRevision = shouldUseImplementRevision(tasks);
|
|
6145
6176
|
const isRerouted = tasks.some((t) => t.status.phases.implement?.rerouted === true);
|
|
6146
6177
|
const wasImplementInProgress = tasks.some((t) => t.status.phases.implement?.status === "in_progress");
|
|
@@ -7368,11 +7399,20 @@ Git error: ${baseDriftResult.diffError ?? "unknown error"}
|
|
|
7368
7399
|
This failure cannot be bypassed with --force.`
|
|
7369
7400
|
);
|
|
7370
7401
|
} else if (baseDriftResult.drift.length > 0) {
|
|
7402
|
+
const taskChanged = getAffectedFiles(`origin/${baseBranch}`, cwd);
|
|
7403
|
+
if (!taskChanged.ok) {
|
|
7404
|
+
die(
|
|
7405
|
+
`--pr aborted: base-drift detected, but the task-changed files could not be computed against origin/${baseBranch}.
|
|
7406
|
+
Drifted files:
|
|
7407
|
+
${baseDriftResult.drift.map((filePath) => ` ${filePath}`).join("\n")}
|
|
7408
|
+
Git error: ${taskChanged.stderr || "unknown error"}
|
|
7409
|
+
This failure cannot be bypassed with --force.`
|
|
7410
|
+
);
|
|
7411
|
+
}
|
|
7371
7412
|
if (!cliArgs.force) {
|
|
7372
|
-
const taskChangedFiles = new Set(getAffectedFiles(`origin/${baseBranch}`, cwd));
|
|
7373
7413
|
const classification = classifyBaseDriftFilesFromData(
|
|
7374
7414
|
baseDriftResult.drift,
|
|
7375
|
-
|
|
7415
|
+
new Set(taskChanged.files)
|
|
7376
7416
|
);
|
|
7377
7417
|
die2(buildBaseDriftAbortMessage(baseBranch, classification));
|
|
7378
7418
|
}
|
|
@@ -8741,32 +8781,47 @@ async function checkAndRoute(phase, taskIds) {
|
|
|
8741
8781
|
}
|
|
8742
8782
|
statuses = taskIds.map(readStatus);
|
|
8743
8783
|
let specGapScopeFiles = [];
|
|
8784
|
+
let specGapScopeError = null;
|
|
8744
8785
|
if (phase === "code_review" && statuses.every((status) => status.full_send === true)) {
|
|
8745
8786
|
const cwd = getActiveCwd(taskIds);
|
|
8746
8787
|
const baseBranch = getBaseBranch(taskIds);
|
|
8747
|
-
const
|
|
8748
|
-
const allowlist = buildAffectedFilesAllowlist(taskIds, { admitManagedDocs: true });
|
|
8788
|
+
const affected = getAffectedFiles(baseBranch, cwd);
|
|
8749
8789
|
const verdicts = statuses.map((status) => getVerdict(status, "code_review"));
|
|
8750
|
-
|
|
8751
|
-
|
|
8790
|
+
const maxIter = statuses.reduce((max, status) => Math.max(max, getIterations(status)), 0);
|
|
8791
|
+
if (!affected.ok) {
|
|
8792
|
+
if (verdicts.includes("spec_gap")) {
|
|
8793
|
+
specGapScopeError = affected.stderr || "unknown error";
|
|
8794
|
+
} else if (!isFullSendScopeCheckExempt(verdicts)) {
|
|
8795
|
+
const recorded = verdicts.filter(Boolean);
|
|
8796
|
+
const verdictNote = ` Recorded code-review verdict(s): ${recorded.length > 0 ? recorded.join(", ") : "none"}; inspect the review before resuming.`;
|
|
8797
|
+
const reason = scopeUnverifiedReason("Full-send code review", taskIds, baseBranch, affected.stderr, verdictNote);
|
|
8798
|
+
warn2(reason);
|
|
8799
|
+
autoBlockPhase(taskIds, "code_review", maxIter, reason);
|
|
8800
|
+
process.exit(2);
|
|
8801
|
+
}
|
|
8802
|
+
} else {
|
|
8803
|
+
const changedFiles = affected.files;
|
|
8804
|
+
const allowlist = buildAffectedFilesAllowlist(taskIds, { admitManagedDocs: true });
|
|
8805
|
+
if (verdicts.includes("spec_gap")) {
|
|
8806
|
+
specGapScopeFiles = verifyBaseDriftFromData(
|
|
8807
|
+
changedFiles,
|
|
8808
|
+
allowlist.paths,
|
|
8809
|
+
taskIds,
|
|
8810
|
+
allowlist.prefixes
|
|
8811
|
+
);
|
|
8812
|
+
}
|
|
8813
|
+
const unjudged = findUnjudgedFullSendFilesFromData(
|
|
8752
8814
|
changedFiles,
|
|
8753
|
-
allowlist
|
|
8815
|
+
allowlist,
|
|
8754
8816
|
taskIds,
|
|
8755
|
-
|
|
8817
|
+
verdicts
|
|
8756
8818
|
);
|
|
8757
|
-
|
|
8758
|
-
|
|
8759
|
-
|
|
8760
|
-
|
|
8761
|
-
|
|
8762
|
-
|
|
8763
|
-
);
|
|
8764
|
-
if (unjudged.length > 0) {
|
|
8765
|
-
const reason = fullSendScopeBlockReason(taskIds, unjudged, verdicts.filter(Boolean));
|
|
8766
|
-
warn2(reason);
|
|
8767
|
-
const maxIter = statuses.reduce((max, status) => Math.max(max, getIterations(status)), 0);
|
|
8768
|
-
autoBlockPhase(taskIds, "code_review", maxIter, reason);
|
|
8769
|
-
process.exit(2);
|
|
8819
|
+
if (unjudged.length > 0) {
|
|
8820
|
+
const reason = fullSendScopeBlockReason(taskIds, unjudged, verdicts.filter(Boolean));
|
|
8821
|
+
warn2(reason);
|
|
8822
|
+
autoBlockPhase(taskIds, "code_review", maxIter, reason);
|
|
8823
|
+
process.exit(2);
|
|
8824
|
+
}
|
|
8770
8825
|
}
|
|
8771
8826
|
}
|
|
8772
8827
|
if (lastCodexExitStatus !== 0) {
|
|
@@ -8844,7 +8899,10 @@ async function checkAndRoute(phase, taskIds) {
|
|
|
8844
8899
|
const specGapIds = taskIds.filter((_, index) => getVerdict(statuses[index], "code_review") === "spec_gap");
|
|
8845
8900
|
if (specGapIds.length > 0) {
|
|
8846
8901
|
const maxIter = statuses.reduce((max, s) => Math.max(max, getIterations(s)), 0);
|
|
8847
|
-
const scopeNote = specGapScopeFiles.length > 0 ? `
|
|
8902
|
+
const scopeNote = specGapScopeFiles.length > 0 ? `Full-send files remain outside Affected Files: ${specGapScopeFiles.join(", ")}. A BLESS accepts the verdict but does not amend the spec, so --pr will still reject these files: before blessing, add them to the spec's Affected Files (as an ## Amendment) or remove them from the branch. ` : specGapScopeError !== null ? `Full-send scope could not be verified: the committed diff against the base branch could not be read.
|
|
8903
|
+
Git error: ${specGapScopeError}
|
|
8904
|
+
Scope was not checked. Fix the base branch or repository history before choosing a recovery option below, and verify scope manually before blessing.
|
|
8905
|
+
` : "";
|
|
8848
8906
|
const reason = `Code review surfaced a spec_gap verdict for task(s): ${specGapIds.join(", ")}. The implementation cannot resolve this \u2014 the root cause is in the spec. ` + scopeNote + `Recovery options (both operate on the full blocked bundle [${taskIds.join(" ")}]):
|
|
8849
8907
|
FIX: amend spec.md with ## Amendment, then: canon run ${taskIds.join(" ")} --reroute
|
|
8850
8908
|
BLESS: canon task accept ${taskIds.join(" ")} code_review --reason "<why>"`;
|
|
@@ -8860,6 +8918,10 @@ async function checkAndRoute(phase, taskIds) {
|
|
|
8860
8918
|
for (const file of specGapScopeFiles) console.log(` ${file}`);
|
|
8861
8919
|
console.log(" BLESS does not amend the spec, so --pr will still reject these files.");
|
|
8862
8920
|
console.log(" Before blessing, add them to Affected Files (## Amendment) or remove them.");
|
|
8921
|
+
} else if (specGapScopeError !== null) {
|
|
8922
|
+
console.log(" Full-send scope could not be verified.");
|
|
8923
|
+
console.log(` Git error: ${specGapScopeError}`);
|
|
8924
|
+
console.log(" Fix the base branch or repository history before choosing FIX or BLESS; verify scope manually before blessing.");
|
|
8863
8925
|
}
|
|
8864
8926
|
console.log("");
|
|
8865
8927
|
console.log(" Two recovery options:");
|
package/package.json
CHANGED
|
@@ -22,6 +22,7 @@ What are we explicitly NOT doing in this task? This prevents scope creep.
|
|
|
22
22
|
Checklist of verifiable outcomes. Each item must be testable.
|
|
23
23
|
|
|
24
24
|
> **For a bug or flake fix:** Include a regression-test AC that fails on the pre-fix code for the stated reason and passes after the fix (red-first). If the mechanism is environment-bound and a faithful repro is impractical, the AC must say so and name the deterministic alternative rather than omitting verification.
|
|
25
|
+
> **For a refactor:** Before any "preserve behavior" AC, run a correctness audit. 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.
|
|
25
26
|
|
|
26
27
|
- [ ] AC-1: ...
|
|
27
28
|
- [ ] AC-2: ...
|
|
@@ -98,3 +99,4 @@ Steps the human should perform to verify the feature works as intended. Written
|
|
|
98
99
|
- [ ] Human Test Plan uses product language only (no code, no file names)
|
|
99
100
|
- [ ] Validation Required has at least one entry marked `- [x]` (not `- [ ]`). `- [ ]` is a placeholder; the spec author flips required checks to `- [x]` before marking spec done. The orchestrator's code_review pre-flight blocks if no `[x]` items are present.
|
|
100
101
|
- [ ] (Bug/flake fixes; N/A for features/refactors) *Problem* states the confirmed mechanism and how it was confirmed, with evidence matching the mechanism class (runtime-dependent mechanisms need executed confirmation), not merely a plausible cause; *Acceptance Criteria* includes a red-first regression-test AC or an explicit environment-bound and faithful-repro-impractical escape with a deterministic alternative
|
|
102
|
+
- [ ] (Refactors; N/A for features/bug fixes) *Problem* records the correctness audit of each behavior declared preserved, with one outcome each (correct as-is, fixed deliberately (with its own AC), split into a separate task, or kept as a named quirk) stated before any "preserve" AC
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: spec-review-lens
|
|
3
|
+
description: Read-only spec reviewer for the /canon-spec-review skill. Dispatched once per angle (shape, factual, spec-quality) with the spec inline; returns cited findings to the synthesizing session.
|
|
4
|
+
tools: Read, Grep, Glob, Bash
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
You are one lens in `/canon-spec-review`. The prompt gives you the spec text, your angle, and your rubric.
|
|
8
|
+
|
|
9
|
+
Review the spec yourself and spawn no sub-agents.
|
|
10
|
+
Invoke no skills.
|
|
11
|
+
Write no files. Run no `canon` commands and ask the user nothing.
|
|
12
|
+
Bash is read-only: search, list, print file ranges, and inspect read-only git history. Use no redirects, in-place edits, or state-changing git.
|
|
13
|
+
Inherit the session's model and effort.
|
|
14
|
+
|
|
15
|
+
Every finding cites `file:line` or an AC number. Re-open the current file before making any claim about code; do not rely on memory.
|
|
16
|
+
|
|
17
|
+
Coverage first: report every finding you can back with a citation, each tagged with a severity. Filtering is the synthesizer's job. `[NO FINDINGS]` is a valid return; do not invent findings.
|
|
18
|
+
|
|
19
|
+
**Severity definitions**
|
|
20
|
+
- **BLOCKING:** would cause wrong behavior or a silent bug, or makes an AC unimplementable as written.
|
|
21
|
+
- **NIT:** an implementation detail the implementer resolves by reading the codebase, a minor ambiguity with an obvious default, or a question the plan phase should answer.
|
|
22
|
+
- **STRONG:** nit-grade under those rules, but likely to cost a review round, so cheaper to fix now.
|
|
23
|
+
|
|
24
|
+
Behavior the spec's Non-Goals explicitly exclude and verify as unaffected is a NIT at most. An omitted required change (such as a caller, parser, migration or test surface), a transitive effect of the change, or an internal contradiction between spec sections stays BLOCKING.
|
|
25
|
+
|
|
26
|
+
Return format:
|
|
27
|
+
|
|
28
|
+
```text
|
|
29
|
+
- [BLOCKING|STRONG|NIT] <one-line finding> — <file:line or AC#>
|
|
30
|
+
<2-3 sentence rationale, citing evidence>
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Return exactly `- [NO FINDINGS]` if you find nothing. Add no preamble or closing remarks.
|
|
@@ -146,6 +146,7 @@ Self-check before presenting:
|
|
|
146
146
|
- [ ] Non-Goals rules out the most tempting scope expansions — back load-bearing exclusions with a positive scope-bound or grep AC, not prose "NOT" alone
|
|
147
147
|
- [ ] Symbols named in ACs actually exist in the codebase — grep-verify before presenting
|
|
148
148
|
- [ ] (Bug/flake fixes; N/A for features/refactors) *Problem* states the confirmed mechanism and how it was confirmed, with evidence matching the mechanism class (runtime-dependent mechanisms need executed confirmation), not merely a plausible cause; *Acceptance Criteria* includes a red-first regression-test AC or an explicit environment-bound and faithful-repro-impractical escape with a deterministic alternative
|
|
149
|
+
- [ ] (Refactors; N/A for features/bug fixes) *Problem* records the correctness audit of each behavior declared preserved, with one outcome each (correct as-is, fixed deliberately (with its own AC), split into a separate task, or kept as a named quirk) stated before any "preserve" AC
|
|
149
150
|
|
|
150
151
|
**Spec-writing rules of thumb** (apply when writing ACs and structure):
|
|
151
152
|
- **Name effects to DELETE**: frame supersession as replacement ("replace `oldFn` with `newFn`; `oldFn` must not exist after"), not separate add/remove bullets.
|
|
@@ -156,6 +157,7 @@ Self-check before presenting:
|
|
|
156
157
|
- **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
158
|
- **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
159
|
- **Refactor specs need hard structural caps**: size cap, explicit deletion expectations per symbol, grep AC for disappeared symbols.
|
|
160
|
+
- **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.
|
|
159
161
|
- **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.
|
|
160
162
|
|
|
161
163
|
**⛔ STOP — present the spec and wait for approval.**
|
|
@@ -8,16 +8,16 @@ effort: high
|
|
|
8
8
|
|
|
9
9
|
# canon-spec-review
|
|
10
10
|
|
|
11
|
-
Previews what Codex's `spec_review` phase would surface — BEFORE `canon run <id>` — by dispatching three parallel sub-agents at the spec from different angles. Returns one inline report; the human revises spec
|
|
11
|
+
Previews what Codex's `spec_review` phase would surface — BEFORE `canon run <id>` — by dispatching three parallel sub-agents at the spec from different angles. Returns one inline report; the human revises the spec or proceeds.
|
|
12
12
|
|
|
13
13
|
## When to use
|
|
14
14
|
|
|
15
15
|
- Spec is written, `human_spec_gate` not yet cleared
|
|
16
|
-
- Task is **full-tier (S/M/L/XL/delicate)** — pre-empts Codex spec_review iterations; saves
|
|
16
|
+
- Task is **full-tier (S/M/L/XL/delicate)** — pre-empts Codex spec_review iterations; saves time and cost
|
|
17
17
|
- Task is **fast-tier (XS non-delicate)** — this is the *only* automated review layer (fast-tier auto-approves `spec_review`, so without this skill the human's read is the only gate before Codex hits the spec at implement-time)
|
|
18
18
|
- After a reroute amendment, before re-invoking the pipeline
|
|
19
19
|
|
|
20
|
-
Don't use for
|
|
20
|
+
Don't use for code-diff review (use `/canon-inline-review`), or for XS changes with no logic to vet (typo fixes, version bumps).
|
|
21
21
|
|
|
22
22
|
## Workflow
|
|
23
23
|
|
|
@@ -39,65 +39,54 @@ State briefly: task ID, size, delicate flag, "dispatching 3 parallel sub-agents.
|
|
|
39
39
|
|
|
40
40
|
### 2. Dispatch 3 sub-agents in one message
|
|
41
41
|
|
|
42
|
-
Send a SINGLE message with three Agent tool calls so they run concurrently.
|
|
43
|
-
|
|
44
|
-
Each sub-agent returns findings in this format (no preamble, no closing remarks):
|
|
45
|
-
|
|
46
|
-
```
|
|
47
|
-
- [BLOCKING|STRONG|NIT] <one-line finding> — <file:line or AC#>
|
|
48
|
-
<2-3 sentence rationale, citing evidence>
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
**Calibration applied to every angle**: silence is the default. Only flag issues that would cause real problems during implementation. A real shape concern becomes the lead reason for a `changes_requested` verdict. Don't manufacture findings to look thorough.
|
|
42
|
+
Send a SINGLE message with three Agent tool calls so they run concurrently. Every call uses `subagent_type: spec-review-lens` and carries the full spec text inline, its angle, scope and rubric. Each lens returns cited findings in its own format. If the Agent tool rejects `spec-review-lens`, stop and tell the human to run `canon upgrade`, then start a new Claude Code session; do not fall back to another agent type.
|
|
52
43
|
|
|
53
44
|
#### Agent A — Structural / shape
|
|
54
45
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
Goal: answer "is this spec solving the right problem in the right shape?" Four questions: (1) is the problem real, (2) is the framing right, (3) is there a materially simpler solution, (4) is the AC decomposition right (compound ACs, missing ACs, ACs solving symptoms not causes)?
|
|
46
|
+
Scope: review whether the spec solves the right problem in the right shape. Answer four questions: is the problem real, is the framing right, is there a materially simpler solution, and is the AC decomposition right (compound ACs, missing ACs, ACs solving symptoms rather than causes)?
|
|
58
47
|
|
|
59
48
|
Constraints: don't audit factual claims (Agent B's job); don't audit completeness against canon's spec-writing rules (Agent C's job). Stay in shape territory.
|
|
60
49
|
|
|
61
|
-
Output: the format above. If no shape concerns, return exactly `- [NO FINDINGS]`.
|
|
62
|
-
|
|
63
50
|
#### Agent B — Factual / ground-truth
|
|
64
51
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
Goal: for each named symbol, file path, function, or behavior the spec mentions — does it exist? Is the description correct? Are Affected Files entries real paths? For symbols in ACs (e.g. "extend `parseFooBar` to handle X"): grep for the symbol, read the function signature, verify the spec's assumptions about return shape and call sites.
|
|
68
|
-
|
|
69
|
-
Constraints: cite `file:line` for every claim. Don't audit shape (Agent A) or stylistic completeness (Agent C).
|
|
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.
|
|
70
53
|
|
|
71
|
-
|
|
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).
|
|
72
55
|
|
|
73
|
-
This angle catches the highest-value class:
|
|
56
|
+
This angle catches the highest-value class: a spec that assumes something exists in one place when it is elsewhere, or describes its contract incorrectly.
|
|
74
57
|
|
|
75
58
|
#### Agent C — Spec-quality completeness
|
|
76
59
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
Goal: check these spec-quality rules:
|
|
60
|
+
Scope: audit against canon's spec-writing rules of thumb. Check these ten items:
|
|
80
61
|
(1) **Name effects to DELETE** — when a change supersedes prior code, is it framed as a single replacement, not separate add/remove bullets?
|
|
81
62
|
(2) **Prefer positive or structural assertions** — are load-bearing "must not" constraints backed by a grep AC or positive reframe, not just prose negation?
|
|
82
|
-
(3) **Affected Files** —
|
|
63
|
+
(3) **Affected Files** — the table sits under a heading that is exactly `### Affected Files`, inside `## Design` or any amendment section (an H2 whose first word is `Amendment`, such as `## Amendment`, `## Amendment Round N` or `## Amendment (full-send scope)`). It lists every file the change writes, including generated mirrors and rebuilt outputs. Files only read for context stay out. A mismatched heading or a missing generated output is BLOCKING, because a pipeline gate rejects it later.
|
|
83
64
|
(4) **Validation Required** — section present AND has at least one `- [x]` checked entry (or an explicit checked "None — <reason>"). A section with zero `[x]` entries is a failing check.
|
|
84
65
|
(5) **Non-goals** — rule out the most tempting scope expansions.
|
|
85
66
|
(6) **Human Test Plan** — product language only; no code, no file paths.
|
|
86
67
|
(7) **Known Risks** — names actual failure modes for the trickiest ACs.
|
|
87
|
-
(8) **Symbols in ACs exist** — for any named function or symbol, has the author
|
|
88
|
-
(9) **Bug/flake-fix evidence** (N/A for features/refactors) — *Problem* states the confirmed mechanism and how it was confirmed, with evidence matching the mechanism class: a deterministic mechanism
|
|
68
|
+
(8) **Symbols in ACs exist** — for any named function or symbol, has the author verified the return shape matches the spec's assumed data contract?
|
|
69
|
+
(9) **Bug/flake-fix evidence** (N/A for features/refactors) — *Problem* states the confirmed mechanism and how it was confirmed, with evidence matching the mechanism class: a deterministic mechanism may cite a trace with verified trigger values; a runtime-dependent mechanism needs executed confirmation (a throwaway prototype-fix spike or a deterministic forced repro). *Acceptance Criteria* includes a red-first regression-test AC or the explicit environment-bound-and-impractical escape with a named deterministic alternative. Missing or under-rung evidence, or a missing red-first AC without the escape, is BLOCKING.
|
|
70
|
+
(10) **Refactor correctness audit evidence** (N/A for features/bug fixes) — does the spec record, for each behavior it declares preserved, whether it is correct today and 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. Missing audit evidence is STRONG, never BLOCKING.
|
|
71
|
+
|
|
72
|
+
Constraints: stay structural/completeness. Don't second-guess shape (Agent A) or independently re-verify symbols against the codebase (Agent B's job). Check (8) audits whether the author verified before writing the AC.
|
|
73
|
+
|
|
74
|
+
### Severity definitions and scope boundary
|
|
89
75
|
|
|
90
|
-
|
|
76
|
+
- **BLOCKING:** would cause wrong behavior or a silent bug, or makes an AC unimplementable as written.
|
|
77
|
+
- **NIT:** an implementation detail the implementer resolves by reading the codebase, a minor ambiguity with an obvious default, or a question the plan phase should answer.
|
|
78
|
+
- **STRONG:** nit-grade under those rules, but likely to cost a review round, so cheaper to fix now.
|
|
91
79
|
|
|
92
|
-
|
|
80
|
+
Behavior the spec's Non-Goals explicitly exclude and verify as unaffected is a NIT at most. An omitted required change (such as a caller, parser, migration or test surface), a transitive effect of the change, or an internal contradiction between spec sections stays BLOCKING.
|
|
93
81
|
|
|
94
82
|
### 3. Synthesize and report
|
|
95
83
|
|
|
96
|
-
|
|
84
|
+
Silence is the default for what reaches the report. Apply these filters:
|
|
97
85
|
|
|
98
|
-
1.
|
|
99
|
-
2.
|
|
100
|
-
3.
|
|
86
|
+
1. Drop any finding with no citation (uncited).
|
|
87
|
+
2. Downgrade any finding inside the scope boundary to NIT (scope-boundary downgrade).
|
|
88
|
+
3. de-dupe overlapping findings, keeping the more specific rationale.
|
|
89
|
+
4. Re-classify on the strongest evidence, then order BLOCKING, STRONG, NIT.
|
|
101
90
|
|
|
102
91
|
Print this inline (do NOT write to a file):
|
|
103
92
|
|
|
@@ -127,22 +116,21 @@ Print this inline (do NOT write to a file):
|
|
|
127
116
|
- Worth a quick pass — STRONG items will probably surface in spec_review; cheaper to fix now
|
|
128
117
|
```
|
|
129
118
|
|
|
130
|
-
If
|
|
119
|
+
If a lens returned `[NO FINDINGS]`, say so under its section. Don't pad. If all three returned no findings, the report is two lines: header + "Proceed — three sub-agents found no issues."
|
|
131
120
|
|
|
132
121
|
## Output principles
|
|
133
122
|
|
|
134
123
|
- **Inline report only.** No file writes. No status.json updates. No spec-review.md mutations.
|
|
135
124
|
- **Operator owns the decision.** This skill surfaces findings; it doesn't revise specs or invoke the pipeline.
|
|
136
|
-
- **One example of what this catches** (from canon-ai's own worktree-canonical-task-state spec authoring): a Pass 1 multi-agent run caught a `taskDirFor → resolveTaskCwd` rename that would have produced infinite recursion at every pipeline phase entry, plus 6 other BLOCKING items. Codex's spec_review would have surfaced these across 2-3 iterations (~30 min each); the skill closed them in one ~15-min pass.
|
|
137
125
|
|
|
138
126
|
## Anti-patterns
|
|
139
127
|
|
|
140
128
|
| Anti-pattern | Why it's wrong | Do instead |
|
|
141
129
|
|---|---|---|
|
|
142
|
-
| Run on
|
|
143
|
-
| Use to revise the spec | The skill doesn't edit files | Read the report, edit spec
|
|
144
|
-
| Manufacture findings to look thorough | Dilutes signal |
|
|
145
|
-
| Use for code-diff review | Spec mode only
|
|
130
|
+
| Run on XS typo fixes or version bumps | Overhead exceeds value when there is no logic to vet | Skip when there's no logic to vet; use freely on any spec that has decisions in it, XS included |
|
|
131
|
+
| Use to revise the spec | The skill doesn't edit files | Read the report, edit the spec manually |
|
|
132
|
+
| Manufacture findings to look thorough | Dilutes signal | `[NO FINDINGS]` is a real verdict; report only cited findings |
|
|
133
|
+
| Use for code-diff review | Spec mode only | Use `/canon-inline-review` for diff review |
|
|
146
134
|
|
|
147
135
|
---
|
|
148
136
|
|