@journeykit/canon 0.35.0 → 0.37.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 (121) hide show
  1. package/dist/skills/SKILL.src.md +41 -20
  2. package/dist/skills/reference/canon-authoring.md +3 -2
  3. package/dist/skills/reference/canon-gate.md +29 -14
  4. package/dist/skills/reference/canon-inventory.md +3 -1
  5. package/dist/skills/reference/canon-learn.md +7 -2
  6. package/dist/skills/reference/canon-policy.md +3 -3
  7. package/dist/skills/reference/canon-report-dashboard.md +5 -5
  8. package/dist/skills/reference/canon-review.md +12 -9
  9. package/dist/skills/reference/canon-storage.md +4 -4
  10. package/dist/skills/reference/canon-subject.md +25 -23
  11. package/dist/skills/reference/role-implementer.md +8 -6
  12. package/dist/skills/reference/role-orchestrator.md +22 -21
  13. package/dist/skills/reference/role-planner.md +7 -7
  14. package/dist/skills/reference/role-reviewer.md +8 -8
  15. package/man/canon-adapter-authorize.1 +2 -2
  16. package/man/canon-adapter-validate.1 +2 -2
  17. package/man/canon-adapter.1 +2 -2
  18. package/man/canon-artifacts-move.1 +6 -6
  19. package/man/canon-artifacts-store.1 +2 -2
  20. package/man/canon-artifacts-verify.1 +2 -2
  21. package/man/canon-artifacts.1 +2 -2
  22. package/man/canon-brief-approve.1 +9 -6
  23. package/man/canon-brief-check.1 +2 -2
  24. package/man/canon-brief-show.1 +2 -2
  25. package/man/canon-brief.1 +2 -2
  26. package/man/canon-change-new.1 +2 -2
  27. package/man/canon-change.1 +2 -2
  28. package/man/canon-completion.1 +2 -2
  29. package/man/canon-context-pack-add.1 +2 -2
  30. package/man/canon-context-pack-show.1 +2 -2
  31. package/man/canon-context-pack-verify.1 +2 -2
  32. package/man/canon-context-pack.1 +2 -2
  33. package/man/canon-context.1 +2 -2
  34. package/man/canon-dashboard.1 +2 -2
  35. package/man/canon-demo-init.1 +2 -2
  36. package/man/canon-demo-review.1 +2 -2
  37. package/man/canon-demo.1 +2 -2
  38. package/man/canon-dispatch-begin.1 +2 -2
  39. package/man/canon-dispatch-diff.1 +2 -2
  40. package/man/canon-dispatch-end.1 +2 -2
  41. package/man/canon-dispatch.1 +2 -2
  42. package/man/canon-divergence-accept.1 +3 -3
  43. package/man/canon-divergence-add.1 +4 -4
  44. package/man/canon-divergence-defer.1 +4 -4
  45. package/man/canon-divergence-list.1 +3 -3
  46. package/man/canon-divergence-resolve.1 +3 -3
  47. package/man/canon-divergence.1 +2 -2
  48. package/man/canon-evidence-add.1 +9 -9
  49. package/man/canon-evidence-approval-payload.1 +4 -4
  50. package/man/canon-evidence-audit.1 +7 -7
  51. package/man/canon-evidence.1 +2 -2
  52. package/man/canon-export.1 +4 -4
  53. package/man/canon-feature-new.1 +2 -2
  54. package/man/canon-feature.1 +2 -2
  55. package/man/canon-finding-add.1 +5 -5
  56. package/man/canon-finding-close.1 +4 -6
  57. package/man/canon-finding-list.1 +2 -2
  58. package/man/canon-finding.1 +2 -2
  59. package/man/canon-format.1 +2 -2
  60. package/man/canon-gate-check.1 +2 -2
  61. package/man/canon-gate-install-hooks.1 +2 -2
  62. package/man/canon-gate-promote.1 +2 -2
  63. package/man/canon-gate.1 +2 -2
  64. package/man/canon-ingest-plans.1 +2 -2
  65. package/man/canon-ingest-records.1 +2 -2
  66. package/man/canon-ingest-sessions.1 +2 -2
  67. package/man/canon-ingest.1 +2 -2
  68. package/man/canon-init.1 +5 -4
  69. package/man/canon-inventory-sync.1 +2 -2
  70. package/man/canon-inventory.1 +2 -2
  71. package/man/canon-ledger-compact.1 +5 -5
  72. package/man/canon-ledger.1 +3 -3
  73. package/man/canon-migrate.1 +8 -8
  74. package/man/canon-plugin-sync.1 +2 -2
  75. package/man/canon-plugin.1 +2 -2
  76. package/man/canon-policy-approve.1 +2 -2
  77. package/man/canon-policy-show.1 +2 -2
  78. package/man/canon-policy.1 +2 -2
  79. package/man/canon-prompt-add.1 +2 -2
  80. package/man/canon-prompt-show.1 +2 -2
  81. package/man/canon-prompt.1 +2 -2
  82. package/man/canon-purge.1 +8 -8
  83. package/man/canon-query.1 +3 -3
  84. package/man/canon-regime-show.1 +2 -2
  85. package/man/canon-regime.1 +2 -2
  86. package/man/canon-report-build.1 +2 -2
  87. package/man/canon-report-check.1 +2 -2
  88. package/man/canon-report-snapshot.1 +2 -2
  89. package/man/canon-report.1 +2 -2
  90. package/man/canon-retrieve.1 +2 -2
  91. package/man/canon-review-change.1 +4 -4
  92. package/man/canon-review-scenario.1 +4 -4
  93. package/man/canon-review.1 +2 -2
  94. package/man/canon-scenario-new.1 +2 -2
  95. package/man/canon-scenario.1 +2 -2
  96. package/man/canon-schema.1 +10 -4
  97. package/man/canon-search.1 +2 -2
  98. package/man/canon-selftest.1 +2 -2
  99. package/man/canon-skills-check.1 +3 -3
  100. package/man/canon-skills-doctor.1 +3 -3
  101. package/man/canon-skills-install.1 +3 -3
  102. package/man/canon-skills-show.1 +2 -2
  103. package/man/canon-skills.1 +2 -2
  104. package/man/canon-status.1 +2 -2
  105. package/man/canon-strategy-approval-payload.1 +3 -3
  106. package/man/canon-strategy-promote.1 +2 -2
  107. package/man/canon-strategy-rollback.1 +3 -3
  108. package/man/canon-strategy.1 +2 -2
  109. package/man/canon-subject-adopt.1 +2 -2
  110. package/man/canon-subject-move.1 +2 -2
  111. package/man/canon-subject-new.1 +2 -2
  112. package/man/canon-subject-show.1 +2 -2
  113. package/man/canon-subject-unadopt.1 +3 -3
  114. package/man/canon-subject.1 +2 -2
  115. package/man/canon-task-done.1 +2 -2
  116. package/man/canon-task.1 +2 -2
  117. package/man/canon-tier-age.1 +5 -5
  118. package/man/canon-tier.1 +2 -2
  119. package/man/canon-trace.1 +2 -2
  120. package/man/canon.1 +3 -3
  121. package/package.json +4 -4
@@ -49,15 +49,15 @@ a feature, a one-line request included. A human is needed once per scope:
49
49
  `canon brief approve <slug> <slug> --approver <their name> --note "<their words>" --relayed-via <ask|AskUserQuestion|chat> --actor <your actor id> --digest <slug>=<digest shown>`
50
50
  (one or more slugs, each with its own `--digest`, one answer for all, nothing written if any is refused).
51
51
  The `Approved:` line then carries `(relayed by <role> <actor> via <tool>)`.
52
- A digest that no longer matches the brief is `RELAY_DIGEST_MISMATCH`: show
53
- it again. Without `--relayed-via` an agent role gets `HUMAN_ONLY`, and so
52
+ A digest that no longer matches the brief is `RELAY_DIGEST_STALE`: show
53
+ it again. Without `--relayed-via` an agent role gets `ACTOR_DENIED`, and so
54
54
  does a repo whose `canon.yaml` says `approval: { relay: deny }`: then the
55
55
  human runs the call themselves. Never remove or unset your role
56
56
  (`env -u CANON_ROLE`, dropping `--as`) to run it yourself.
57
57
  Never edit the policy to make the gate pass: a policy that differs from
58
- the approved one fails closed (`POLICY_CHANGED_SINCE_APPROVAL`, not
58
+ the approved one fails closed (`POLICY_APPROVAL_STALE`, not
59
59
  waivable); stop and ask the human. A brief edited after its
60
- approval is not approved (`BRIEF_CHANGED_SINCE_APPROVAL`): it needs a new
60
+ approval is not approved (`BRIEF_APPROVAL_STALE`): it needs a new
61
61
  answer. The ship guard and `canon evidence add` refuse an unapproved or
62
62
  edited brief, but those are backstops: the stop itself is at step 1.
63
63
  - **Approval is scoped.** Evidence for a scenario tagged `@flow:<change>.<F-id>`
@@ -120,14 +120,18 @@ the record carries, so a review of a working tree survives the commit made
120
120
  from it; a review is judged by what it pins (the scenario and what its
121
121
  evidence says), never by the commit or the code tree. Outside a git work
122
122
  tree, or in a repo with no commits, a command that needs a commit refuses
123
- `NO_COMMIT`.
123
+ `COMMIT_REQUIRED`.
124
124
 
125
125
  ### Evidence follows the code
126
126
 
127
127
  After a chore approval or a reopen, a scenario whose latest evidence was
128
128
  recorded on the current `codeTree` needs nothing new: a docs-only chore then
129
129
  needs its change-level review alone. What counts as code is `code.exclude` in
130
- `canon.yaml` (globs, default `docs/**` and `*.md`). When code did change,
130
+ `canon.yaml` (globs, default `docs/**` and `*.md`). A chore approved over the
131
+ code as it still is (the `(code tree <id>)` of its `Approved:` line is the
132
+ current tree) asks for no evidence at all, whatever tree the evidence is on, and
133
+ `status` says so; an approval with no recorded tree follows the rule above.
134
+ When code did change,
131
135
  `canon status`, `meta.next` and the refusal hint lead with one batch over
132
136
  every report, for example
133
137
  `canon evidence add --all-scenarios --report junit:a.xml --report junit:b.xml --role implementer`
@@ -181,7 +185,7 @@ names the stores not read.
181
185
 
182
186
  canon cannot detect a role; you declare it. Pass `--as <role>` or set
183
187
  `CANON_ROLE=planner|implementer|reviewer|orchestrator`; a flag wins over the
184
- variable, and an unknown value is refused (`UNKNOWN_ROLE`).
188
+ variable, and an unknown value is refused (`ROLE_INVALID`).
185
189
 
186
190
  - **A role is set, or you were spawned as one:** load only
187
191
  `canon skills show role-<role>` and do that role's steps. `canon status`
@@ -211,17 +215,34 @@ A role is guidance, not enforcement: `--role` on `evidence add` and
211
215
  your complete command set, every option of every leaf included. Run `canon schema <command> --json` only for a
212
216
  command it does not list, or after a `USAGE` refusal (read `error.field`
213
217
  and `error.expected` first). `canon schema --json` is the tree of every group and command
214
- with its `effect` and `x-canon.roles` (`--as <role>` lists one role's, and `x-canon.as` names it); `canon schema <group> --json` lists a group's
215
- commands; `canon schema <command> --json` is one command in full: `options` (each with
216
- `valueType`, a typed `default`, `possibleValues`, `env`, `flagOnly`), `errors`, `outputs`
217
- with the JSON Schema of each report in `schemas`, `input` (the schema of one `--input`
218
- document and a complete `example`) and `examples`. The first line of every `--help`
219
- points at it.
218
+ with its `effect` and `x-canon.roles` (`--as <role>` lists one role's, and `x-canon.as` names it);
219
+ its `options` define once the options that two or more commands share (`--repo`, `--as`, `--actor`, …)
220
+ and a command names them. `canon schema <group> --json` lists a group's
221
+ commands; `canon schema <command> --json` is the call surface of one command and nothing
222
+ else: `options` (each name of a tree option, or an object with `valueType`, a typed
223
+ `default`, `possibleValues`, `env`, `flagOnly`), `operands`, `constraints`, `validators`, `errors`,
224
+ `outputs`, `input` (what one `--input` document is and a complete `example`) and `examples`.
225
+ A member at its default is absent. `--json` is always accepted, `--input` when the command
226
+ has `input`, `--fields` when an output has `fields`, and the codes `USAGE` (with canon's
227
+ finer `*_INVALID`/`*_REQUIRED` argument codes), `INTERNAL`, `INPUT_*`, `BATCH_INCOMPLETE`,
228
+ `PLAN_STALE` (with `--commit`) and `CURSOR_INVALID` (with `--cursor`) can come without
229
+ being listed. `--commit` and `--plan-digest` are listed on exactly the commands that offer
230
+ them: a command that lists `commit` previews on a bare run. `canon schema <command> --schemas --json`
231
+ gives the JSON Schema of each report in `schemas`, `Input` (the schema of one `--input`
232
+ document) and `rejects` (what each validator refuses, in order); `#/schemas/<Name>` in
233
+ the command document resolves there. The first line of every `--help` points at it.
234
+ - An option of the same name takes the same value in every command that has it, and `canon schema` declares it once. An id
235
+ (`--project`, `--role`, `--domain`) is a kebab slug: lowercase letters and digits, single hyphens between them. A name
236
+ (`--actor`, `--approver`, `--relayed-via`) and a sentence (`--reason`, `--summary`, `--override-reason`) are one line of text:
237
+ the white space around it is trimmed (what is recorded, and what a signature is made of, is the trimmed text), and a value
238
+ that is empty once trimmed, or has a control character (a tab, an escape) or a line break inside it, is refused with the
239
+ command's own argument code. `--round` and `--limit` start at 1, and `--regime` is the four-part key `retrieve` and
240
+ `dispatch begin` both take. `finding close` takes `fixed`, `rejected` or `deferred`, never `open`.
220
241
  - Pass `--json` and read stdout as one document, nothing on stderr. Success is
221
242
  `{"ok":true,"data":…,"meta":…}`; failure is
222
243
  `{"ok":false,"error":{…},"meta":…}` (a check that fails keeps its report in
223
244
  `data`). `meta` carries `tool`, `toolVersion`, `contract` (`atip/1`),
224
- `contractVersion` (`1.2`), `schemaVersion`,
245
+ `contractVersion` (`1.5`), `schemaVersion`,
225
246
  `command`, and when present `next`, `warnings` (`[{code, message}]`) and
226
247
  `page`; a repeated write that changed nothing reports `data.unchanged: true`.
227
248
  Absent members are omitted, never `null` or `[]` (a value you sent empty comes back as `received: ""`).
@@ -243,7 +264,7 @@ A role is guidance, not enforcement: `--role` on `evidence add` and
243
264
  - **Exit codes:** only 0, 1 and 2 exist: 0 ok; 1 the command ran and its answer
244
265
  is negative or incomplete (a failed check, a violation, `unverifiable` when
245
266
  nothing was examined, a partly failed batch); 2 refused to run (usage,
246
- `CONFIG_MISSING`, an id or file that does not exist, an invalid argument).
267
+ `CONFIG_REQUIRED`, an id or file that does not exist, an invalid argument).
247
268
  A refusal leaves the tree unchanged.
248
269
  - Repeating a write is safe: a record that already exists with identical
249
270
  content is reported `unchanged` (`data.unchanged`), not duplicated.
@@ -258,13 +279,13 @@ A role is guidance, not enforcement: `--role` on `evidence add` and
258
279
  has blockers; the approval step appears only when the brief is clean and
259
280
  unapproved.
260
281
  - Page long lists with `--limit <n>` and `--cursor <nextCursor>` (`--all`
261
- for everything); a cursor holds only for the call that issued it, so change nothing but `--limit`, `--all`, `--fields` and `--json`, and keep `CANON_ROLE` as it was (`CURSOR_INVALID` with `error.reason` `ARGUMENTS_CHANGED` otherwise; `FOREIGN` for a cursor of another command or another canon version: start again from the first page). `--limit`, `--cursor`, `--all` and `--fields` are flags: an `--input` document key of that name is `INPUT_INVALID` with `reason` `FLAG_ONLY_KEY`. Project fields with `--fields a,b`
282
+ for everything); a cursor holds only for the call that issued it, so change nothing but `--limit`, `--all`, `--fields` and `--json`, and keep `CANON_ROLE` as it was; the next call is `meta.next[0].argv` as it stands (the arguments you gave and the cursor, never a default). An argument you add is a change even when it states the default (`--repo .`): canon binds an explicit default to the cursor (`CURSOR_INVALID` with `error.reason` `ARGUMENTS_CHANGED`, `error.field` `--cursor`; `FOREIGN` for a cursor of another command or another canon version: start again from the first page). `--limit`, `--cursor`, `--all` and `--fields` are flags: an `--input` document key of that name is `INPUT_INVALID` with `reason` `FLAG_ONLY_KEY`. Project fields with `--fields a,b`
262
283
  (`status`, `export`, `query`, `finding list`, `divergence list`, `context`): the paths `canon schema <command> --json` lists under `outputs[].fields`, spelled as the answer spells them; `meta.fields` lists what was kept.
263
284
  - `--override-reason <text>` is the only way to proceed past a waivable
264
285
  refusal; it is recorded, echoed in `data.overrides[]` and listed by `status`
265
286
  for the human. There is no `--force`. `brief approve`, `policy approve`,
266
287
  `divergence accept` and the `migrate --commit` that re-binds `Approved:`
267
- lines record a human's decision and refuse every agent role (`HUMAN_ONLY`)
288
+ lines record a human's decision and refuse every agent role (`ACTOR_DENIED`)
268
289
  unless they record the explicit answer the human gave you, with
269
290
  `--relayed-via <tool>` (show, ask, record, as for the brief; `canon skills
270
291
  show role-planner`); never unset your role to run one.
@@ -281,8 +302,8 @@ before anything is written, and that check reads no state: an invalid element wr
281
302
  its own code, `error.field` pointing at `/<i>/<key>` and no `data`. An element that fails when it runs (the object
282
303
  it names does not exist) stops the batch: exit 1, `BATCH_INCOMPLETE`, `error.field` exactly `/<i>`, the element
283
304
  `failed` with its own `error` (no `data`), the later ones `skipped`, and the earlier ones done (not undone). Re-running the same batch reports the finished elements
284
- `unchanged`. A typo in a key is refused (`INPUT_INVALID`, `field` naming the key), never dropped; a document with several problems reports them all, the first as the error and every one in `error.details` in the order the document wrote its keys, and a value outside a closed set is `INPUT_INVALID` too (`reason` `INVALID_VALUE`, `field` the key), not `USAGE`. Every non-empty line of an NDJSON file is a document (a `#` line is not a comment). Integers compare by number: `1`, `"1"` and `--round +1` are one value, and `"+1"`, `"0x1"` and `1.0` in a document are `INVALID_VALUE`.
285
- `canon schema <command> --json` shows the leaf's `input`: the schema of one document and a complete `example`.
305
+ `unchanged`. A typo in a key is refused (`INPUT_INVALID`, `field` naming the key), never dropped; a document with several problems reports them all, the first as the error (`field` is the first `error.details` entry's) and every one in `error.details` in the order the document wrote its keys, and a value outside a closed set is `INPUT_INVALID` too (`reason` `INVALID_VALUE`, `field` the key), not `USAGE`. Every non-empty line of an NDJSON file is a document (a `#` line is not a comment). Integers compare by number: `1`, `"1"` and `--round +1` are one value, `"+1"`, `"0x1"` and `1.0` in a document are `INVALID_VALUE`, and an integer beyond ±9007199254740991 is refused as a number and as a string, in a document (`INVALID_VALUE`, `field` its pointer) and on the command line (`USAGE`). `--input` may give a required option and the command line only the operand; an option given in both with different values is `INPUT_CONFLICT` at the key's pointer. A repeatable option's none is the key left out: `[]` is refused (`INPUT_INVALID`, `reason` `INVALID_VALUE`, `field` the key's pointer), as is `[]` for any other option and for `operands`.
306
+ `canon schema <command> --json` shows the leaf's `input` (what one document is, and a complete `example`); `canon schema <command> --schemas --json` gives `Input`, the JSON Schema of one document.
286
307
 
287
308
  ```text
288
309
  canon scenario new --input '[{"operands":["auth.login.01"],"title":"Logs in","case":"happy"},{"operands":["auth.login.02"],"title":"Wrong password","case":"failure"}]'
@@ -338,7 +359,7 @@ canon init --providers=claude,codex
338
359
  canon skills install
339
360
  # any subset or none; rewrites skills.providers
340
361
  canon skills install --providers=claude,codex,omp,pi
341
- # read-only drift check; exit 1 (SKILLS_DRIFT) names each orphan, a projected file the bundle no longer ships
362
+ # read-only drift check; exit 1 (SKILLS_CHECK_FAILED) names each orphan, a projected file the bundle no longer ships
342
363
  canon skills check
343
364
  # diagnostics, incl. a stray canon projection for an unselected provider and a projection a formatter rewrote, and `orphan:` lines for projected files the bundle no longer ships; never deletes user files
344
365
  canon skills doctor
@@ -114,8 +114,9 @@ Fix it and the next command indexes the files (see `canon-inventory`).
114
114
  Many scenarios at once: `--input` takes inline JSON, a file or `-`. An
115
115
  array (or NDJSON) is a batch, validated whole before anything is written;
116
116
  each element holds `operands` (the scenario tag) and the other options as
117
- camelCase keys. `canon schema scenario new --json` shows `input`: the schema of one document
118
- and a complete `example`. There is no `--from`.
117
+ camelCase keys. `canon schema scenario new --json` shows `input` (its limits and a complete
118
+ `example`); `canon schema scenario new --schemas --json` gives `Input`, the schema of one
119
+ document. There is no `--from`.
119
120
 
120
121
  ```text
121
122
  canon scenario new --input '[{"operands":["checkout.cart.01"],"title":"Adding an item raises the count","subject":"checkout-core","lane":"behavior","case":"happy"},{"operands":["checkout.cart.04"],"title":"Adding an out-of-stock item is refused","subject":"checkout-core","lane":"behavior","case":"failure"}]'
@@ -28,7 +28,7 @@ Every violation carries one of these stable, grep-able strings:
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
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). |
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`, `EVIDENCE_NOTE_INVALID`, exit 2). |
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
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
 
@@ -225,9 +225,9 @@ canon divergence accept cart.add.01 --project root --reason "known gap" --tracki
225
225
 
226
226
  Accepting a scenario whose latest evidence is `divergent` as a known gap
227
227
  limited by something outside the change is a human-only act: with a role
228
- declared (`--as`, `CANON_ROLE`) the leaf is refused with `HUMAN_ONLY`. It is
228
+ declared (`--as`, `CANON_ROLE`) the leaf is refused with `ACTOR_DENIED`. It is
229
229
  never a way to skip a fix.
230
- Refused (`DIVERGENCE_ACCEPT_REFUSED`) unless the latest verdict is
230
+ Refused (`DIVERGENCE_CONFLICT` when the evidence is not divergent, `DIVERGENCE_DENIED` when the actor or session attested it) unless the latest verdict is
231
231
  divergent and the session and actor differ from the evidence's. The record
232
232
  pins that evidence: new evidence for the scenario, or `--expiry` passing,
233
233
  ends it. While it holds, the ship guard counts the cell covered and
@@ -255,6 +255,17 @@ review of the chore itself (`canon review change <slug>`) before
255
255
  `--to shipped`; a changed verdict or case set stales that scenario's
256
256
  review as before (and a new `divergence accept`, where the gap remains).
257
257
 
258
+ A chore's approval records the code tree of the work tree at that moment, as
259
+ `(code tree <id>)` in its `Approved:` line (outside the brief digest; none
260
+ outside git or with no commit). While that tree is the current code tree the
261
+ chore changed no code since it was approved, so it asks for no evidence at
262
+ all, whatever tree each scenario's latest evidence is on, and `status` says so
263
+ on its line; its change-level review is still asked, and it must be a review of
264
+ this code (the review records the code tree it saw): a brief edited after the
265
+ approval needs a new review, and so does a review of other code, as when the
266
+ same brief is approved again over code that came after the review. An approval
267
+ with no recorded tree (written before 0.37) follows the rule above.
268
+
258
269
  ### Audit records (`canon evidence audit`)
259
270
 
260
271
  canon never generates or runs an audit. An external evaluator does, and
@@ -304,10 +315,10 @@ never rides along into a confirming audit. `--drop-attachment` and
304
315
  The approval is never assumed: the agent writes the brief, shows it to the
305
316
  human and asks, and builds nothing for that scope until the human answers.
306
317
  `canon evidence add` is the backstop for that rule (0.16 M7). It
307
- refuses (exit `1`, nothing staged or stored) while a subject the record
318
+ refuses (exit `2`, nothing staged or stored) while a subject the record
308
319
  attests for has an enforced, unapproved brief: a change the record
309
320
  answers to (below) has a canon `.canon/changes/<change>/brief.md` with no `Approved:`
310
- line, was edited after its approval (`BRIEF_CHANGED_SINCE_APPROVAL`), or
321
+ line, was edited after its approval (`BRIEF_APPROVAL_STALE`), or
311
322
  there is no canon `brief.md` yet but the change comes from an
312
323
  external openspec plan with a `proposal.md` under the plan sources (the
313
324
  check `canon status` and the ship guard use; see
@@ -461,7 +472,7 @@ mentions. Each bound case is its own attachment with its name and
461
472
  outcome. `--report-case <name>` (repeatable; a Rust test by its
462
473
  function name) ADDS cases; it never hides the scenario's own. No match,
463
474
  or a `--report-case` naming no case, refuses (exit 2); a `faithful`
464
- verdict when ANY bound case failed or was skipped refuses (exit 1). Files
475
+ verdict when ANY bound case failed or was skipped refuses (exit 2). Files
465
476
  must be inside the repository, are recorded by relative path, and are
466
477
  stored (see Bound evidence stays provable).
467
478
 
@@ -517,8 +528,9 @@ report (a JUnit test case's `[[ATTACHMENT|path]]` lines, which Playwright's
517
528
  JUnit reporter writes; a Cucumber JSON embedding), so a test's screenshot
518
529
  stays with its scenario. A file the report does not name goes with
519
530
  `--artifact <scenario-id>=<path>`; a bare `--artifact <path>` in a batch is
520
- refused (`EVIDENCE_INVALID_ARGUMENT`). A report attachment that is missing,
521
- outside the repo or oversized is refused.
531
+ refused (`EVIDENCE_ARGUMENT_CONFLICT`). A report attachment that is missing,
532
+ outside the repo or oversized is refused (`EVIDENCE_BINDING_NOT_FOUND`,
533
+ `EVIDENCE_BINDING_DENIED`, `EVIDENCE_BINDING_INVALID`).
522
534
 
523
535
  It is all or nothing: a claimed `faithful` verdict over a failed case,
524
536
  a fabrication marker in `--summary`, an unapproved brief for any
@@ -536,13 +548,14 @@ or `-`; an array (or NDJSON) is a batch, validated element by element before
536
548
  the first write (a refusal writes nothing, exit 2). Keys are the option
537
549
  names in camelCase plus `operands`: one object per scenario, with
538
550
  `scenario`, `class`, `ref` and `role`. `canon schema evidence add --json`
539
- shows `input`: the schema of one document and a complete `example`. Each element reports `written`,
551
+ shows `input` (its limits and a complete `example`) and `canon schema evidence add --schemas --json`
552
+ gives `Input`, the schema of one document. Each element reports `written`,
540
553
  `unchanged` or `failed` in `data.items[]`.
541
554
 
542
555
  `--rev <rev>` names the commit the evidence ran against (default `HEAD`).
543
556
  Evidence for uncommitted code is recorded with its `codeTree` and
544
557
  `worktreeDirty`; outside git, or with no commit, the add is refused with
545
- `NO_COMMIT`.
558
+ `COMMIT_REQUIRED`.
546
559
 
547
560
  **Done when:** `canon gate check` shows no violation for the scenarios you
548
561
  recorded evidence for; each record's own output line says whether it was
@@ -697,7 +710,7 @@ aggregates every record bound to the task, on one line:
697
710
  (the latest `--summary`, or a default naming the latest passing record).
698
711
  Every other path — missing evidence, a `Divergent` verdict, a fabricated
699
712
  note on any of the task's records — leaves the row byte-unchanged and
700
- exits `1` with the blocking violation on stderr. After a flip, the plan
713
+ exits `2` with the blocking violation on stderr. After a flip, the plan
701
714
  source is re-ingested so the record store's task status agrees with the
702
715
  checkbox (`canon query --kind task` reads `done`); a failed re-ingest is
703
716
  a stderr warning naming `canon ingest plans`, never a failed flip.
@@ -734,7 +747,7 @@ Staging → committed: every well-formed record under
734
747
  applies, assigned a monotonic per-(role, surface) `run_seq` (gap-free
735
748
  within one invocation, continuing from the committed max), and moved into
736
749
  the committed ledger. A malformed or unpartitionable candidate is refused
737
- — exit `1`, no `run_seq` consumed, the file left in place. `--dry-run`
750
+ — exit `2` (`PROMOTION_INVALID`; several are `error.details[]`), no `run_seq` consumed, the file left in place. A refusal carries no `data`. `--dry-run`
738
751
  prints the plan (target path + assigned `run_seq` per candidate) without
739
752
  writing or deleting.
740
753
 
@@ -789,8 +802,10 @@ It installs no git hook: copy the script into git's `pre-commit` hook
789
802
  agent hook files and the script, also under `--dry-run`. In `--json`,
790
803
  `installed` is true when an agent hook file changed and
791
804
  `preCommitScriptWritten` when the script was written. A `--repo` that is not an
792
- existing directory exits 2 with `HOOKS_INSTALL_FAILED` (also under `--dry-run`)
793
- and creates nothing.
805
+ existing directory exits 2 with `HOOKS_REPO_INVALID` (also under `--dry-run`)
806
+ and creates nothing. When the install writes the first hook file and then cannot
807
+ write the second, it exits 1 with `HOOKS_INSTALL_INCOMPLETE`: the first file
808
+ stays, `meta.next` leads with `canon status`, then run the install again.
794
809
 
795
810
  Prefer this over hand-editing `settings.json`/`hooks.json`.
796
811
 
@@ -164,7 +164,9 @@ specs:
164
164
  whole root aborts, 0 writes, violations reported.
165
165
  - **A root on the pre-0.17 layout** (a `.feature` file under
166
166
  `<root>/features/`) → a `layout-grammar` violation at path `features`;
167
- the root aborts, 0 writes, exit 1. Run `canon migrate`. An empty
167
+ the root aborts, 0 writes, exit 2 (`INVENTORY_INVALID`, each violation in
168
+ `error.details`; exit 1 `INVENTORY_INCOMPLETE` when an earlier root was
169
+ indexed first). Run `canon migrate`. An empty
168
170
  leftover `features/` directory is not a refusal.
169
171
  - **A duplicate `scenario_id`** within one root → that root aborts, 0
170
172
  writes, reported as a sync error (a distinct lane from fmt violations).
@@ -52,7 +52,11 @@ The production activation path is a signed, paired-evaluation workflow:
52
52
  `meta.next` names the human signing step. The `data` contains the evaluation digest, RFC3339 `approved_at`,
53
53
  subject `strategy:<strategy_id>`, namespace
54
54
  `canon-learning-approval-v1`, and `payload_hex`. `approval-payload` never
55
- reads a private key and never sets `verified`.
55
+ reads a private key and never sets `verified`. `--approval-at` is an RFC 3339
56
+ date-time (`2026-10-02T12:00:00Z`; a date alone, white space around it and a
57
+ one-digit month are refused): canon never stamps it, so fix it once and give
58
+ the same value to the signature and to the record. The bytes hold the instant
59
+ as UTC to the nanosecond, so one instant written `Z` or `+09:00` is one payload.
56
60
  6. Decode `payload_hex` to the exact canonical JSON bytes. The signed object
57
61
  is:
58
62
 
@@ -190,7 +194,8 @@ canon strategy rollback <strategy_id> --reason "<reason>" --actor alice \
190
194
 
191
195
  `--signature-file` is an armored SSH detached signature over the
192
196
  `canon-learning-approval-v1` payload bound to
193
- `strategy:<id>:rollback:<sha256(reason)>`, actor, and `--approval-at`.
197
+ `strategy:<id>:rollback:<sha256(reason)>`, actor, and `--approval-at` (the same
198
+ RFC 3339 date-time that was signed; another instant fails the signature).
194
199
  `actor` MUST be listed in policy-pinned `approval.allowed_signers`; verification
195
200
  uses that file and `ssh-keygen -Y verify`. Successful rollback writes durable
196
201
  provenance, sets lifecycle `rolled_back`, and records the optional contradicting
@@ -76,7 +76,7 @@ A human approves the policy after reviewing `.canon/policy.yaml`, in the
76
76
  same answer that approves a subject's brief (0.16; see the skill's working
77
77
  loop). `canon brief approve` records both in one call. `canon policy
78
78
  approve` is a human's decision: with a role declared (`--as` or `CANON_ROLE`)
79
- it is refused with `HUMAN_ONLY` (exit 2, not retryable) unless it records the
79
+ it is refused with `ACTOR_DENIED` (exit 2, not retryable) unless it records the
80
80
  answer the human gave in the conversation: show them `.canon/policy.yaml`, ask,
81
81
  and on an explicit approval run it (the steps are in `canon skills show role-planner`) with `--relayed-via <tool>`, `--actor`,
82
82
  `--approver` and `--note` (their name and words); the file then carries `relay`
@@ -117,8 +117,8 @@ unreviewed-promotion policy — policy changed since approval: .canon/policy.yam
117
117
  ```
118
118
 
119
119
  `canon gate check` exits `1`; `canon subject move` (the ship guard and
120
- the reopen) refuses with `POLICY_CHANGED_SINCE_APPROVAL`, or
121
- `POLICY_APPROVAL_UNREADABLE` for an unreadable approval, and
120
+ the reopen) refuses with `POLICY_APPROVAL_STALE`, or
121
+ `POLICY_APPROVAL_CORRUPT` (or `POLICY_APPROVAL_DENIED` when the file may not be read) for a broken approval, and
122
122
  `--override-reason` does not waive either. A policy never approved stays
123
123
  an advisory. `canon status` prints the same text as a `warning:` line (and
124
124
  in `--json` `warnings`). The record names who approved which bytes and
@@ -143,7 +143,7 @@ canon dashboard --snapshot /tmp/canon-snap --port 8080
143
143
  - Runs in any repo: the released binary carries the app (in canon's own
144
144
  checkout a built `packages/dashboard/dist` is used instead). It prints the
145
145
  panels URL and a briefs URL (`/briefs.html`), then serves until Ctrl-C.
146
- A taken port fails with `DASHBOARD_PORT_IN_USE`: pass `--port <n>` or
146
+ A taken port fails with `DASHBOARD_PORT_UNAVAILABLE`: pass `--port <n>` or
147
147
  `--port 0`.
148
148
  - Without the `duckdb` CLI on `PATH` the server still runs; the panels page
149
149
  says why there is no snapshot and how to get one (it never shows demo
@@ -160,11 +160,11 @@ answer once and clicks Approve: one request carries every selected brief with
160
160
  the sha256 of the file the page read, and records exactly what `canon brief
161
161
  approve <id>+ --approver <name> --note <text>` records, the `Approved:` line
162
162
  bound to the brief digest and the policy approval, for each. A brief that
163
- changed after the page loaded (`BRIEF_CHANGED_SINCE_PAGE_LOAD`) or that has
164
- blockers refuses the whole request (409 `BRIEF_APPROVE_REFUSED`, naming each
165
- refused brief): nothing is written and the page says to read it again. A request
163
+ changed after the page loaded (`BRIEF_PAGE_STALE`) or that has
164
+ blockers refuses the whole request (409, the first refused brief's own code such as `BRIEF_INVALID`, with an
165
+ `error.details` entry for each refused brief): nothing is written and the page says to read it again. A request
166
166
  naming one brief the old way (`changeId`, `sha256`) is still accepted. The server is localhost only and
167
- refuses requests from other origins (`DASHBOARD_ORIGIN_REFUSED`).
167
+ refuses requests from other origins (`DASHBOARD_REQUEST_DENIED`).
168
168
 
169
169
  For an agent: when clean briefs are waiting, show them (`canon brief show
170
170
  <slug> --body`), ask with the harness question tool, and tell the human they
@@ -89,7 +89,7 @@ in the JSON. Give the reason when you reject or defer: it is the only record
89
89
  of why the finding was not fixed.
90
90
 
91
91
  `--scenario` (repeatable, each a scenario the index knows; refused with
92
- `FINDING_UNKNOWN_SCENARIO` otherwise) names the scenarios the finding is
92
+ `SCENARIO_NOT_FOUND` otherwise) names the scenarios the finding is
93
93
  about. After a `fixed` close, fresh evidence for those scenarios alone
94
94
  re-verifies the fix; a finding naming none asks every evidenced scenario of
95
95
  the subject, as before. `finding close` copies the list.
@@ -116,14 +116,15 @@ needs it or `--introduced-unknown` (never both). A revision that names no
116
116
  commit is refused (exit `2`). A finding recorded against uncommitted code
117
117
  is allowed: the record carries the `codeTree` it saw and `worktreeDirty`.
118
118
  Outside git, or in a repository with no commit, the write is refused with
119
- `NO_COMMIT`.
119
+ `COMMIT_REQUIRED`.
120
120
 
121
121
  Many findings at once: `--input` takes inline JSON, a file or `-`; an array
122
122
  (or NDJSON) is a batch validated before any write. Keys are the option
123
123
  names in camelCase (`change`, `actor`, `resolutionRev`); `canon schema
124
- finding add --json` shows `input`: the schema of one document and a complete `example`. An identical
124
+ finding add --json` shows `input` (its limits and a complete `example`) and `canon schema finding add
125
+ --schemas --json` gives `Input`, the schema of one document. An identical
125
126
  repeat is `unchanged` (`data.unchanged`) and writes nothing; a different
126
- finding under an occupied key is `FINDING_SEQ_OCCUPIED`.
127
+ finding under an occupied key is `FINDING_EXISTS`.
127
128
 
128
129
  ### A defect the author found in its own work
129
130
 
@@ -154,7 +155,7 @@ text in the summary, not a field.
154
155
 
155
156
  | Disposition | Meaning |
156
157
  |---|---|
157
- | `open` | Raised and not yet dealt with. The default on `finding add`; `finding close --disposition open` reopens. |
158
+ | `open` | Raised and not yet dealt with. The default on `finding add`; `finding close` refuses it (a defect that came back is a new finding). |
158
159
  | `fixed` | Fixed by a commit. Carries `--resolution-rev <rev>` (default `HEAD`) naming a commit this repo holds; only `fixed` may carry one. |
159
160
  | `rejected` | The reviewer's point was considered and declined. Needs `--reason`. |
160
161
  | `deferred` | Accepted, but postponed to later work. Needs `--reason`. |
@@ -163,8 +164,10 @@ text in the summary, not a field.
163
164
 
164
165
  Every reader takes the **latest version per natural key**
165
166
  `(change_id, round, seq)` — the same fold the report's finding panel
166
- uses. A blocker raised `open` and later closed `fixed` counts as fixed; a
167
- fixed finding reopened with `--disposition open` counts as open again.
167
+ uses. A blocker raised `open` and later closed `fixed` counts as fixed.
168
+ A finding is not reopened: `finding close` takes `fixed`, `rejected` or
169
+ `deferred` and refuses `--disposition open`; a defect that came back is a new
170
+ `finding add` (the next `--seq`).
168
171
 
169
172
  ## `canon review scenario`
170
173
 
@@ -197,7 +200,7 @@ saw (the tree with `.canon/`, the spec roots and `code.exclude` removed), and
197
200
  allowed, and the commit made from it has the same code tree, so committing it
198
201
  afterwards leaves the review as it was: a review is judged by its pins (the
199
202
  scenario and what its evidence says), not by the commit. Outside git, or
200
- with no commit, the write is refused with `NO_COMMIT`. An identical review
203
+ with no commit, the write is refused with `COMMIT_REQUIRED`. An identical review
201
204
  written again is `unchanged` (`data.unchanged` under `--json`) and adds no
202
205
  record.
203
206
 
@@ -475,7 +478,7 @@ stderr: canon subject move: review guard for building → verifying (spec_covera
475
478
 
476
479
  A target status outside the scope prints `skipped unreviewed-promotion —
477
480
  `specced` is not in require_review.scope`. A transition into an in-scope
478
- status with gaps prints the violations, exits 1, leaves the record
481
+ status with gaps prints the violations, exits 2, leaves the record
479
482
  unchanged, and names `--override-reason`.
480
483
 
481
484
  `--override-reason "<one line>"` waives ONLY `unreviewed-promotion` and
@@ -192,7 +192,7 @@ section listing them and emits a matching stderr `WARN`.
192
192
 
193
193
  ## Retention, export, and access audit
194
194
 
195
- `canon purge --kind <kind> --before <RFC3339> [--commit --plan-digest <planDigest>] [--repo <dir>] [--json]`
195
+ `canon purge --kind <kind> --before <RFC3339 date or date-time> [--commit --plan-digest <planDigest>] [--repo <dir>] [--json]`
196
196
  is the canonical data-lifecycle boundary: the bare run previews: it prints the removal set and
197
197
  its `planDigest` and deletes nothing; `--commit --plan-digest <planDigest>` deletes only if
198
198
  the plan recomputed now has the same digest (`PLAN_STALE`, exit 2, when the
@@ -220,7 +220,7 @@ human's review and `--commit --plan-digest`), and an export's page state (`total
220
220
  file and answers with `data: { output, kind, recordCount }`; `--dry-run`
221
221
  answers with what it would write and writes nothing.
222
222
 
223
- `canon export --kind <kind> [--before <RFC3339>] [--after <RFC3339>]
223
+ `canon export --kind <kind> [--before <RFC3339 date or date-time>] [--after <RFC3339 date or date-time>]
224
224
  [--output <file>] [--json] [--limit <n> | --all] [--cursor <c>]` emits a versioned manifest containing source scope,
225
225
  counts, digests, and redacted metadata. It does not authorize backend access;
226
226
  credentials remain an operator prerequisite. New output files are mode
@@ -295,7 +295,7 @@ answer; without it init takes the harnesses whose
295
295
  `.claude`, `.codex`, `.omp` or `.pi` directory exists, and with none detected
296
296
  it writes only a commented example
297
297
  and installs nothing. `.agents` alone never selects Codex. `canon skills
298
- install|check|doctor` follow the section; `install` prunes projected files the bundle no longer ships when their bytes match the lock (a modified one stays, reported as `kept:`), `check` exits 1 (`SKILLS_DRIFT`) naming each orphan and `doctor` prints `orphan:` lines; `canon skills install
298
+ install|check|doctor` follow the section; `install` prunes projected files the bundle no longer ships when their bytes match the lock (a modified one stays, reported as `kept:`), `check` exits 1 (`SKILLS_CHECK_FAILED`) naming each orphan and `doctor` prints `orphan:` lines; `canon skills install
299
299
  --providers=<list>` rewrites it in place. Without the section they use the
300
300
  install lock's providers, then the detected ones; nothing undetected is ever
301
301
  added. A present section that is not this shape, or names another harness,
@@ -395,7 +395,7 @@ so a killed commit never leaves it held, and the file is removed only by a
395
395
  call that made it to take the lock and then ended refused (a refusal leaves
396
396
  no file and no `.git/info/exclude` line behind). A
397
397
  command that waited 30 s for it starts nothing: it exits `2` with
398
- `REPO_WRITE_BUSY` (retryable; run it again in a moment). The first
398
+ `REPO_LOCK_UNAVAILABLE` (retryable; run it again in a moment). The first
399
399
  call that needs the lock makes the file, in any repository.
400
400
 
401
401
  ## What `canon init` keeps out of git