@journeykit/canon 0.34.0 → 0.36.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 (126) hide show
  1. package/dist/skills/SKILL.src.md +47 -30
  2. package/dist/skills/reference/canon-artifact-ingest.md +6 -3
  3. package/dist/skills/reference/canon-authoring.md +9 -5
  4. package/dist/skills/reference/canon-context.md +6 -3
  5. package/dist/skills/reference/canon-fmt.md +2 -1
  6. package/dist/skills/reference/canon-gate.md +29 -21
  7. package/dist/skills/reference/canon-inventory.md +16 -11
  8. package/dist/skills/reference/canon-plan-import.md +6 -3
  9. package/dist/skills/reference/canon-plugins.md +12 -6
  10. package/dist/skills/reference/canon-policy.md +3 -3
  11. package/dist/skills/reference/canon-report-dashboard.md +11 -8
  12. package/dist/skills/reference/canon-review.md +8 -7
  13. package/dist/skills/reference/canon-session-ingest.md +12 -6
  14. package/dist/skills/reference/canon-storage.md +47 -17
  15. package/dist/skills/reference/canon-subject.md +45 -35
  16. package/dist/skills/reference/role-implementer.md +33 -33
  17. package/dist/skills/reference/role-orchestrator.md +83 -77
  18. package/dist/skills/reference/role-planner.md +28 -28
  19. package/dist/skills/reference/role-reviewer.md +29 -29
  20. package/man/canon-adapter-authorize.1 +2 -2
  21. package/man/canon-adapter-validate.1 +2 -2
  22. package/man/canon-adapter.1 +2 -2
  23. package/man/canon-artifacts-move.1 +6 -6
  24. package/man/canon-artifacts-store.1 +2 -2
  25. package/man/canon-artifacts-verify.1 +2 -2
  26. package/man/canon-artifacts.1 +2 -2
  27. package/man/canon-brief-approve.1 +5 -4
  28. package/man/canon-brief-check.1 +2 -2
  29. package/man/canon-brief-show.1 +2 -2
  30. package/man/canon-brief.1 +2 -2
  31. package/man/canon-change-new.1 +2 -2
  32. package/man/canon-change.1 +2 -2
  33. package/man/canon-completion.1 +2 -2
  34. package/man/canon-context-pack-add.1 +2 -2
  35. package/man/canon-context-pack-show.1 +2 -2
  36. package/man/canon-context-pack-verify.1 +2 -2
  37. package/man/canon-context-pack.1 +2 -2
  38. package/man/canon-context.1 +2 -2
  39. package/man/canon-dashboard.1 +2 -2
  40. package/man/canon-demo-init.1 +2 -2
  41. package/man/canon-demo-review.1 +2 -2
  42. package/man/canon-demo.1 +2 -2
  43. package/man/canon-dispatch-begin.1 +2 -2
  44. package/man/canon-dispatch-diff.1 +2 -2
  45. package/man/canon-dispatch-end.1 +2 -2
  46. package/man/canon-dispatch.1 +2 -2
  47. package/man/canon-divergence-accept.1 +2 -2
  48. package/man/canon-divergence-add.1 +3 -3
  49. package/man/canon-divergence-defer.1 +3 -3
  50. package/man/canon-divergence-list.1 +2 -2
  51. package/man/canon-divergence-resolve.1 +3 -3
  52. package/man/canon-divergence.1 +2 -2
  53. package/man/canon-evidence-add.1 +5 -5
  54. package/man/canon-evidence-approval-payload.1 +3 -3
  55. package/man/canon-evidence-audit.1 +4 -4
  56. package/man/canon-evidence.1 +2 -2
  57. package/man/canon-export.1 +2 -2
  58. package/man/canon-feature-new.1 +2 -2
  59. package/man/canon-feature.1 +2 -2
  60. package/man/canon-finding-add.1 +3 -3
  61. package/man/canon-finding-close.1 +2 -2
  62. package/man/canon-finding-list.1 +2 -2
  63. package/man/canon-finding.1 +2 -2
  64. package/man/canon-format.1 +2 -2
  65. package/man/canon-gate-check.1 +2 -2
  66. package/man/canon-gate-install-hooks.1 +2 -2
  67. package/man/canon-gate-promote.1 +2 -2
  68. package/man/canon-gate.1 +2 -2
  69. package/man/canon-ingest-plans.1 +2 -2
  70. package/man/canon-ingest-records.1 +2 -2
  71. package/man/canon-ingest-sessions.1 +2 -2
  72. package/man/canon-ingest.1 +2 -2
  73. package/man/canon-init.1 +7 -7
  74. package/man/canon-inventory-sync.1 +2 -2
  75. package/man/canon-inventory.1 +2 -2
  76. package/man/canon-ledger-compact.1 +5 -5
  77. package/man/canon-ledger.1 +3 -3
  78. package/man/canon-migrate.1 +8 -8
  79. package/man/canon-plugin-sync.1 +2 -2
  80. package/man/canon-plugin.1 +2 -2
  81. package/man/canon-policy-approve.1 +2 -2
  82. package/man/canon-policy-show.1 +2 -2
  83. package/man/canon-policy.1 +2 -2
  84. package/man/canon-prompt-add.1 +2 -2
  85. package/man/canon-prompt-show.1 +2 -2
  86. package/man/canon-prompt.1 +2 -2
  87. package/man/canon-purge.1 +7 -7
  88. package/man/canon-query.1 +2 -2
  89. package/man/canon-regime-show.1 +2 -2
  90. package/man/canon-regime.1 +2 -2
  91. package/man/canon-report-build.1 +2 -2
  92. package/man/canon-report-check.1 +2 -2
  93. package/man/canon-report-snapshot.1 +2 -2
  94. package/man/canon-report.1 +2 -2
  95. package/man/canon-retrieve.1 +2 -2
  96. package/man/canon-review-change.1 +3 -3
  97. package/man/canon-review-scenario.1 +3 -3
  98. package/man/canon-review.1 +2 -2
  99. package/man/canon-scenario-new.1 +2 -2
  100. package/man/canon-scenario.1 +2 -2
  101. package/man/canon-schema.1 +10 -4
  102. package/man/canon-search.1 +2 -2
  103. package/man/canon-selftest.1 +2 -2
  104. package/man/canon-skills-check.1 +2 -2
  105. package/man/canon-skills-doctor.1 +2 -2
  106. package/man/canon-skills-install.1 +2 -2
  107. package/man/canon-skills-show.1 +5 -5
  108. package/man/canon-skills.1 +2 -2
  109. package/man/canon-status.1 +2 -2
  110. package/man/canon-strategy-approval-payload.1 +2 -2
  111. package/man/canon-strategy-promote.1 +2 -2
  112. package/man/canon-strategy-rollback.1 +2 -2
  113. package/man/canon-strategy.1 +2 -2
  114. package/man/canon-subject-adopt.1 +2 -2
  115. package/man/canon-subject-move.1 +2 -2
  116. package/man/canon-subject-new.1 +2 -2
  117. package/man/canon-subject-show.1 +2 -2
  118. package/man/canon-subject-unadopt.1 +3 -3
  119. package/man/canon-subject.1 +2 -2
  120. package/man/canon-task-done.1 +2 -2
  121. package/man/canon-task.1 +2 -2
  122. package/man/canon-tier-age.1 +5 -5
  123. package/man/canon-tier.1 +2 -2
  124. package/man/canon-trace.1 +2 -2
  125. package/man/canon.1 +3 -3
  126. package/package.json +4 -4
@@ -18,11 +18,16 @@ concurrency outgrows sqlite's single-writer mode.
18
18
  ## `canon ingest sessions [--watch] [--interval-secs N] [--home <dir>] [--full] [--all-workspaces]`
19
19
 
20
20
  ```bash
21
- canon ingest sessions # one pass over this project's sources
22
- canon ingest sessions --watch # poll every 30s (see --interval-secs)
23
- canon ingest sessions --full # ignore the watermark; re-parse every in-scope file
24
- canon ingest sessions --all-workspaces # machine-wide scan
25
- canon ingest sessions --home /custom # override the scan-root home ($HOME)
21
+ # one pass over this project's sources
22
+ canon ingest sessions
23
+ # poll every 30s (see --interval-secs)
24
+ canon ingest sessions --watch
25
+ # ignore the watermark; re-parse every in-scope file
26
+ canon ingest sessions --full
27
+ # machine-wide scan
28
+ canon ingest sessions --all-workspaces
29
+ # override the scan-root home ($HOME)
30
+ canon ingest sessions --home /custom
26
31
  ```
27
32
 
28
33
  **Done when:** the run summary (below) ends with `malformed records … : 0`
@@ -175,7 +180,8 @@ These events interleave with `token_usage` events in one deterministic
175
180
  order (directive-before-token on a timestamp tie). Query them with:
176
181
 
177
182
  ```bash
178
- canon query --kind event # filter detail.label == "user_directive"
183
+ # filter detail.label == "user_directive"
184
+ canon query --kind event
179
185
  ```
180
186
 
181
187
  ## Reading the run summary
@@ -105,9 +105,9 @@ canon query --kind trajectory --json
105
105
 
106
106
  # Page a long result and keep only the fields you need; `meta` carries
107
107
  # `page` = {returned, total, nextCursor} and `fields` = the paths kept; a present `nextCursor` means more, and
108
- # `meta.next` repeats the call with `--cursor <nextCursor>` (`--all` returns everything). A cursor is bound to the
109
- # call that issued it: change only `--limit`, `--all` or `--fields`; any other change is `CURSOR_INVALID`
110
- # (`error.reason` `ARGUMENTS_CHANGED`). A field path is the record's own key, written as it is.
108
+ # `meta.next` repeats the call's explicit arguments with `--cursor=<nextCursor>` (`--all` returns everything). A cursor is bound to the
109
+ # call that issued it, `CANON_ROLE` included: change only `--limit`, `--all`, `--fields` or `--json`; any other change is `CURSOR_INVALID`
110
+ # (`error.reason` `ARGUMENTS_CHANGED`; `FOREIGN` for a cursor an older canon issued: start from the first page). A field path is the record's own key, written as it is.
111
111
  canon query --kind trajectory --json --limit 50 --fields at,kind
112
112
  canon query --kind trajectory --json --limit 50 --fields at,kind --cursor <nextCursor>
113
113
  ```
@@ -122,9 +122,12 @@ file names, tracked in git. `status`, the gate, the guards and `canon query`
122
122
  read the ledger only.
123
123
 
124
124
  ```bash
125
- canon query --kind evidence --history # every version, archive included
126
- canon ledger compact # preview; writes nothing
127
- canon ledger compact --commit --plan-digest "$PLAN_DIGEST" # pre-0.23 ledger or interrupted move; idempotent; `canon migrate` runs it
125
+ # every version, archive included
126
+ canon query --kind evidence --history
127
+ # preview; writes nothing
128
+ canon ledger compact
129
+ # pre-0.23 ledger or interrupted move; idempotent; `canon migrate` runs it
130
+ canon ledger compact --commit --plan-digest "$PLAN_DIGEST"
128
131
  ```
129
132
 
130
133
  A crash between the write and the move leaves a readable ledger that the next
@@ -287,12 +290,12 @@ skills:
287
290
  providers: [claude, codex] # any of claude, codex, omp, pi; [] for none
288
291
  ```
289
292
 
290
- `canon init` writes it: on a terminal it asks, with the harnesses whose
291
- `.claude`, `.codex`, `.omp` or `.pi` directory exists checked;
292
- `--providers a,b` (or `none`) answers without asking; with neither it takes
293
- the detected ones, and with none detected it writes only a commented example
293
+ `canon init` writes it and never asks: `--providers a,b` (or `none`) is the
294
+ answer; without it init takes the harnesses whose
295
+ `.claude`, `.codex`, `.omp` or `.pi` directory exists, and with none detected
296
+ it writes only a commented example
294
297
  and installs nothing. `.agents` alone never selects Codex. `canon skills
295
- 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
296
299
  --providers=<list>` rewrites it in place. Without the section they use the
297
300
  install lock's providers, then the detected ones; nothing undetected is ever
298
301
  added. A present section that is not this shape, or names another harness,
@@ -311,17 +314,22 @@ or when the index holds a scenario of a spec root that no `.feature` file
311
314
  of it declares any more.
312
315
 
313
316
  The commands that write records (`canon subject move`, `canon review
314
- scenario`, `canon evidence add` and `canon artifacts store`) rebuild a stale index on disk first: they
317
+ scenario`, `canon evidence add` and `canon artifacts store`) rebuild a stale index on disk, once nothing
318
+ refuses the call (a refused call writes nothing, whatever the index; until then they read the index as the
319
+ refresh would leave it, in memory): they
315
320
  run `canon inventory sync`'s code on the changed files (validation still
316
- covers the whole root) and print one stderr line:
321
+ covers the whole root) and print one stderr line (in JSON mode the line is a `meta.warnings` entry,
322
+ never a line on stderr):
317
323
 
318
324
  ```stderr
319
- 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
325
+ stderr: 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
320
326
  ```
321
327
 
322
328
  The commands that only read (`canon gate check`, `canon status`) write
323
329
  nothing: they grade a stale index as the refresh would leave it, built in
324
- memory, and print no refresh line. `canon query --kind scenario` and the
330
+ memory, and print no refresh line. `canon subject show` is the one read
331
+ command that declares the refresh, as a cache effect (its `effectNote`), after the subject is found.
332
+ `canon query --kind scenario` and the
325
333
  report panels read the index on disk as it is.
326
334
 
327
335
  A deleted scenario is not erased: `canon inventory sync` — and a writer's
@@ -332,7 +340,7 @@ a live generation that supersedes the tombstone. A root that fails to
332
340
  sync writes no tombstone either.
333
341
 
334
342
  ```stderr
335
- canon artifacts store: refreshed the scenario index from 1 scenario(s) no longer in any feature file (cart.pay.01): 1 record(s) written
343
+ stderr: canon artifacts store: refreshed the scenario index from 1 scenario(s) no longer in any feature file (cart.pay.01): 1 record(s) written
336
344
  ```
337
345
 
338
346
  `canon inventory sync` and every refresh hold an OS file lock on
@@ -350,7 +358,7 @@ the `verifying → shipped` guard refuses, and `canon status` lists only
350
358
  `canon inventory sync`:
351
359
 
352
360
  ```stderr
353
- canon artifacts store: found the scenario index stale for 1 changed feature file(s) (…): 0 record(s) written; NOT refreshed: root `root` (2 format violation(s), 0 sync error(s)) — `canon inventory sync` lists them
361
+ stderr: canon artifacts store: found the scenario index stale for 1 changed feature file(s) (…): 0 record(s) written; NOT refreshed: root `root` (2 format violation(s), 0 sync error(s)) — `canon inventory sync` lists them
354
362
  ```
355
363
 
356
364
  Nothing is written in a directory without `canon.yaml`. `canon inventory
@@ -370,6 +378,26 @@ released when its holder exits, however it exits, so it never goes stale;
370
378
  a writer gives up after 30 s with a message naming the file and saying to
371
379
  retry (the command exits `2` and writes no record).
372
380
 
381
+ ## The repository write lock
382
+
383
+ A committed destructive command (`canon purge`, `canon tier age`, `canon ledger
384
+ compact`, `canon artifacts move`, `canon migrate`) recomputes its plan, checks
385
+ its digest and then makes the effects, and nothing may change the plan in
386
+ between. It holds an OS file lock on `.canon/repo-write.lock` for the whole
387
+ span, from before it recomputes the plan to after its last effect; a preview
388
+ holds nothing. Every other command that writes what a plan reads (ledger
389
+ appends, the artifact store, briefs, specs, hooks, skill files, `canon.yaml`)
390
+ holds the same lock while it writes, so it waits for a commit and a commit
391
+ waits for it; reads, `--dry-run` runs and previews hold nothing. The lock is
392
+ the outermost one: it is taken before the scenario-index lock and the
393
+ artifact-store lock. It is released when its holder exits, however it exits,
394
+ so a killed commit never leaves it held, and the file is removed only by a
395
+ call that made it to take the lock and then ended refused (a refusal leaves
396
+ no file and no `.git/info/exclude` line behind). A
397
+ command that waited 30 s for it starts nothing: it exits `2` with
398
+ `REPO_LOCK_UNAVAILABLE` (retryable; run it again in a moment). The first
399
+ call that needs the lock makes the file, in any repository.
400
+
373
401
  ## What `canon init` keeps out of git
374
402
 
375
403
  A fresh `canon init` writes these lines to `.gitignore` (each under a
@@ -384,6 +412,8 @@ comment, only the missing ones):
384
412
  - `.canon/scenario-index.lock` — the lock serializing index rebuilds.
385
413
  - `.canon/artifact-store.lock` — the lock serializing artifact store
386
414
  writes.
415
+ - `.canon/repo-write.lock` — the lock a committed destructive command and
416
+ every writer of what its plan reads hold.
387
417
  - `.canon/dashboard-snapshot/` — the snapshot `canon dashboard`
388
418
  regenerates on every run.
389
419
  - `.canon/learn/` and `.canon/r2/` — local learn scratch and the local
@@ -40,7 +40,8 @@ Write (or update) the Subject before touching anything else, so every
40
40
  downstream artifact has a `subject_id` to pin to.
41
41
 
42
42
  ```bash
43
- canon context --repo . # record kinds, enum domains, policy requirements
43
+ # record kinds, enum domains, policy requirements
44
+ canon context --repo .
44
45
  canon subject new subject-domain-loop \
45
46
  --domain dev --title "Subject domain loop" \
46
47
  --summary "The durable product unit + its per-domain loop" \
@@ -70,7 +71,8 @@ canon subject new subject-domain-loop \
70
71
  subjects at once: `--input` takes inline JSON, a file or `-`; an array (or
71
72
  NDJSON) is a batch validated before any write, with `operands` holding the
72
73
  id and the other keys the option names in camelCase (`canon schema subject
73
- new --json` shows `input`: the schema of one document and a complete `example`). Writing an identical subject
74
+ new --json` shows `input`, its limits and a complete `example`; `canon schema subject new --schemas --json`
75
+ gives `Input`, the schema of one document). Writing an identical subject
74
76
  again is a no-op reported `unchanged` (`data.unchanged`).
75
77
 
76
78
  ### 2. Retrieve subject-scoped strategy memory BEFORE working
@@ -130,12 +132,13 @@ from anywhere). Any other transition is rejected by failure class, exits
130
132
  canon subject move subject-domain-loop --to specced
131
133
  canon subject move subject-domain-loop --to building
132
134
  canon subject move subject-domain-loop --to verifying
133
- canon subject move subject-domain-loop --to shipped # gated — see below
135
+ # gated — see below
136
+ canon subject move subject-domain-loop --to shipped
134
137
  ```
135
138
 
136
139
  On success the status updates in place; `--json` prints the updated
137
140
  record. On a gate block it prints violations by failure class to stderr,
138
- exits 1, and the record is unchanged.
141
+ exits 2 (the code names the condition: `SUBJECT_APPROVAL_REQUIRED`, `EVIDENCE_STALE`, `LAYOUT_MIGRATION_REQUIRED` or `SUBJECT_INVALID`; several causes are `error.details[]`), and the record is unchanged.
139
142
 
140
143
  **Corpus-wide, the same join:** `canon gate check` with
141
144
  `.canon/policy.yaml`'s opt-in `spec_coverage` section generalizes this
@@ -164,7 +167,7 @@ from shipping, until every evidenced scenario has evidence recorded after
164
167
  the close or the approval; the refusal names the scenarios and the
165
168
  `canon evidence add --all-scenarios` command. Each refusal prints by
166
169
  failure class (`uncovered-cell`), carries a `hint`,
167
- exits 1, and the status stays `verifying`. `retired` is not gated.
170
+ exits 2, and the status stays `verifying`. `retired` is not gated.
168
171
 
169
172
  **The brief (0.16, 0.17, 0.19):** `verifying → shipped` also refuses where a brief
170
173
  exists and is not approved, read exactly as `canon status` reads it (one
@@ -172,7 +175,7 @@ shared check, so status says `ship` only when this passes): the subject's
172
175
  scope (every change the subject answers for, see below) has a canon `.canon/changes/<change>/brief.md`
173
176
  with no `Approved:` line outside HTML comments and code fences, whose
174
177
  `Approved:` line carries a digest the brief's text no longer matches
175
- (`BRIEF_CHANGED_SINCE_APPROVAL`; the brief was edited after the human
178
+ (`BRIEF_APPROVAL_STALE`; the brief was edited after the human
176
179
  answered), or has no canon `brief.md` yet but comes from an external
177
180
  openspec plan with a `proposal.md` under the plan sources. It also
178
181
  refuses while `canon brief check` has blocker findings for that change,
@@ -182,8 +185,8 @@ a policy. The refusal is an `unreviewed-promotion` naming the subject, and
182
185
  says what to do:
183
186
 
184
187
  ```output
185
- canon subject move: SUBJECT_GATE_VIOLATION — unreviewed-promotion demo-subject — verifying → shipped: brief not approved — .canon/changes/c-demo/brief.md has no `Approved:` line; a human must answer the brief, and only from that answer the agent will record their words with `canon brief approve c-demo --approver <the human> --note "<their words>"`
186
- canon subject move: refused — get the human's answer to the brief and record it with `canon brief approve`, or pass --override-reason <text> to move anyway; the waiver is recorded on the subject
188
+ output: canon subject move: SUBJECT_INVALID — unreviewed-promotion demo-subject — verifying → shipped: brief not approved — .canon/changes/c-demo/brief.md has no `Approved:` line; a human must answer the brief, and only from that answer the agent will record their words with `canon brief approve c-demo --approver <the human> --note "<their words>"`
189
+ output: canon subject move: refused — get the human's answer to the brief and record it with `canon brief approve`, or pass --override-reason <text> to move anyway; the waiver is recorded on the subject
187
190
  ```
188
191
 
189
192
  For an external change with no canon brief yet, the reason reads
@@ -209,7 +212,7 @@ This refusal is a backstop, and so is the evidence one: shipping is not
209
212
  the first thing an unapproved brief stops. The agent writes the brief,
210
213
  runs `canon brief check` until it has no blockers, then shows it to the
211
214
  human and asks, and nothing is built or attested for the subject until the human
212
- answers. `canon evidence add` refuses (exit 1, nothing staged) a
215
+ answers. `canon evidence add` refuses (exit 2, nothing staged) a
213
216
  scenario whose `@subject` owner, or a task whose change
214
217
  an adopting subject, has an enforced, unapproved brief, through the same
215
218
  check; see `canon-gate`.
@@ -221,7 +224,7 @@ check; see `canon-gate`.
221
224
  edited since, and even if the tags were removed; or (3) a *chore*, which has
222
225
  no scenario to tag and so governs from the moment it is adopted. They pass
223
226
  only when every one of them reads as approved now; an unapproved one and one
224
- edited after its approval (`BRIEF_CHANGED_SINCE_APPROVAL`) are named for
227
+ edited after its approval (`BRIEF_APPROVAL_STALE`) are named for
225
228
  different reasons, and the answer does not depend on the order the changes
226
229
  were adopted or approved. The ship guard's brief-structure check runs over
227
230
  all of them. When several block, the hint is one
@@ -243,7 +246,7 @@ answers only to the changes its tags name (counting only changes the subject
243
246
  adopted); another change's edit after its approval never blocks it. An untagged
244
247
  scenario (one whose tags were removed reads the same) answers to every adopted
245
248
  change with an `Approved:` line, a chore included, so an approved change edited
246
- since refuses it (`BRIEF_CHANGED_SINCE_APPROVAL`); an unapproved chore blocks
249
+ since refuses it (`BRIEF_APPROVAL_STALE`); an unapproved chore blocks
247
250
  reopen and ship, never evidence. When no adopted change has an approved line,
248
251
  a scenario with evidence recorded before the subject first adopted a change
249
252
  (and without a brief override) needs no brief, and one without answers to the
@@ -259,7 +262,7 @@ When `.canon/policy.yaml` sets `spec_coverage.require_review` (see
259
262
  prints on stderr which it ran or skipped:
260
263
 
261
264
  ```output
262
- canon subject move: review guard for building → verifying (spec_coverage.require_review, scope: verifying, shipped)
265
+ output: canon subject move: review guard for building → verifying (spec_coverage.require_review, scope: verifying, shipped)
263
266
  ran unreviewed-promotion — every owned scenario needs a review by an actor, and from a session, other than its evidence's
264
267
  ran open-blocker — 1 adopted change(s) checked for open blocker findings
265
268
  ```
@@ -268,7 +271,7 @@ A target outside `require_review.scope` prints `skipped
268
271
  unreviewed-promotion — `specced` is not in require_review.scope`. A
269
272
  transition INTO an in-scope status with gaps — an owned scenario with no
270
273
  qualifying review (`unreviewed-promotion`), or an open blocker finding
271
- on an adopted change (`open-blocker`) — prints the violations, exits 1,
274
+ on an adopted change (`open-blocker`) — prints the violations, exits 2,
272
275
  leaves the record unchanged, and names `--override-reason`. `shipped`
273
276
  also refuses a `false-success` (a scenario whose latest audit is
274
277
  `refuted`) and, when `require_audit.scope` covers `shipped`, an owed
@@ -316,8 +319,8 @@ canon subject move demo-subject --to verifying \
316
319
  ```
317
320
 
318
321
  ```output
319
- canon subject move: reopened `demo-subject` from shipped, recorded by `impl`: follow-up: the retry path leaks a file handle; keep the follow-up in its approved change `c-demo`, then scenario, evidence and review; shipping again runs every guard
320
- canon subject move: demo-subject → verifying — written directly; nothing to promote
322
+ output: canon subject move: reopened `demo-subject` from shipped, recorded by `impl`: follow-up: the retry path leaks a file handle; keep the follow-up in its approved change `c-demo`, then scenario, evidence and review; shipping again runs every guard
323
+ output: canon subject move: demo-subject → verifying — written directly; nothing to promote
321
324
  ```
322
325
 
323
326
  - `--reason "<one line>"` is required for `shipped → verifying` (exit 2
@@ -330,14 +333,14 @@ canon subject move: demo-subject → verifying — written directly; nothing to
330
333
  - Inside the approved scope it needs no human: every change the subject
331
334
  answers for (tagged, approved or chore) has an approved brief, or there
332
335
  is no brief to answer.
333
- Otherwise it refuses, exit 1, naming the brief to approve, and
336
+ Otherwise it refuses, exit 2, naming the brief to approve, and
334
337
  `--override-reason` does not waive it:
335
338
  ``unreviewed-promotion demo-subject — shipped → verifying: reopening needs the approved scope, and the brief is not approved — .canon/changes/c-next/brief.md has no `Approved:` line; …``.
336
339
  - The review guard does not run on a reopen (it prints `skipped … — a
337
340
  reopen moves the subject back to fix its gaps; shipping again runs
338
341
  it`). `verifying → shipped` afterwards runs every guard above again.
339
- - A policy that fails closed (`POLICY_CHANGED_SINCE_APPROVAL`,
340
- `POLICY_APPROVAL_UNREADABLE`: see `canon-policy`) refuses the reopen and
342
+ - A policy that fails closed (`POLICY_APPROVAL_STALE`,
343
+ `POLICY_APPROVAL_CORRUPT`, `POLICY_APPROVAL_DENIED`: see `canon-policy`) refuses the reopen and
341
344
  the ship, and `--override-reason` does not waive it.
342
345
  - `canon change new` on a shipped subject prints the reopen command
343
346
  (`canon subject move <id> --to verifying --reason "<why it reopens>"`)
@@ -353,9 +356,11 @@ Planning docs written outside canon become change/task records via
353
356
  `canon subject adopt` lifts an imported change into a managed Subject:
354
357
 
355
358
  ```bash
356
- canon ingest plans --repo . # import → change/task rows
359
+ # import → change/task rows
360
+ canon ingest plans --repo .
361
+ # link change → subject
357
362
  canon subject adopt subject-domain-loop \
358
- --change subject-domain-loop-plan # link change → subject
363
+ --change subject-domain-loop-plan
359
364
  ```
360
365
 
361
366
  A change made with `canon change new` (below) is recorded and adopted
@@ -377,7 +382,7 @@ second subject adds that subject, never re-points the change, and says
377
382
  so:
378
383
 
379
384
  ```output
380
- canon subject adopt: linked change `refactor-core` to subject `editing` as well; the change now belongs to `perception` and `editing` — written directly; nothing to promote
385
+ output: canon subject adopt: linked change `refactor-core` to subject `editing` as well; the change now belongs to `perception` and `editing` — written directly; nothing to promote
381
386
  ```
382
387
 
383
388
  Every reader joins a subject to its changes from both sides (the
@@ -418,7 +423,7 @@ canon subject unadopt <S> --change <change> --json
418
423
 
419
424
  Removes the link on both records and keeps the change and its brief, so the
420
425
  draft can be adopted elsewhere (a `proposed` holding subject, say) while the
421
- live subject keeps shipping. It is refused with `SUBJECT_UNADOPT_REFUSED`
426
+ live subject keeps shipping. It is refused with `SUBJECT_CONFLICT` (reason `APPROVED_BRIEF` or `FLOW_TAGGED`)
422
427
  for a change whose brief carries an `Approved:` line, and while a scenario
423
428
  the subject owns carries a `@flow:<change>.*` tag (it would fall back to an
424
429
  older approval; remove or retag those scenarios first, the hint names them);
@@ -434,8 +439,8 @@ canon change new add-login --subject auth --title "Add login"
434
439
  ```
435
440
 
436
441
  ```output
437
- canon change new: wrote .canon/changes/add-login/brief.md
438
- canon change new: recorded change `add-login` adopted into subject `auth` — written directly; nothing to promote
442
+ output: canon change new: wrote .canon/changes/add-login/brief.md
443
+ output: canon change new: recorded change `add-login` adopted into subject `auth` — written directly; nothing to promote
439
444
  ```
440
445
 
441
446
  - **Writes** canon's own change dir, `.canon/changes/<slug>/`, holding
@@ -493,8 +498,10 @@ brief (`data.next`, `meta.next`).
493
498
  ### The brief: `canon brief show`, `canon brief check`, `canon brief approve`
494
499
 
495
500
  ```text
496
- canon brief show [<slug>] # state and the next one to three steps; never edits
497
- canon brief check [<slug>] [--json] # findings; exit 0 clean, 2 blockers, 1 error
501
+ # state and the next one to three steps; never edits
502
+ canon brief show [<slug>]
503
+ # findings; exit 0 clean, 2 blockers, 1 error
504
+ canon brief check [<slug>] [--json]
498
505
  canon brief approve <slug> <slug> --approver <the human> --note "<their words>"
499
506
  ```
500
507
 
@@ -503,12 +510,12 @@ same answer and one timestamp on every brief and on the policy approval
503
510
  (0.31). Every slug is checked before anything is written (a brief that
504
511
  exists and reads, no blocker, a one-line name and answer, a body that does not
505
512
  end inside an open code fence); one refused slug refuses the call, exit 2
506
- `BRIEF_APPROVE_REFUSED` naming each refused slug with its own code, and
507
- nothing is written. A repeated slug counts once. With one slug the output is as
513
+ with the first refused slug's own code (`BRIEF_INVALID`, `BRIEF_NOT_FOUND`) and an
514
+ `error.details` entry for each refused slug, and nothing is written. A repeated slug counts once. With one slug the output is as
508
515
  before; with several, `--json` carries one entry per slug (`written`,
509
516
  `unchanged`, `failed`, `skipped`). A disk error while writing exits 1 and
510
517
  leaves the slugs already written approved. The call is human-only for every
511
- slug; the `HUMAN_ONLY` hint gives the human one line with all the slugs.
518
+ slug; the `ACTOR_DENIED` hint gives the human one line with all the slugs.
512
519
 
513
520
  Without the change operand, the repo's only live change is read. The brief is the
514
521
  PRD `canon change new` writes; sections are found by their English `##`
@@ -518,7 +525,7 @@ before. A brief with `<!-- canon:… -->` comments or Korean headings is in the
518
525
  0.30 format: `brief check` gives it one blocker (`brief.old-format`) and
519
526
  `canon migrate` rewrites it; `status`, `gate check`, `context` and `brief show`
520
527
  warn, and a command that writes and judges it is refused
521
- (`OLD_BRIEF_FORMAT`).
528
+ (`BRIEF_MIGRATION_REQUIRED`).
522
529
 
523
530
  - **Blockers (structure, `brief.*`)**: a missing, duplicated or
524
531
  out-of-order section; a flow with no steps or a duplicate step id;
@@ -543,7 +550,7 @@ warn, and a command that writes and judges it is refused
543
550
  `Approved: <who> <when> — "<words>" (brief sha256:<digest>)` into the
544
551
  brief and records the policy approval in the same call. It refuses
545
552
  (exit 2) while the check has structure blockers. Edit the brief
546
- afterwards and every reader says `BRIEF_CHANGED_SINCE_APPROVAL` until a
553
+ afterwards and every reader says `BRIEF_APPROVAL_STALE` until a
547
554
  human answers again and `approve` runs again. Run it only from the
548
555
  human's answer, with their words as `--note`.
549
556
 
@@ -567,8 +574,10 @@ step that writes it.
567
574
  ## Where are we: `canon status`
568
575
 
569
576
  ```bash
570
- canon status # subjects by status, their gaps, next commands
571
- canon status --json # the same report, stable shape
577
+ # subjects by status, their gaps, next commands
578
+ canon status
579
+ # the same report, stable shape
580
+ canon status --json
572
581
  canon status --repo ../other
573
582
  ```
574
583
 
@@ -611,7 +620,7 @@ uses, so status and the gate cannot disagree about a gap:
611
620
  `Approved: <who> <when> — "<the human's words>" (brief sha256:<digest>)`,
612
621
  the digest covering the brief without its `Approved:` lines. A brief
613
622
  edited after that reads `brief: not approved (<brief> changed after its
614
- approval (BRIEF_CHANGED_SINCE_APPROVAL))` and names the re-approval
623
+ approval (BRIEF_APPROVAL_STALE))` and names the re-approval
615
624
  command: the changed brief needs a new human answer. A line with no
616
625
  digest (written before 0.19) stays approved, with an `advisory:` to
617
626
  re-approve so it binds to the text. Fixes and follow-ups
@@ -818,7 +827,8 @@ and `canon gate check` exits `0`; read `data.subjects[]` for the counts and
818
827
 
819
828
  ```bash
820
829
  canon query --kind subject --domain dev --status building
821
- canon report build --repo . # renders the "Subjects" panel
830
+ # renders the "Subjects" panel
831
+ canon report build --repo .
822
832
  ```
823
833
 
824
834
  - `canon query --kind subject [--domain <d>] [--status <s>]` — the