canon-ai 3.1.3 → 3.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -4,6 +4,18 @@
4
4
 
5
5
  ## [Unreleased]
6
6
 
7
+ ## [3.2.1] — 2026-09-13
8
+
9
+ ### Changed
10
+
11
+ - **The project is clearly identified as an independent AI coding tool.** The README and npm package description now distinguish canon-ai from Canon's camera, printer, and imaging businesses; canon-ai does not control or interact with Canon hardware.
12
+
13
+ ## [3.2.0] — 2026-09-11
14
+
15
+ ### Added
16
+
17
+ - **New `/canon-sweep` skill triages the lessons-learned buffer.** It walks every entry with a prune-or-keep default, prefers the smallest carrier (a site comment, a test, or a clause on an existing rule) over a new rule, and proposes a per-entry verdict table for human approval before writing anything. Installed by `canon init` / `canon upgrade` and checked by `canon doctor`.
18
+
7
19
  ## [3.1.3] — 2026-09-08
8
20
 
9
21
  ### Added
package/README.md CHANGED
@@ -6,6 +6,8 @@
6
6
  >
7
7
  > A spec-first, multi-agent coding pipeline you drop into any repo.
8
8
 
9
+ > **Affiliation notice:** Canon is an independent open-source developer tool for orchestrating AI coding agents. It is not affiliated with, endorsed by, sponsored by, or otherwise connected to Canon Inc., Canon U.S.A., or their camera, printer, imaging, or other product lines. It does not control or interact with Canon hardware.
10
+
9
11
  ---
10
12
 
11
13
  ## What this is
@@ -130,6 +132,7 @@ The other installed skills (auto-trigger on natural-language phrases — see eac
130
132
  | `/canon-spec` | Authoring a new task — "let's add X", "start a task for…" |
131
133
  | `/canon-spec-review` | Pre-flighting a spec before invoking the pipeline |
132
134
  | `/canon-inline-review` | Independent cross-review of inline or below-pipeline changes before commit or PR |
135
+ | `/canon-sweep` | Triaging `docs/lessons-learned.md` — promote, keep, or prune, human-approved |
133
136
  | `/canon-pipeline` | Driving an existing task forward (`canon run`, `--pr`, `--ship`, recovery) |
134
137
  | `/canon-status` | "Where are we?" — surfaces phases and blockers across in-flight tasks |
135
138
  | `/canon-changelog` | Drafting release notes (projects that version their releases) |
@@ -211,7 +214,9 @@ Canon drives a lot of `git`, `gh`, `codex`, and `npm` invocations, plus short sh
211
214
  "Skill(canon-spec-review)",
212
215
  "Skill(canon-spec-review:*)",
213
216
  "Skill(canon-inline-review)",
214
- "Skill(canon-inline-review:*)"
217
+ "Skill(canon-inline-review:*)",
218
+ "Skill(canon-sweep)",
219
+ "Skill(canon-sweep:*)"
215
220
  ]
216
221
  }
217
222
  }
@@ -308,7 +313,7 @@ Drop this into any repo and you have:
308
313
  - Agent rules and per-agent guidance delivered just-in-time via per-phase prompt templates (`src/orchestrator/prompts/templates/`) and the `/canon-*` skills; adopters generate their own `AGENTS.md` / `CLAUDE.md` with the built-in `/init`
309
314
  - `docs/` — knowledge corpus templates with detailed scaffolding
310
315
  - `.claude/settings.json` — Claude Code permissions + SessionStart hook
311
- - Claude Code skills installed by `canon init`: `/canon-init` (knowledge-corpus bootstrap), `/canon-spec` (new task authoring), `/canon-spec-review` (pre-flight a spec), `/canon-inline-review` (independent cross-review of below-pipeline work), `/canon-pipeline` (drive an existing task), `/canon-status` (in-flight task map), `/canon-changelog` (release notes for versioned projects)
316
+ - Claude Code skills installed by `canon init`: `/canon-init` (knowledge-corpus bootstrap), `/canon-spec` (new task authoring), `/canon-spec-review` (pre-flight a spec), `/canon-inline-review` (independent cross-review of below-pipeline work), `/canon-pipeline` (drive an existing task), `/canon-status` (in-flight task map), `/canon-changelog` (release notes for versioned projects), `/canon-sweep` (triage the lessons-learned buffer)
312
317
  - Unit-test suite covering the policy module, orchestrator extractors, and validation parsers (`npm test`)
313
318
 
314
319
  ## Where to Go Deeper
package/dist/cli/index.js CHANGED
@@ -1117,7 +1117,9 @@ var RECOMMENDED_ALLOW = [
1117
1117
  "Skill(canon-spec-review)",
1118
1118
  "Skill(canon-spec-review:*)",
1119
1119
  "Skill(canon-inline-review)",
1120
- "Skill(canon-inline-review:*)"
1120
+ "Skill(canon-inline-review:*)",
1121
+ "Skill(canon-sweep)",
1122
+ "Skill(canon-sweep:*)"
1121
1123
  ];
1122
1124
  var RECOMMENDED_NUDGE = [
1123
1125
  "This project uses canon, a spec-first multi-agent pipeline.",
@@ -1245,7 +1247,7 @@ function checkTemplates(cwd) {
1245
1247
  }
1246
1248
  function checkCanonVersion(cwd) {
1247
1249
  const versionPath = join(cwd, ".canon", "version");
1248
- const installedVersion = "3.1.3";
1250
+ const installedVersion = "3.2.1";
1249
1251
  if (!existsSync(versionPath)) {
1250
1252
  return { label: ".canon/version", status: "warn", detail: "missing \u2014 run `canon upgrade`" };
1251
1253
  }
@@ -1268,7 +1270,7 @@ function checkSkills(cwd) {
1268
1270
  detail: "canon-init skill missing \u2014 run `canon init` or `canon upgrade`"
1269
1271
  };
1270
1272
  }
1271
- const skillNames = ["canon-spec", "canon-pipeline", "canon-status", "canon-changelog", "canon-spec-review", "canon-inline-review"];
1273
+ const skillNames = ["canon-spec", "canon-pipeline", "canon-status", "canon-changelog", "canon-spec-review", "canon-inline-review", "canon-sweep"];
1272
1274
  const missing = skillNames.filter((s) => !existsSync(join(cwd, ".claude", "skills", s, "SKILL.md")));
1273
1275
  if (missing.length > 0) {
1274
1276
  return {
@@ -1742,7 +1744,7 @@ function initCmd(_args) {
1742
1744
  }
1743
1745
  function writeCanonVersion(cwd) {
1744
1746
  const versionPath = join2(cwd, ".canon", "version");
1745
- const version = "3.1.3";
1747
+ const version = "3.2.1";
1746
1748
  mkdirSync(dirname2(versionPath), { recursive: true });
1747
1749
  writeFileSync(versionPath, version + "\n");
1748
1750
  }
@@ -2751,7 +2753,7 @@ function isInstalledSourcePath(sourcePath) {
2751
2753
  return sourcePath.includes("/node_modules/") || sourcePath.includes("\\node_modules\\") || sourcePath.includes("/_npx/") || sourcePath.includes("\\_npx\\");
2752
2754
  }
2753
2755
  function resolveCanonVersion(explicit) {
2754
- return explicit ?? "3.1.3" ?? "dev";
2756
+ return explicit ?? "3.2.1" ?? "dev";
2755
2757
  }
2756
2758
  function resolveOrchestratorCommit(repoRoot, upstreamCommit, runGitAt) {
2757
2759
  const ownToplevel = captureGitOutput(repoRoot, ["rev-parse", "--show-toplevel"], runGitAt);
@@ -4877,7 +4879,7 @@ function currentPinFromManifest(manifest) {
4877
4879
  return "unknown";
4878
4880
  }
4879
4881
  function bakedVersion() {
4880
- return "3.1.3";
4882
+ return "3.2.1";
4881
4883
  }
4882
4884
  function ownPackageName(pkgDir) {
4883
4885
  try {
@@ -5089,6 +5091,7 @@ var CANON_OWNED = [
5089
5091
  ".claude/skills/canon-changelog/SKILL.md",
5090
5092
  ".claude/skills/canon-spec-review/SKILL.md",
5091
5093
  ".claude/skills/canon-inline-review/SKILL.md",
5094
+ ".claude/skills/canon-sweep/SKILL.md",
5092
5095
  ".claude/agents/code-review-anchored.md",
5093
5096
  ".claude/agents/code-review-cold.md",
5094
5097
  ".canon/templates/status.json",
@@ -5351,7 +5354,7 @@ function runUpgrade(cwd, pkgDir, options = {}) {
5351
5354
  cutoverWarnings.push(docsRefsCheckRel);
5352
5355
  }
5353
5356
  const versionPath = join5(cwd, ".canon", "version");
5354
- const newVersion = "3.1.3";
5357
+ const newVersion = "3.2.1";
5355
5358
  const currentVersion = existsSync5(versionPath) ? readFileSync4(versionPath, "utf8").trim() : null;
5356
5359
  if (currentVersion !== newVersion) {
5357
5360
  pending.push({ rel: ".canon/version", projectPath: versionPath, content: newVersion + "\n" });
@@ -5659,7 +5662,7 @@ Global:
5659
5662
  `);
5660
5663
  }
5661
5664
  function printVersion() {
5662
- console.log("3.1.3");
5665
+ console.log("3.2.1");
5663
5666
  }
5664
5667
  switch (command) {
5665
5668
  case "doctor":
@@ -4961,7 +4961,7 @@ function isInstalledSourcePath(sourcePath) {
4961
4961
  return sourcePath.includes("/node_modules/") || sourcePath.includes("\\node_modules\\") || sourcePath.includes("/_npx/") || sourcePath.includes("\\_npx\\");
4962
4962
  }
4963
4963
  function resolveCanonVersion(explicit) {
4964
- return explicit ?? "3.1.3" ?? "dev";
4964
+ return explicit ?? "3.2.1" ?? "dev";
4965
4965
  }
4966
4966
  function resolveOrchestratorCommit(repoRoot, upstreamCommit, runGitAt) {
4967
4967
  const ownToplevel = captureGitOutput(repoRoot, ["rev-parse", "--show-toplevel"], runGitAt);
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "canon-ai",
3
- "version": "3.1.3",
3
+ "version": "3.2.1",
4
4
  "type": "module",
5
- "description": "A spec-first, multi-agent coding pipeline you drop into any repo. The law your agents work under.",
5
+ "description": "A spec-first, multi-agent AI coding pipeline you drop into any repo. The law your agents work under.",
6
6
  "engines": {
7
7
  "node": "24.x"
8
8
  },
@@ -27,14 +27,14 @@
27
27
  },
28
28
  "devDependencies": {
29
29
  "@types/mustache": "^4.2.6",
30
- "@types/node": "^22.20.1",
31
- "eslint": "^10.8.1",
30
+ "@types/node": "^24.13.3",
31
+ "eslint": "^10.10.0",
32
32
  "mustache": "^4.2.0",
33
- "simple-git-hooks": "^2.13.1",
33
+ "simple-git-hooks": "^2.14.0",
34
34
  "tsup": "^8.5.1",
35
- "tsx": "^4.23.12",
35
+ "tsx": "^4.23.13",
36
36
  "typescript": "^5.9.3",
37
- "typescript-eslint": "^8.66.0"
37
+ "typescript-eslint": "^8.70.0"
38
38
  },
39
39
  "simple-git-hooks": {
40
40
  "pre-commit": "npm run sync-templates -- --stage"
@@ -135,7 +135,9 @@ By default, Claude Code prompts on every `git`, `gh`, `codex`, and `canon` subpr
135
135
  "Skill(canon-pipeline)", "Skill(canon-pipeline:*)",
136
136
  "Skill(canon-status)", "Skill(canon-status:*)",
137
137
  "Skill(canon-changelog)", "Skill(canon-changelog:*)",
138
- "Skill(canon-spec-review)", "Skill(canon-spec-review:*)"
138
+ "Skill(canon-spec-review)", "Skill(canon-spec-review:*)",
139
+ "Skill(canon-inline-review)", "Skill(canon-inline-review:*)",
140
+ "Skill(canon-sweep)", "Skill(canon-sweep:*)"
139
141
  ]
140
142
  }
141
143
  }
@@ -0,0 +1,141 @@
1
+ ---
2
+ name: canon-sweep
3
+ description: Use when the human asks to sweep, triage, or prune `docs/lessons-learned.md` — phrases like "sweep the lessons buffer", "let's do a lessons sweep", "promote or prune lessons learned", or explicit `/canon-sweep` invocation. Human-invoked only. A QA "buffer exceeds ~15 entries" note in a task's `done.md` or a just-closed release milestone are reasons to *suggest* a sweep to the human, never to start one. The skill proposes a verdict and the exact promoted text per entry and writes nothing until the human confirms. Not for appending new lessons (the QA phase does that) or for editing task notes.
4
+ allowed-tools: Read Glob Grep Edit Write Bash(git log *) Bash(git status *) Bash(git branch *) Bash(git diff *) Bash(git show *) Bash(git worktree list) Bash(canon task list)
5
+ effort: medium
6
+ ---
7
+
8
+ # Lessons Sweep
9
+
10
+ Use this skill to triage the lessons-learned buffer: decide, entry by entry, whether each one is promoted into a permanent doc, kept in the buffer, or pruned. The buffer is a staging area, not an archive. The sweep exists to keep it small and to keep the permanent docs from bloating.
11
+
12
+ **Promotion is rare.** Every line promoted is read by every agent on every future task, so it must earn that recurring cost. Most entries end as keep or prune. But the threshold that prompted the sweep is a reason to pay attention, not a target: never manufacture a prune to hit a count.
13
+
14
+ ## When to use
15
+
16
+ - The human asks for a sweep.
17
+ - You notice a reason to suggest one: a task's `done.md` carries the QA signal that the buffer exceeds ~15 entries, or a release milestone just closed. Suggest it in one line and wait. Never start a sweep on your own.
18
+
19
+ ## Preconditions
20
+
21
+ Triage (Steps 1 to 3) is read-only and can run from any checkout. Before **applying** anything (Step 4), run this preflight and stop if any check fails:
22
+
23
+ - `git status` is clean and `git branch --show-current` is a dedicated non-default branch.
24
+ - `git worktree list` shows the current directory as the main working tree, not a linked worktree. A task worktree is where a pipeline edits code; shared docs must not change there.
25
+ - `canon task list` shows no task with a phase in progress. A pipeline running in another worktree would otherwise pick up or collide with the sweep's edits to shared docs at merge time. If one is in flight, wait for it to reach `human_review` or stop it before applying. The sweep touches shared docs and should ship as its own small change through the normal review path.
26
+
27
+ Read before triaging:
28
+
29
+ 1. `docs/lessons-learned.md` in full, including the HTML sweep comments at the bottom. They record what was kept with a re-evaluation condition and what was already promoted; they are the cheapest way to avoid redoing work.
30
+ 2. The candidate destinations, so you can check coverage: `docs/patterns.md`, `docs/decisions.md`, `docs/architecture.md`, and the project's agent instruction files (`AGENTS.md`, `CLAUDE.md`) if it keeps them.
31
+ 3. `git log docs/lessons-learned.md` since the last sweep comment, and the task IDs shipped in that window, so you can tell "did not recur" from "had no chance to recur."
32
+
33
+ ## Step 1 — Triage every entry
34
+
35
+ Walk each entry through this sequence. Stop at the first stage that decides it, and write a one-line reason.
36
+
37
+ ### Stage A — Is it already settled? → Prune
38
+
39
+ - **Already covered.** Grep the permanent docs for the concept, not the exact words. A restatement of an existing pitfall, decision, or rule is a prune.
40
+ - **Obsolete.** The code path, tool, or convention it describes no longer exists.
41
+ - **Self-evident.** A competent agent with no doc at all would already do this. Reminders of general good practice are not lessons.
42
+ - **Its prevention has landed.** A test, lint check, or gate now enforces it, or a tracked follow-up (an issue, a backlog item the project actually works from) has explicitly taken ownership. A note in a sweep comment is not ownership; do not prune an unresolved structural lesson on the promise of a future test.
43
+
44
+ ### Stage B — Is the evidence sufficient? → Evaluate promotion
45
+
46
+ Promotion is on the table when either holds:
47
+
48
+ - **Recurrence.** It has bitten at least twice, in independent tasks. Check archived task notes and prior sweep comments for the second occurrence before claiming it.
49
+ - **Severity.** It bit once, but the cost of one more occurrence clearly exceeds the recurring cost of a rule read on every task. Say why in the reason.
50
+
51
+ And both of these hold:
52
+
53
+ - **Surprising or project-specific.** The reader could not have derived it from general experience.
54
+ - **Not already absorbed.** If an existing rule can take it as a clause, that is still a promotion, as an extension (Step 2). Only a brand-new rule requires that no nearby rule exists.
55
+
56
+ If the evidence is sufficient, go to Step 2 to pick the carrier. If it is not, fall through.
57
+
58
+ ### Stage C — Otherwise → Keep, with a condition
59
+
60
+ - **Happened once, not severe.** Leave it and see whether it bites again.
61
+ - **Borderline and adjacent to an existing rule.** Note which rule, so the next sweep can decide on a one-clause graft without re-reading.
62
+ - **Kept before, with no chance to recur.** If no task since the last sweep touched the area the lesson is about, the absence of recurrence is not evidence. Keep it. Prune on "no recurrence" only when there was relevant exposure and it still did not bite, or the human decides to let it go. This never applies to an unresolved structural lesson: one whose prevention is a test, lint check, or gate that has not landed and has no tracked owner stays until it does (Stage A), regardless of recurrence.
63
+
64
+ A kept entry always carries an explicit condition: "re-evaluate if X recurs in a different task" or "prune at the next sweep if tasks touching Y ship without hitting this." "Keep, unsure" is not a verdict.
65
+
66
+ ## Step 2 — Choose the smallest carrier
67
+
68
+ Promotion does not mean "write a new rule." Choose the lightest form that would have prevented the incident, in this order of preference:
69
+
70
+ 1. **A comment at the site.** If the trap is local to one function, file, or config block, a one-line comment where the next person will trip is better than a rule read by every agent on every task.
71
+ 2. **A test, lint check, or gate.** If the lesson can be enforced mechanically, prefer that over prose. If writing it is out of scope for the sweep, the entry stays in the buffer until the check lands or a tracked follow-up owns it (Stage A).
72
+ 3. **Extending an existing rule.** Add a clause, a trigger-table row, or a second example to a rule that already exists. This is the most common shape of a real promotion.
73
+ 4. **A new rule.** Only when no existing rule is close enough to absorb it. Keep it to the rule and its concrete prevention; cut the story of how it was discovered.
74
+
75
+ When the carrier is a doc, match the doc to the kind of truth:
76
+
77
+ | Kind of lesson | Home |
78
+ |---|---|
79
+ | A pitfall: something that looks right and silently isn't | `docs/patterns.md` (and its trigger table, if the project keeps one) |
80
+ | A settled decision or an explicit rule the project has chosen | `docs/decisions.md` |
81
+ | A command, validation, or environment gotcha | `docs/architecture.md`, validation section |
82
+ | A workflow rule every agent must know on every task, no matter what it is working on | The project's agent instruction files, if it keeps them. This is the highest bar: those files load into every session, so a rule lands here only when it is unconditionally load-bearing |
83
+
84
+ Rules of thumb for the promoted text:
85
+
86
+ - **Write the rule, not the war story.** Title names the rule. Body is rule, failure mode in one sentence, concrete prevention. Drop dates, task IDs, and the narrative of discovery; those stay in the buffer's git history.
87
+ - **Generalize past the incident.** The promoted form should read correctly to someone working on an unrelated feature. If it only makes sense with the original task in mind, it is not ready.
88
+ - **Verify anything executable.** If the entry prescribes a command, flag, or path, run it or check it exists before promoting. The buffer can carry an error for weeks; a permanent doc will carry it for years.
89
+ - **Lessons about the pipeline itself go upstream.** If the lesson is about canon's own behavior rather than this project's code, it belongs in a report to the canon-ai maintainers, not in this project's docs. Canon-managed files are overwritten on upgrade, so a rule written into one is lost. If this repository *is* the canon-ai source, upstream is here: the promotion lands in the managed file's authoritative copy and follows the project's own mirror-sync workflow.
90
+ - **A rule in the wrong home is nearly as bad as no rule.** Agents read docs just in time; a spec rule buried in a pitfalls doc is never read at spec time.
91
+
92
+ ## Step 3 — Propose before writing
93
+
94
+ Present the human with a verdict table before touching any file:
95
+
96
+ ```
97
+ | # | Entry (title) | Verdict | Carrier / condition | Why (one line) |
98
+ ```
99
+
100
+ **For every promote row, also show the exact text you intend to add and exactly where it goes** (file and section, or the existing rule being extended, with the new clause in place). The permanent rule is the most consequential output of the sweep; the human approves the words, not just the classification.
101
+
102
+ Then ask for approval with `AskUserQuestion`, offering: approve all, approve with changes (the human names which rows to flip or which text to edit), or stop. Do not write until the human has confirmed. If the human flips a verdict or rewrites a rule, accept it and move on.
103
+
104
+ ## Step 4 — Apply
105
+
106
+ Run the preflight above, then for each approved row:
107
+
108
+ - **Promote:** land the approved carrier, then delete the entry from the buffer. For a doc, make the edit exactly as approved. For a site comment, write the comment at the approved location. For a test, lint check, or gate, the entry leaves the buffer only once the check is actually in place; if it is deferred, the row is a keep, not a promote.
109
+ - **Prune:** delete the entry.
110
+ - **Keep:** leave the entry in place, byte for byte.
111
+
112
+ Then update the HTML sweep comments at the bottom of `docs/lessons-learned.md`. Add one comment for this sweep in the same shape as the existing ones:
113
+
114
+ ```html
115
+ <!-- Buffer swept YYYY-MM-DD (N entries reviewed: A promoted, B kept in buffer, C pruned).
116
+ Promotions → <destination>: <what was added, one line each>.
117
+ Kept in buffer: <entry> — re-evaluate if <condition>.
118
+ Pruned: <entry> — <reason>.
119
+ Unresolved follow-ups: <structural fixes not yet landed, upstream reports not yet filed>.
120
+ Prior entries are in git history. -->
121
+ ```
122
+
123
+ Keep the comment block bounded, or it becomes the archive this file says it is not. Carry forward only what the next sweep needs: the current keep conditions and any unresolved follow-ups. Drop older comments whose promotions and prunes are fully settled; git history retains them.
124
+
125
+ ## Step 5 — Hand off
126
+
127
+ Report to the human:
128
+
129
+ - the counts, and the number of entries remaining in the buffer. If it is still near or above the QA signal threshold, say why (approved keeps, unresolved structural follow-ups) rather than treating that as a failure;
130
+ - each promotion and where it landed;
131
+ - each follow-up that needs a decision: a test to write, an issue to file upstream, an entry waiting on ownership;
132
+ - the verification you ran on any command or path you promoted.
133
+
134
+ Do not commit. The human decides how the change ships, and it should get an independent review like any other below-pipeline edit.
135
+
136
+ ## What this skill does not do
137
+
138
+ - Append new lessons. That is the QA phase's job, and its entry format is its own concern.
139
+ - Set the sweep threshold. The buffer's own "How to use" section owns that.
140
+ - Rewrite or reorganize entries it is keeping.
141
+ - Start a sweep without a human asking.