@journeykit/canon 0.26.0 → 0.28.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.
Files changed (133) hide show
  1. package/dist/skills/SKILL.src.md +245 -89
  2. package/dist/skills/reference/brief-align.md +4 -2
  3. package/dist/skills/reference/brief-clarify.md +7 -5
  4. package/dist/skills/reference/brief-harden.md +4 -2
  5. package/dist/skills/reference/brief-polish.md +7 -5
  6. package/dist/skills/reference/brief-shape.md +4 -2
  7. package/dist/skills/reference/canon-artifact-ingest.md +11 -7
  8. package/dist/skills/reference/canon-authoring.md +16 -2
  9. package/dist/skills/reference/canon-context.md +21 -11
  10. package/dist/skills/reference/canon-fmt.md +10 -2
  11. package/dist/skills/reference/canon-gate.md +119 -83
  12. package/dist/skills/reference/canon-inventory.md +34 -22
  13. package/dist/skills/reference/canon-learn.md +23 -19
  14. package/dist/skills/reference/canon-plan-import.md +10 -7
  15. package/dist/skills/reference/canon-plugins.md +15 -8
  16. package/dist/skills/reference/canon-policy.md +20 -16
  17. package/dist/skills/reference/canon-report-dashboard.md +30 -19
  18. package/dist/skills/reference/canon-retrieve.md +13 -10
  19. package/dist/skills/reference/canon-review.md +96 -68
  20. package/dist/skills/reference/canon-session-ingest.md +7 -3
  21. package/dist/skills/reference/canon-storage.md +82 -51
  22. package/dist/skills/reference/canon-subject.md +123 -84
  23. package/dist/skills/reference/canon-vocab.md +1 -1
  24. package/dist/skills/reference/role-implementer.md +295 -64
  25. package/dist/skills/reference/role-orchestrator.md +547 -55
  26. package/dist/skills/reference/role-planner.md +189 -32
  27. package/dist/skills/reference/role-reviewer.md +257 -58
  28. package/dist/skills/scripts/canon-retrieve-pre-dispatch.sh +10 -5
  29. package/man/canon-adapter-authorize.1 +44 -0
  30. package/man/canon-adapter-validate.1 +44 -0
  31. package/man/canon-adapter.1 +46 -0
  32. package/man/canon-artifacts-move.1 +63 -0
  33. package/man/canon-artifacts-store.1 +54 -0
  34. package/man/canon-artifacts-verify.1 +48 -0
  35. package/man/canon-artifacts.1 +45 -0
  36. package/man/canon-brief-approve.1 +62 -0
  37. package/man/canon-brief-check.1 +44 -0
  38. package/man/canon-brief-show.1 +50 -0
  39. package/man/canon-brief.1 +63 -0
  40. package/man/canon-change-new.1 +66 -0
  41. package/man/canon-change.1 +42 -0
  42. package/man/canon-completion.1 +60 -0
  43. package/man/canon-context-pack-add.1 +47 -0
  44. package/man/canon-context-pack-show.1 +44 -0
  45. package/man/canon-context-pack-verify.1 +44 -0
  46. package/man/canon-context-pack.1 +50 -0
  47. package/man/canon-context.1 +44 -0
  48. package/man/canon-dashboard.1 +50 -0
  49. package/man/canon-demo-init.1 +44 -0
  50. package/man/canon-demo-review.1 +44 -0
  51. package/man/canon-demo.1 +47 -0
  52. package/man/canon-dispatch-begin.1 +80 -0
  53. package/man/canon-dispatch-diff.1 +41 -0
  54. package/man/canon-dispatch-end.1 +60 -0
  55. package/man/canon-dispatch.1 +45 -0
  56. package/man/canon-divergence-accept.1 +68 -0
  57. package/man/canon-divergence-add.1 +97 -0
  58. package/man/canon-divergence-defer.1 +71 -0
  59. package/man/canon-divergence-list.1 +56 -0
  60. package/man/canon-divergence-resolve.1 +68 -0
  61. package/man/canon-divergence.1 +51 -0
  62. package/man/canon-evidence-add.1 +168 -0
  63. package/man/canon-evidence-approval-payload.1 +65 -0
  64. package/man/canon-evidence-audit.1 +121 -0
  65. package/man/canon-evidence.1 +57 -0
  66. package/man/canon-export.1 +108 -0
  67. package/man/canon-feature-new.1 +62 -0
  68. package/man/canon-feature.1 +39 -0
  69. package/man/canon-finding-add.1 +196 -0
  70. package/man/canon-finding-close.1 +117 -0
  71. package/man/canon-finding-list.1 +59 -0
  72. package/man/canon-finding.1 +67 -0
  73. package/man/canon-format.1 +49 -0
  74. package/man/canon-gate-check.1 +44 -0
  75. package/man/canon-gate-install-hooks.1 +59 -0
  76. package/man/canon-gate-promote.1 +62 -0
  77. package/man/canon-gate.1 +50 -0
  78. package/man/canon-ingest-plans.1 +53 -0
  79. package/man/canon-ingest-records.1 +56 -0
  80. package/man/canon-ingest-sessions.1 +62 -0
  81. package/man/canon-ingest.1 +50 -0
  82. package/man/canon-init.1 +98 -0
  83. package/man/canon-inventory-sync.1 +50 -0
  84. package/man/canon-inventory.1 +39 -0
  85. package/man/canon-ledger-compact.1 +50 -0
  86. package/man/canon-ledger.1 +54 -0
  87. package/man/canon-migrate.1 +89 -0
  88. package/man/canon-plugin-sync.1 +53 -0
  89. package/man/canon-plugin.1 +39 -0
  90. package/man/canon-policy-approve.1 +50 -0
  91. package/man/canon-policy-show.1 +41 -0
  92. package/man/canon-policy.1 +52 -0
  93. package/man/canon-prompt-add.1 +47 -0
  94. package/man/canon-prompt-show.1 +41 -0
  95. package/man/canon-prompt.1 +43 -0
  96. package/man/canon-purge.1 +70 -0
  97. package/man/canon-query.1 +111 -0
  98. package/man/canon-regime-show.1 +50 -0
  99. package/man/canon-regime.1 +39 -0
  100. package/man/canon-report-build.1 +44 -0
  101. package/man/canon-report-check.1 +41 -0
  102. package/man/canon-report-snapshot.1 +47 -0
  103. package/man/canon-report.1 +45 -0
  104. package/man/canon-retrieve.1 +56 -0
  105. package/man/canon-review-change.1 +98 -0
  106. package/man/canon-review-scenario.1 +98 -0
  107. package/man/canon-review.1 +42 -0
  108. package/man/canon-scenario-new.1 +80 -0
  109. package/man/canon-scenario.1 +39 -0
  110. package/man/canon-schema.1 +63 -0
  111. package/man/canon-selftest.1 +69 -0
  112. package/man/canon-skills-check.1 +47 -0
  113. package/man/canon-skills-doctor.1 +47 -0
  114. package/man/canon-skills-install.1 +53 -0
  115. package/man/canon-skills-show.1 +104 -0
  116. package/man/canon-skills.1 +48 -0
  117. package/man/canon-status.1 +51 -0
  118. package/man/canon-strategy-approval-payload.1 +53 -0
  119. package/man/canon-strategy-promote.1 +59 -0
  120. package/man/canon-strategy-rollback.1 +62 -0
  121. package/man/canon-strategy.1 +45 -0
  122. package/man/canon-subject-adopt.1 +53 -0
  123. package/man/canon-subject-move.1 +80 -0
  124. package/man/canon-subject-new.1 +65 -0
  125. package/man/canon-subject-show.1 +44 -0
  126. package/man/canon-subject-unadopt.1 +63 -0
  127. package/man/canon-subject.1 +51 -0
  128. package/man/canon-task-done.1 +47 -0
  129. package/man/canon-task.1 +42 -0
  130. package/man/canon-tier-age.1 +50 -0
  131. package/man/canon-tier.1 +39 -0
  132. package/man/canon.1 +169 -0
  133. package/package.json +9 -5
@@ -27,10 +27,10 @@ Every violation carries one of these stable, grep-able strings:
27
27
  | `stale-evidence` | A passing record degraded to stale: its declared surface changed since its `evidence_sha`, or HEAD moved past `staleness.max_commits_behind`. Only degrades an already-green record. Also: a file bound to a latest evidence record whose bytes are gone — no intact blob in `.canon/artifacts/sha256/` and no working-tree copy with the recorded digest — or a file bound to an in-effect review with no intact blob (see Bound evidence stays provable below). |
28
28
  | `malformed-evidence` | A candidate record doesn't parse, is misfiled, or carries an unparseable interim tag. Malformed evidence is no evidence. |
29
29
  | `flagged` | The human-only `flagged` overlay is set — never green regardless of passing evidence. |
30
- | `unevidenced-flip` | `canon gate task <task_id>` was asked to flip a checkbox with no matching, non-`Divergent` evidence record. |
31
- | `fabricated-evidence` | An evidence note contains a blocklisted marker (`"would pass"`, `"TBD"`, `"n/a"`) standing as a whole word — never inside a path, a file name, a URL or a longer word, so `pack.json/atlas` is not `n/a` — or a bare `verified` claim with no attached command result. `canon evidence add` refuses such a note before writing (`canon evidence add: refused — fabricated-evidence …`, exit 1). |
30
+ | `unevidenced-flip` | `canon task done <task_id>` was asked to flip a checkbox with no matching, non-`Divergent` evidence record. |
31
+ | `fabricated-evidence` | An evidence note contains a blocklisted marker (`"would pass"`, `"TBD"`, `"n/a"`) standing as a whole word — never inside a path, a file name, a URL or a longer word, so `pack.json/atlas` is not `n/a` — or a bare `verified` claim with no attached command result. `canon evidence add` refuses such a note before writing (refused with `fabricated-evidence`, exit 1). |
32
32
  | `open-blocker` | With `spec_coverage.require_review` set (and `block_on_findings`, the default), a Finding whose latest version is severity `blocker`, disposition `open`, on a change an in-scope subject adopted — or closed `fixed` while an evidenced scenario of the subject has no evidence recorded after the close (re-verification). Subject string `<change_id>#<round>.<seq>`. See `canon-review`. |
33
- | `false-success` | The latest audit record (`canon evidence add --kind audit`) of a scenario says `refuted`: an external evaluator found a counterexample to the claimed success. Always on, regardless of policy or sampling, and takes precedence over any declared faithful verdict; only a newer audit (`confirmed` or `indeterminate`) clears it. The detail names the evaluator, its version, its sha256 and the counterexample paths. |
33
+ | `false-success` | The latest audit record (`canon evidence audit`) of a scenario says `refuted`: an external evaluator found a counterexample to the claimed success. Always on, regardless of policy or sampling, and takes precedence over any declared faithful verdict; only a newer audit (`confirmed` or `indeterminate`) clears it. The detail names the evaluator, its version, its sha256 and the counterexample paths. |
34
34
 
35
35
  ## `canon gate check [--repo <dir>] [--release]`
36
36
 
@@ -53,26 +53,34 @@ repo ROOT's `.canon/policy.yaml` and `.canon/ledger` from any subdirectory.
53
53
 
54
54
  When `.canon/policy.yaml` exists but has no approval, the gate prints
55
55
  `advisory: policy not approved: …` after its result, naming
56
- `canon policy approve --by <who>`; the exit code is unchanged. When its
56
+ `canon policy approve --approver <who>`; the exit code is unchanged. When its
57
57
  bytes no longer match the approved digest, or the approval cannot be read,
58
58
  the gate fails closed: the violation is reported and the exit is `1` (see
59
59
  `canon-policy`).
60
60
 
61
- Before it reads the scenario index, the gate rebuilds any stale part of
62
- it from the changed `.feature` files (the same code `canon inventory
63
- sync` runs) and says so in one stderr line, silent when the index is
64
- fresh:
65
-
66
- ```
67
- canon gate check: refreshed the scenario index from 1 changed feature file(s) (.canon/specs/kind=feature/area=cart/add.feature): 1 record(s) written
68
- ```
69
-
70
- A root that fails to sync (format violations, a duplicate id) is named
71
- instead, ending ``NOT refreshed: root `x` (N format violation(s), 0 sync
72
- error(s)) — `canon inventory sync` lists them``, and the gate fails
73
- closed: it adds `uncovered-cell <root> — the scenario index for spec root
74
- `<root>` is stale and could not be refreshed (…); `canon inventory sync`
75
- lists the problems to fix` and exits 1, since the index it would grade
61
+ On a repository that still holds 0.27 canon state, `canon gate check` (like
62
+ `canon status` and `canon context`) warns and puts `canon migrate --json`
63
+ first in `meta.next`: run `canon migrate` (a preview), then `canon migrate
64
+ --commit --plan-digest <digest from the preview>`, then `canon skills
65
+ install`. Read the whole result with `--json`: `data.violations` lists the
66
+ failures by class and `meta.next` the steps.
67
+
68
+ **Done when:** `canon gate check` exits `0` with no violation; `canon status`
69
+ repeats the result and names what is still open.
70
+
71
+ The gate is a read command: it writes nothing. A scenario index that is
72
+ stale (a `.feature` file added, edited or deleted since the last sync) is
73
+ graded as the refresh would leave it, built in memory from the changed
74
+ files with the code `canon inventory sync` runs; the index on disk is
75
+ brought up to date by the next command that writes (`canon evidence add`,
76
+ `canon review scenario`, `canon subject move`) or by `canon inventory sync`.
77
+ It prints no refresh line.
78
+
79
+ A root that fails to sync (format violations, a duplicate id) cannot be
80
+ graded in memory either, and the gate fails closed: it adds
81
+ `uncovered-cell <root> — the scenario index for spec root `<root>` is
82
+ stale and could not be refreshed (…); `canon inventory sync` lists the
83
+ problems to fix` and exits 1, since the index it would grade
76
84
  does not hold the edits. The `verifying → shipped` guard refuses with
77
85
  the same violation, and `canon status` lists the sync as its only step.
78
86
  Fix what the sync lists and run the gate again. See `canon-storage`
@@ -121,11 +129,11 @@ A present section, even `require_evidence: false` or a malformed one
121
129
 
122
130
  - **Unimplemented** — no `EvidenceRecord` carries the scenario's
123
131
  `(project_id, scenario_id)`. Author one with
124
- `canon evidence add --scenario-id <id> --project-id <root-id>`; both
125
- flags are required together, because two spec roots may carry the same
126
- scenario id. `--summary` is allowed here too (scanned for fabrication
132
+ `canon evidence add --scenario-id <id> --project-id <root-id>`; the
133
+ root id is needed once more than one spec root is configured, because
134
+ two roots may carry the same scenario id. `--summary` is allowed here too (scanned for fabrication
127
135
  markers like a task's); it is a row suffix only when the record also
128
- names a `--task`. A record carrying both `--task` and `--scenario-id`
136
+ names a `--task-id`. A record carrying both `--task-id` and `--scenario-id`
129
137
  is one attestation answering both joins: it counts for the task cell
130
138
  and for the scenario cell (verdicts, the shipped gate, the review gate).
131
139
  - **Mismatched** — the folded divergence state is `open`,
@@ -143,12 +151,12 @@ A present section, even `require_evidence: false` or a malformed one
143
151
  case). This is the gap evidence presence cannot see: every scenario
144
152
  attested, none of them specifying a refusal. Untagged scenarios satisfy
145
153
  no case, so an unclassified corpus is reported, not passed. Works with
146
- `require_evidence: false` too. `canon subject status <id> --to shipped`
154
+ `require_evidence: false` too. `canon subject move <id> --to shipped`
147
155
  applies the same rule to the scenarios the subject owns.
148
156
  - **Independent review** (`require_review`) — scenarios whose subject is
149
157
  in `require_review.scope` (default `[verifying, shipped]`; `[]` = every
150
158
  scenario; independent of `scope` above) need a `Review` record
151
- (`canon review add`) whose latest version per reviewer is `signed`,
159
+ (`canon review scenario`) whose latest version per reviewer is `signed`,
152
160
  still matches the scenario digest and latest evidence it pinned
153
161
  (a review written before 0.15 has no pins and skips this check), and,
154
162
  when `distinct_actor` (default `true`), is by a reviewer and actor
@@ -173,7 +181,7 @@ A present section, even `require_evidence: false` or a malformed one
173
181
  - **Sampled audits** (`require_audit`) — a deterministic sample of
174
182
  in-scope scenarios (`require_audit.scope`, default `[verifying,
175
183
  shipped]`; `exclude_lanes` applies) must carry an external audit
176
- (`canon evidence add --kind audit`, see Audit records below). A sampled
184
+ (`canon evidence audit`, see Audit records below). A sampled
177
185
  scenario whose latest audit is missing or `indeterminate` is
178
186
  `uncovered-cell`:
179
187
 
@@ -204,11 +212,13 @@ purpose, so enabling nothing changes no existing verdict.
204
212
  ### Accepted divergence and re-verification
205
213
 
206
214
  ```bash
207
- canon divergence accept --project-id <root-id> --scenario-id <id> --reason "<why>" --tracking <issue-url-or-ref> [--expiry YYYY-MM-DD] --reviewer <me> --actor-id <me> --session-id <session> --as reviewer
215
+ canon divergence accept cart.add.01 --project-id root --reason "known gap" --tracking "issue#12" --expiry 2026-12-31 --actor-id reviewer-2 --role reviewer --session-id review-session-1
208
216
  ```
209
217
 
210
- A reviewer accepts a scenario whose latest evidence is `divergent` as a
211
- known gap limited by something outside the change; never to skip a fix.
218
+ Accepting a scenario whose latest evidence is `divergent` as a known gap
219
+ limited by something outside the change is a human-only act: with a role
220
+ declared (`--as`, `CANON_ROLE`) the leaf is refused with `HUMAN_ONLY`. It is
221
+ never a way to skip a fix.
212
222
  Refused (`DIVERGENCE_ACCEPT_REFUSED`) unless the latest verdict is
213
223
  divergent and the session and actor differ from the evidence's. The record
214
224
  pins that evidence: new evidence for the scenario, or `--expiry` passing,
@@ -226,36 +236,36 @@ from shipping until every one of its evidenced scenarios has evidence
226
236
  recorded after the close or the approval. A blocker that names its
227
237
  scenarios (`finding add --scenario-id`) asks only those, and the refusal
228
238
  names them with the per-scenario command (`canon evidence add --project-id
229
- <root-id> --scenario-id <id> --report <format>:<path> …`). Otherwise the
239
+ <root-id> --scenario-id <id> --report <format>:<path>` plus `--class`, `--role` and `--ref`). Otherwise the
230
240
  refusal names the scenarios and the batch command: `canon evidence add --project-id <root-id>
231
- --all-scenarios --report <format>:<path> --kind test-run --role
232
- implementer --ref "<test command>" …`. Reviews pin what the evidence says
241
+ --all-scenarios --report <format>:<path> --class test-run --role
242
+ implementer --ref "<test command>"`. Reviews pin what the evidence says
233
243
  (its verdict and the identity of its checks), not which record it is, so
234
244
  recording the suite again with the same verdicts from the same cases leaves
235
245
  every scenario review current. A chore instead needs one change-level
236
- review of the chore itself (`canon review add --change-id <slug> …`) before
246
+ review of the chore itself (`canon review change <slug>`) before
237
247
  `--to shipped`; a changed verdict or case set stales that scenario's
238
248
  review as before (and a new `divergence accept`, where the gap remains).
239
249
 
240
- ### Audit records (`--kind audit`)
250
+ ### Audit records (`canon evidence audit`)
241
251
 
242
252
  canon never generates or runs an audit. An external evaluator does, and
243
253
  canon records its result:
244
254
 
245
255
  ```bash
246
- canon evidence add --kind audit --project-id root --scenario-id cart.add.01 \
256
+ canon evidence audit cart.add.01 --project-id root \
247
257
  --ref "judge run 42" --role reviewer \
248
258
  --evaluator-id judge --evaluator-version 1.2 \
249
- --evaluator-digest <64 lowercase hex sha256> \
259
+ --evaluator-digest 9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08 \
250
260
  --audit-result refuted --report junit:audit/counterexample.xml
251
261
  ```
252
262
 
253
- - `--kind audit` requires `--evaluator-id`, `--evaluator-version`,
254
- `--evaluator-digest` (sha256, 64 lowercase hex), `--audit-result
255
- confirmed|refuted|indeterminate`, and `--scenario-id` with
256
- `--project-id`. It refuses `--task`, `--verdict`, `--surface-ref` and
257
- `--approval-*`; the four evaluator flags are refused on any other kind.
258
- All refusals exit `2`.
263
+ - `evidence audit` requires the scenario operand, `--ref`, `--evaluator-id`,
264
+ `--evaluator-version`, `--evaluator-digest` (sha256, 64 lowercase hex)
265
+ and `--audit-result confirmed|refuted|indeterminate`, with `--project-id`
266
+ where several spec roots exist. It takes no `--task-id`, `--verdict`,
267
+ `--surface-ref` or `--approval-*`; the evaluator flags exist on no other
268
+ command. All refusals exit `2`.
259
269
  - The record carries `audit: {evaluator_id, evaluator_version,
260
270
  evaluator_digest, result}`; its verdict is derived (`confirmed` →
261
271
  faithful, `refuted` → divergent, `indeterminate` → not-applicable).
@@ -265,10 +275,10 @@ canon evidence add --kind audit --project-id root --scenario-id cart.add.01 \
265
275
  - Audits fold apart: per `(project_id, scenario_id)` there is a latest
266
276
  declared record and a latest audit. An audit never becomes the
267
277
  scenario's latest declared verdict.
268
- - `canon subject status <id> --to shipped` refuses `false-success`, and an
278
+ - `canon subject move <id> --to shipped` refuses `false-success`, and an
269
279
  owed sampled audit when `require_audit.scope` covers `shipped`.
270
280
  - `canon status` shows an `audits` section (sampled / confirmed / owed /
271
- refuted) and suggests `canon evidence add --kind audit …` in `next:`.
281
+ refuted) and suggests `canon evidence audit` in `next:`.
272
282
 
273
283
  ### A record binds what its own write names
274
284
 
@@ -277,7 +287,7 @@ canon evidence add --kind audit --project-id root --scenario-id cart.add.01 \
277
287
  from an earlier record, in a single add or in a suite batch
278
288
  (`--all-scenarios`), and no drop is recorded: history keeps the earlier
279
289
  records, and a new record is reviewed on its own files. An audit
280
- (`--kind audit`) replaces a refutation the same way, so a counterexample
290
+ (`canon evidence audit`) replaces a refutation the same way, so a counterexample
281
291
  never rides along into a confirming audit. `--drop-attachment` and
282
292
  `--drop-reason` no longer exist.
283
293
 
@@ -295,11 +305,11 @@ external openspec plan with a `proposal.md` under the plan sources (the
295
305
  check `canon status` and the ship guard use; see
296
306
  `canon-subject`; which changes a scenario answers to is scoped by its `@flow:<change>.<F-id>` tags, described there). The subjects are the scenario's `@subject` owner for
297
307
  `--scenario-id`, and every subject adopting the task's change for
298
- `--task`:
308
+ `--task-id`:
299
309
 
300
- ```
301
- canon evidence add: unreviewed-promotion demo-subject — evidence for world.demo.01: brief not approved — .canon/changes/c-demo/brief.md has no `Approved:` line, so the human has not answered it; once they do, record their words with `canon brief approve --change-id c-demo --by <the human> --note "<their words>"`
302
- canon evidence add: refused — stop, report the brief to the human and wait for their answer: nothing is built or attested for a subject before its brief is approved. Record their answer with `canon brief approve --by <the human> --note "<their words>"` only from that answer; to attest anyway, pass --override-reason <text>, which is recorded on the evidence record
310
+ ```stderr
311
+ canon evidence add: unreviewed-promotion demo-subject — evidence for world.demo.01: brief not approved — .canon/changes/c-demo/brief.md has no `Approved:` line, so the human has not answered it; once they do, record their words with `canon brief approve c-demo --approver <the human> --note "<their words>"`
312
+ canon evidence add: refused — stop, report the brief to the human and wait for their answer: nothing is built or attested for a subject before its brief is approved. Record their answer with `canon brief approve --approver <the human> --note "<their words>"` only from that answer; to attest anyway, pass --override-reason <text>, which is recorded on the evidence record
303
313
  ```
304
314
 
305
315
  For a change from an external plan with no canon brief yet, the first
@@ -309,13 +319,13 @@ openspec/changes/c-ext/proposal.md, which canon reads and never edits, so
309
319
  its approval lives in that canon file, so the human has not answered it;
310
320
  once they do, create .canon/changes/c-ext/brief.md holding a `Source:
311
321
  openspec/changes/c-ext/proposal.md` line, then record their words with
312
- `canon brief approve --change-id c-ext --by <the human> --note "<their words>"` ``. An `Approved:` line in the
322
+ `canon brief approve c-ext --approver <the human> --note "<their words>"` ``. An `Approved:` line in the
313
323
  external `proposal.md` itself is never read.
314
324
 
315
325
  A subject with no brief to answer (no adopted change, or a newest change
316
326
  with neither a canon `brief.md` nor an external `proposal.md`) and a
317
327
  scenario no subject owns are unaffected.
318
- `canon review add` and `canon finding add` are never refused.
328
+ `canon review scenario` and `canon finding add` are never refused.
319
329
  `--override-reason "<one line>"` attests anyway, prints the `waived …`
320
330
  lines and `override recorded by `<actor>` for the N brief refusal(s)
321
331
  above: <reason>`, and records on the evidence record:
@@ -328,6 +338,12 @@ brief_override:
328
338
  actor: { agent_id: lead } # from --actor-id
329
339
  ```
330
340
 
341
+ Under `--json` the same waiver is `data.overrides[]` (`code` of the waived
342
+ refusal, `reason` verbatim), so a caller sees what its `--override-reason`
343
+ let through; a repeat of an identical record is `unchanged`
344
+ (`meta.unchanged`) and records no second override. `--override-reason`
345
+ exists on `subject move` too (see `canon-subject`).
346
+
331
347
  A blank or multi-line reason exits 2; an override when nothing refused is
332
348
  not recorded. `canon status` lists each such record as a `warning:`, and
333
349
  `canon gate check` prints them after the result without failing:
@@ -339,7 +355,7 @@ brief overrides: 1 evidence record(s) attested before their brief was approved
339
355
 
340
356
  ### Review-waiver advisories
341
357
 
342
- A subject moved into a review-scoped status with `canon subject status <id> --to <state>
358
+ A subject moved into a review-scoped status with `canon subject move <id> --to <state>
343
359
  --override-reason` (see `canon-subject`) keeps the `unreviewed-promotion`
344
360
  and `open-blocker` gaps its waiver recorded visible without failing the
345
361
  gate. Only those exact `(class, subject)` pairs are waived; a gap that
@@ -353,7 +369,7 @@ review waivers: 1 advisory(ies) — not failing the gate:
353
369
 
354
370
  The waiver lasts only until the subject's next status write.
355
371
 
356
- `canon subject status <id> --to shipped` also refuses where a brief exists and
372
+ `canon subject move <id> --to shipped` also refuses where a brief exists and
357
373
  is not approved (the subject's scope, its newest adopted change walking back past pending drafts
358
374
  no scenario is tagged `@flow:<change>.*` for, has a canon
359
375
  `brief.md` with no `Approved:` line or one the brief's text no longer
@@ -416,13 +432,13 @@ what backed the claim:
416
432
  ```bash
417
433
  # any file: a Playwright trace, screenshots, an agent QA log — by sha256
418
434
  canon evidence add --scenario-id cart.add.04 --project-id root \
419
- --kind agent-qa --ref "stagehand run 7" --role implementer \
435
+ --class agent-qa --ref "stagehand run 7" --role implementer \
420
436
  --artifact test-results/cart-trace.zip
421
437
  # a JUnit XML or Cucumber JSON report: also records the matched case + outcome
422
- canon evidence add ... --report junit:target/nextest/default/junit.xml
423
- canon evidence add ... --report cucumber:reports/cucumber.json
438
+ canon evidence add --scenario-id cart.add.04 --project-id root --class test-run --ref "cargo nextest run" --role implementer --report junit:target/nextest/default/junit.xml
439
+ canon evidence add --scenario-id cart.add.04 --project-id root --class test-run --ref "cucumber-js" --role implementer --report cucumber:reports/cucumber.json
424
440
  # a Swift Testing event stream (swift test --experimental-event-stream-output <path>)
425
- canon evidence add ... --report swift-testing:.build/events.jsonl
441
+ canon evidence add --scenario-id cart.add.04 --project-id root --class test-run --ref "swift test" --role implementer --report swift-testing:.build/events.jsonl
426
442
  ```
427
443
 
428
444
  A case is bound when the scenario id is its OWN: the first
@@ -476,7 +492,7 @@ One test run proves many scenarios. Bind it once:
476
492
  ```bash
477
493
  canon evidence add --project-id root --all-scenarios \
478
494
  --report junit:target/nextest/default/junit.xml \
479
- --kind test-run --ref "cargo nextest run" --role implementer
495
+ --class test-run --ref "cargo nextest run" --role implementer
480
496
  ```
481
497
 
482
498
  Each indexed scenario of `--project-id` that owns a case (the rule above)
@@ -499,30 +515,50 @@ It is all or nothing: a claimed `faithful` verdict over a failed case,
499
515
  a fabrication marker in `--summary`, an unapproved brief for any
500
516
  scenario's subject refuses the whole batch, and nothing is written. Each
501
517
  record binds only what the batch names (nothing is carried forward).
502
- `--scenario-id`, `--task`,
503
- `--report-case`, `--kind audit` and the approval flags name one subject
518
+ `--scenario-id`, `--task-id`,
519
+ `--report-case`, `canon evidence audit` and the approval flags name one subject
504
520
  and are refused with `--all-scenarios`.
505
521
 
506
522
  A task's evidence with `--report` and no `--scenario-id` binds EVERY case
507
523
  in the report, so a `faithful` verdict needs all of them to pass.
508
524
 
525
+ Many records at once without a report: `--input` takes inline JSON, a file
526
+ or `-`; an array (or NDJSON) is a batch, validated element by element before
527
+ the first write (a refusal writes nothing, exit 2). Keys are the option
528
+ names in camelCase plus `operands`: one object per scenario, with
529
+ `scenarioId`, `class`, `ref` and `role`. `canon schema evidence add --json`
530
+ shows the input schema and an example. Each element reports `written`,
531
+ `unchanged` or `failed` in `data.items[]`.
532
+
533
+ `--rev <rev>` names the commit the evidence ran against (default `HEAD`).
534
+ Evidence for uncommitted code is recorded with its `codeTree` and
535
+ `worktreeDirty`; outside git, or with no commit, the add is refused with
536
+ `NO_COMMIT`.
537
+
538
+ **Done when:** `canon gate check` shows no violation for the scenarios you
539
+ recorded evidence for; each record's own output line says whether it was
540
+ written or `unchanged`.
541
+
509
542
  ### A write that makes a review stale says so (0.18)
510
543
 
511
544
  When new evidence (one record or a batch) supersedes the evidence a
512
545
  current pinned review saw, `canon evidence add` prints one line per
513
546
  review it makes stale (its verdict or checks differ from what the review saw), with the command that re-pins it:
514
547
 
515
- ```
516
- canon evidence add: review by `reviewer-2` of cart.add.01 is now stale (this record's verdict or checks differ from what it saw); re-pin it: canon review add --project-id root --scenario-id cart.add.01 --reviewer reviewer-2 --actor-id reviewer-2 --role reviewer --pin <HEAD sha> --original-spec-ref specs/kind=feature/area=cart/add.feature
548
+ ```stderr
549
+ canon evidence add: review by `reviewer-2` of cart.add.01 is now stale (this record's verdict or checks differ from what it saw); re-pin it: canon review scenario cart.add.01 --project-id root --actor-id reviewer-2 --role reviewer --original-spec-ref specs/kind=feature/area=cart/add.feature
517
550
  ```
518
551
 
519
552
  The decision is the gate's own (the same stale-review rule `canon gate
520
553
  check` reports). The command repeats the review's reviewer, actor, role,
521
- session, provenance ref, runtime labels and artifacts, pinned at `HEAD` (`<sha>`
522
- when the repository has no commit). A record that is only staged says
554
+ session, provenance ref, runtime labels and artifacts, at the default
555
+ `--rev` (`HEAD`). A record that is only staged says
523
556
  the review "goes stale once this evidence is promoted": promote first,
524
- then re-pin. The reviewer re-reviews before re-pinning; several
525
- scenarios re-pin in one `canon review add` (see `canon-review`).
557
+ then re-review. The reviewer re-reviews before re-pinning; several
558
+ scenarios re-review in one `canon review scenario` (see `canon-review`).
559
+
560
+ **Done when:** `canon gate check` reports no stale review for the scenarios
561
+ you recorded evidence for; the re-review's own output says `pinned`.
526
562
 
527
563
  ## Bound evidence stays provable (the artifact store)
528
564
 
@@ -536,11 +572,11 @@ raise the limit with `--max-artifact-mib <N>`.
536
572
  Where those bytes live is `canon.yaml`'s `artifact_store: { backend: local |
537
573
  <s3 tier>, prefix: artifacts/ }` (default `local`: the directory above,
538
574
  git-tracked, no credentials). With an object-store tier, `evidence add`,
539
- `review add` and `evidence vault` upload each blob under
575
+ `review scenario` and `artifacts store` upload each blob under
540
576
  `<prefix>sha256/<digest>` and keep nothing locally. `canon gate check` and
541
577
  `canon artifacts verify [--full]` ask the store (a HEAD per digest; `--full`
542
578
  downloads and re-hashes) and refuse, never pass, when it cannot be reached.
543
- `canon artifacts migrate --to <tier> [--dry-run]` moves an existing local
579
+ `canon artifacts move --tier <tier>` (a preview; `--commit --plan-digest` acts) moves an existing local
544
580
  store; it is the repo owner's decision. See `canon-storage`.
545
581
 
546
582
  `canon gate check` re-checks every file bound to a LATEST evidence record
@@ -550,11 +586,11 @@ store; it is the repo owner's decision. See `canon-storage`.
550
586
  after the working-tree file is rewritten (a smoke script that rewrites
551
587
  `reports/smoke.json` on every run no longer orphans the record);
552
588
  - no blob, but the working-tree path still hashes to the recorded digest
553
- → clean, with an advisory naming `canon evidence vault` (records from
589
+ → clean, with an advisory naming `canon artifacts store` (records from
554
590
  before the store existed):
555
591
 
556
592
  ```
557
- evidence artifacts: 1 bound file(s) not in the artifact store — not failing the gate; run `canon evidence vault` to store them before the working tree changes:
593
+ evidence artifacts: 1 bound file(s) not in the artifact store — not failing the gate; run `canon artifacts store` to store them before the working tree changes:
558
594
  unstored cart.add.01 reports/junit.xml (sha256 b7cb…) is verified against the working tree only
559
595
  ```
560
596
  - anything else → `stale-evidence` naming the path, the recorded digest,
@@ -570,7 +606,7 @@ subject's status, and a repo with no `spec_coverage` would otherwise never
570
606
  have its bindings checked. A superseded record is not checked —
571
607
  re-attesting is the fix.
572
608
 
573
- Files bound to a review (`canon review add --artifact`, see
609
+ Files bound to a review (`canon review scenario --artifact`, see
574
610
  `canon-review`) are checked by the same rule for every CURRENT in-effect
575
611
  review: the latest per `(project_id, scenario_id, reviewer)`, less any
576
612
  that went stale against its scenario (its spec or evidence pin moved). A stale review is
@@ -587,7 +623,7 @@ Anything else is `stale-evidence` on the review's scenario, and the
587
623
  detail names the review: ``review by `<reviewer>` (actor `<id>`,
588
624
  <time>)``.
589
625
 
590
- `canon evidence vault [--max-artifact-mib <N>]` stores the bytes for
626
+ `canon artifacts store [--max-artifact-mib <N>]` stores the bytes for
591
627
  existing evidence records and current reviews (a superseded review's files are not vaulted): every attachment whose
592
628
  working-tree file still matches its digest is copied in, and everything
593
629
  else is listed as not stored, with both digests. It exits `0` whenever it
@@ -623,7 +659,7 @@ Malformed `risk_tiers` sections produce a stable `uncovered-cell` policy
623
659
  violation (`risk_tiers policy-invalid: ...`) instead of degrading to an
624
660
  empty clean map.
625
661
 
626
- ## `canon gate task <task_id> [--repo <dir>]`
662
+ ## `canon task done <task_id> [--repo <dir>]`
627
663
 
628
664
  For imported external plans, and for a `tasks.md` that already exists in
629
665
  a canon change. `canon change new` writes no `tasks.md` (0.19), and
@@ -642,7 +678,7 @@ task at all is a loud failure naming every source consulted. A repo with
642
678
  no `.canon/changes/` and `sources: []` is refused: "this repo configures
643
679
  no plan sources (no `.canon/changes` and no `plans.sources` entries in
644
680
  canon.yaml) … write the change with `canon change new`, or list your
645
- external plans under `plans.sources`" (`canon dispatch --task` says the
681
+ external plans under `plans.sources`" (`canon dispatch begin --task-id` says the
646
682
  same).
647
683
  It requires
648
684
  a matching non-`Divergent` evidence record, and flips `- [ ]` → `- [x]`
@@ -656,7 +692,7 @@ exits `1` with the blocking violation on stderr. After a flip, the plan
656
692
  source is re-ingested so the record store's task status agrees with the
657
693
  checkbox (`canon query --kind task` reads `done`); a failed re-ingest is
658
694
  a stderr warning naming `canon ingest plans`, never a failed flip.
659
- The success line is `canon gate task: <task_id> flipped — written
695
+ The success line is `canon task done: <task_id> flipped — written
660
696
  directly; nothing to promote`. An already-`[x]` row is an idempotent no-op (exit `0`). An unknown
661
697
  `task_id` is reported (exit `1`).
662
698
 
@@ -664,19 +700,19 @@ A flip on evidence that cannot prove the task still succeeds, but warns
664
700
  on stderr: when none of the task's evidence records binds a file and one
665
701
  of them cites a canon command as its `--ref` (`canon gate check`,
666
702
  `canon status`), the warning says to bind the artifact or report that
667
- proves the task (`canon evidence add --task <id> --artifact <path>` or
703
+ proves the task (`canon evidence add --task-id <id> --artifact <path>` or
668
704
  `--report <format>:<path>`). A canon command reports on canon's own
669
705
  records, so it is never the proof of a task.
670
706
 
671
707
  ```bash
672
- canon gate task my-change#5.2 --repo .
708
+ canon task done my-change#5.2 --repo .
673
709
  ```
674
710
 
675
711
  This is canon's own authority for the checkbox grammar — always flip
676
- through `canon gate task`, never by hand.
712
+ through `canon task done`, never by hand.
677
713
 
678
714
  Canon only reads external plans, with this one exception: for a task
679
- from an external plan source, `canon gate task` flips the checkbox in
715
+ from an external plan source, `canon task done` flips the checkbox in
680
716
  that plan's own `tasks.md` (or plan document). It is the one place canon
681
717
  writes into an external plan; their briefs and approvals still live in
682
718
  `.canon/changes/<change>/brief.md`. (Open for the maintainer: record
@@ -713,8 +749,8 @@ commits everything staged. It stays safe to run anyway: with nothing
713
749
  staged it prints
714
750
  `nothing staged` and exits `0`. `divergence add` stages for `canon
715
751
  gate promote` and names it, under the same `write:` rule. Every
716
- other write — `review add`, `subject new|adopt|status`, `change new`, `scenario new`, `feature new`,
717
- `gate task` (it edits the plan document and re-ingests it)
752
+ other write — `review scenario`, `subject new|adopt|move`, `change new`, `scenario new`, `feature new`,
753
+ `task done` (it edits the plan document and re-ingests it)
718
754
  — is final when it returns, and its success line ends `— written
719
755
  directly; nothing to promote`. Full promote-on-write contract:
720
756
  `canon-storage` (Staged writes and promotion). `divergence resolve|defer|accept` stage and promote their own record in the same run and end `— staged and promoted in this run; nothing to promote`.
@@ -739,7 +775,7 @@ canon gate install-hooks --repo . --event PreToolUse --matcher Edit --command "c
739
775
 
740
776
  Prefer this over hand-editing `settings.json`/`hooks.json`.
741
777
 
742
- ## `canon gate selftest`
778
+ ## `canon selftest --suite trust-spine-gate`
743
779
 
744
780
  Runs the shipped fixture corpus — one fixture per failure class (ten,
745
781
  including `false-success`), each a
@@ -749,7 +785,7 @@ deliberately broken corpus proving that class fires and ONLY that class
749
785
  check` run's green.
750
786
 
751
787
  ```bash
752
- canon gate selftest
788
+ canon selftest --suite trust-spine-gate
753
789
  ```
754
790
 
755
791
  ## Reading a violation
@@ -6,21 +6,26 @@ records every downstream stage joins against.
6
6
 
7
7
  ## You rarely run it yourself
8
8
 
9
- Since 0.16 the commands that read the index refresh it first:
10
- `canon status`, `canon gate check`, `canon evidence add`,
11
- `canon review add` and `canon subject status` index added, edited and
12
- deleted `.feature` files themselves, with the same code this command runs,
13
- scoped to the changed files, and say so in one stderr line:
14
-
15
- ```
16
- canon status: refreshed the scenario index from 1 changed feature file(s) (.canon/specs/kind=feature/area=cart/add.feature): 1 record(s) written
9
+ Since 0.16 the commands that read the index see added, edited and deleted
10
+ `.feature` files without a sync step. `canon evidence add`, `canon evidence
11
+ audit`, `canon artifacts store`, `canon divergence accept`, `canon review
12
+ scenario` and `canon subject move` refresh the index on disk first, with the
13
+ same code this command runs, scoped to the changed files, and say so in one
14
+ stderr line:
15
+
16
+ ```stderr
17
+ canon artifacts store: refreshed the scenario index from 1 changed feature file(s) (.canon/specs/kind=feature/area=cart/add.feature): 1 record(s) written
17
18
  ```
18
19
 
20
+ `canon status` and `canon gate check` are read commands and write nothing:
21
+ they grade the index as that refresh would leave it, built in memory, and
22
+ print no line.
23
+
19
24
  So the working loop has no sync step and no `canon format` step: write the
20
25
  scenario (`canon scenario new` writes it format-clean) and run the next
21
26
  command. Run `canon inventory sync` when:
22
27
 
23
- - a reader prints `NOT refreshed` — a root has a format violation (a
28
+ - a command prints `NOT refreshed` — a root has a format violation (a
24
29
  hand-written `Feature:`/`Scenario:` header with no `# canon:` provenance
25
30
  comment, an emptied file, …) or a duplicate id, so its files were not
26
31
  indexed. `canon gate check` then fails (`uncovered-cell <root>`),
@@ -40,8 +45,8 @@ not the index already holds its scenarios, `shipped` is refused, and
40
45
  fix it; run `canon migrate`, which moves `features/kind=feature/` to
41
46
  `<root>/kind=feature/`.
42
47
 
43
- `canon migrate --dry-run` prints the plan without writing anything, and
44
- `canon migrate` applies it. Commit first: it refuses while a path it
48
+ `canon migrate` prints the plan without writing anything, and
49
+ `canon migrate --commit --plan-digest <digest>` applies it. Commit first: it refuses while a path it
45
50
  moves has uncommitted changes, and when a destination already exists.
46
51
  A root at the repo-level `specs` moves to `.canon/specs`, and
47
52
  `canon.yaml` records `former_roots: [specs]`, so reviews pinned before
@@ -57,26 +62,26 @@ and the openspec source `canon init` wrote is removed only after every
57
62
  pointer brief is written and nothing unread is left under
58
63
  `openspec/changes/` (each unread directory is named).
59
64
 
60
- The reader's refresh treats a file as stale when its latest index
65
+ The refresh treats a file as stale when its latest index
61
66
  generation has another `source_digest` or `scenario_digest`. A
62
67
  generation written before 0.15 has no `scenario_digest`, so a repo that
63
68
  kept an old index gets it rewritten on the first read.
64
69
 
65
70
  ## Where the index sits in the loop
66
71
 
67
- ```
72
+ ```text
68
73
  author .feature corpus (canon scenario new)
69
74
  │
70
75
  ▼
71
- scenario index # refreshed by every reader; `canon inventory sync` rebuilds it
76
+ scenario index # refreshed by the writers; `canon inventory sync` rebuilds it
72
77
  │
73
78
  ▼
74
- canon review add # attests a review — see canon-inventory downstream / canon-gate
75
- canon divergence {add,promote,resolve,defer,accept,status} # tracks/resolves a divergence
79
+ canon review scenario # attests a review — see canon-inventory downstream / canon-gate
80
+ canon divergence add|resolve|defer|accept|list # tracks/resolves a divergence
76
81
  canon gate check [--release] # trust-ladder / staleness / coverage — see canon-gate
77
82
  │
78
83
  ▼
79
- canon ingest artifacts # with artifacts.native_records: true in canon.yaml —
84
+ canon ingest records # with artifacts.native_records: true in canon.yaml —
80
85
  # feeds review/divergence verdicts into the flywheel
81
86
  # (see canon-artifact-ingest)
82
87
  ```
@@ -94,7 +99,7 @@ canon inventory sync --spec-root ./specs # ad hoc: ignore canon.yaml entirely
94
99
 
95
100
  Per configured root, in order:
96
101
 
97
- 1. **Validate** — runs `canon format --check` over the root. ANY violation
102
+ 1. **Validate** — runs `canon format` over the root. ANY violation
98
103
  (missing provenance, layout-grammar, …) ABORTS THE WHOLE ROOT: zero
99
104
  records written for it, the violation(s) reported. Never a partial sync.
100
105
  2. **Scan** — walks `<root>/kind=feature/area=<area>/<surface>.feature`, pairing each
@@ -120,6 +125,13 @@ The scenario index shape is deliberately general: `title` +
120
125
  `source_digest`, nothing else. It carries no coverage field — coverage
121
126
  stays `canon gate check`'s own authority (see `canon-gate`).
122
127
 
128
+ `--dry-run` runs the same validation and reports what would be written
129
+ without writing. A rerun that finds every digest unchanged writes nothing.
130
+
131
+ **Done when:** `canon inventory sync` exits `0` with no violation or sync
132
+ error for the root, and `canon query --kind scenario` lists the scenarios;
133
+ `canon status` then reads the index without a `NOT refreshed` line.
134
+
123
135
  ## `specs.roots[]` config (`canon.yaml`)
124
136
 
125
137
  ```yaml
@@ -145,7 +157,7 @@ specs:
145
157
 
146
158
  ## Sync-abort causes
147
159
 
148
- - **A `canon format --check` validation violation** in the root's corpus →
160
+ - **A `canon format` validation violation** in the root's corpus →
149
161
  whole root aborts, 0 writes, violations reported.
150
162
  - **A root on the pre-0.17 layout** (a `.feature` file under
151
163
  `<root>/features/`) → a `layout-grammar` violation at path `features`;
@@ -156,12 +168,12 @@ specs:
156
168
 
157
169
  ## How the index feeds the rest
158
170
 
159
- - **Review** — `canon review add` attests a verdict against a synced
171
+ - **Review** — `canon review scenario` attests a verdict against a synced
160
172
  scenario. (See `canon-inventory` downstream commands and each command's `--help`.)
161
- - **Divergence** — `canon divergence {add,promote,resolve,defer,accept,status}`
173
+ - **Divergence** — `canon divergence add|resolve|defer|accept|list`
162
174
  tracks/resolves divergences against synced scenarios.
163
175
  - **Gate** — `canon gate check` enforces trust-ladder / staleness /
164
176
  coverage over the corpus (see `canon-gate`).
165
- - **Ingest** — `canon ingest artifacts` with `artifacts.native_records:
177
+ - **Ingest** — `canon ingest records` with `artifacts.native_records:
166
178
  true` in `canon.yaml` feeds review/divergence verdicts into the
167
179
  reward flywheel (see `canon-artifact-ingest`).