@journeykit/canon 0.33.0 → 0.35.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 (125) hide show
  1. package/dist/skills/SKILL.src.md +44 -23
  2. package/dist/skills/reference/canon-artifact-ingest.md +6 -3
  3. package/dist/skills/reference/canon-authoring.md +8 -5
  4. package/dist/skills/reference/canon-context.md +8 -5
  5. package/dist/skills/reference/canon-fmt.md +2 -1
  6. package/dist/skills/reference/canon-gate.md +12 -8
  7. package/dist/skills/reference/canon-inventory.md +13 -10
  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-report-dashboard.md +6 -3
  11. package/dist/skills/reference/canon-review.md +2 -2
  12. package/dist/skills/reference/canon-session-ingest.md +12 -6
  13. package/dist/skills/reference/canon-storage.md +48 -16
  14. package/dist/skills/reference/canon-subject.md +27 -18
  15. package/dist/skills/reference/role-implementer.md +58 -58
  16. package/dist/skills/reference/role-orchestrator.md +128 -123
  17. package/dist/skills/reference/role-planner.md +44 -44
  18. package/dist/skills/reference/role-reviewer.md +46 -46
  19. package/man/canon-adapter-authorize.1 +3 -3
  20. package/man/canon-adapter-validate.1 +3 -3
  21. package/man/canon-adapter.1 +3 -3
  22. package/man/canon-artifacts-move.1 +3 -3
  23. package/man/canon-artifacts-store.1 +3 -3
  24. package/man/canon-artifacts-verify.1 +3 -3
  25. package/man/canon-artifacts.1 +3 -3
  26. package/man/canon-brief-approve.1 +3 -3
  27. package/man/canon-brief-check.1 +3 -3
  28. package/man/canon-brief-show.1 +3 -3
  29. package/man/canon-brief.1 +3 -3
  30. package/man/canon-change-new.1 +3 -3
  31. package/man/canon-change.1 +3 -3
  32. package/man/canon-completion.1 +3 -3
  33. package/man/canon-context-pack-add.1 +3 -3
  34. package/man/canon-context-pack-show.1 +3 -3
  35. package/man/canon-context-pack-verify.1 +3 -3
  36. package/man/canon-context-pack.1 +3 -3
  37. package/man/canon-context.1 +4 -4
  38. package/man/canon-dashboard.1 +3 -3
  39. package/man/canon-demo-init.1 +3 -3
  40. package/man/canon-demo-review.1 +3 -3
  41. package/man/canon-demo.1 +3 -3
  42. package/man/canon-dispatch-begin.1 +3 -3
  43. package/man/canon-dispatch-diff.1 +3 -3
  44. package/man/canon-dispatch-end.1 +3 -3
  45. package/man/canon-dispatch.1 +3 -3
  46. package/man/canon-divergence-accept.1 +3 -3
  47. package/man/canon-divergence-add.1 +3 -3
  48. package/man/canon-divergence-defer.1 +3 -3
  49. package/man/canon-divergence-list.1 +6 -6
  50. package/man/canon-divergence-resolve.1 +3 -3
  51. package/man/canon-divergence.1 +3 -3
  52. package/man/canon-evidence-add.1 +3 -3
  53. package/man/canon-evidence-approval-payload.1 +3 -3
  54. package/man/canon-evidence-audit.1 +3 -3
  55. package/man/canon-evidence.1 +3 -3
  56. package/man/canon-export.1 +6 -6
  57. package/man/canon-feature-new.1 +3 -3
  58. package/man/canon-feature.1 +3 -3
  59. package/man/canon-finding-add.1 +4 -4
  60. package/man/canon-finding-close.1 +3 -3
  61. package/man/canon-finding-list.1 +6 -6
  62. package/man/canon-finding.1 +3 -3
  63. package/man/canon-format.1 +3 -3
  64. package/man/canon-gate-check.1 +3 -3
  65. package/man/canon-gate-install-hooks.1 +3 -3
  66. package/man/canon-gate-promote.1 +3 -3
  67. package/man/canon-gate.1 +3 -3
  68. package/man/canon-ingest-plans.1 +3 -3
  69. package/man/canon-ingest-records.1 +3 -3
  70. package/man/canon-ingest-sessions.1 +3 -3
  71. package/man/canon-ingest.1 +3 -3
  72. package/man/canon-init.1 +9 -9
  73. package/man/canon-inventory-sync.1 +3 -3
  74. package/man/canon-inventory.1 +3 -3
  75. package/man/canon-ledger-compact.1 +3 -3
  76. package/man/canon-ledger.1 +3 -3
  77. package/man/canon-migrate.1 +3 -3
  78. package/man/canon-plugin-sync.1 +3 -3
  79. package/man/canon-plugin.1 +3 -3
  80. package/man/canon-policy-approve.1 +3 -3
  81. package/man/canon-policy-show.1 +3 -3
  82. package/man/canon-policy.1 +3 -3
  83. package/man/canon-prompt-add.1 +3 -3
  84. package/man/canon-prompt-show.1 +3 -3
  85. package/man/canon-prompt.1 +3 -3
  86. package/man/canon-purge.1 +3 -3
  87. package/man/canon-query.1 +6 -6
  88. package/man/canon-regime-show.1 +3 -3
  89. package/man/canon-regime.1 +3 -3
  90. package/man/canon-report-build.1 +3 -3
  91. package/man/canon-report-check.1 +3 -3
  92. package/man/canon-report-snapshot.1 +3 -3
  93. package/man/canon-report.1 +3 -3
  94. package/man/canon-retrieve.1 +4 -4
  95. package/man/canon-review-change.1 +3 -3
  96. package/man/canon-review-scenario.1 +3 -3
  97. package/man/canon-review.1 +3 -3
  98. package/man/canon-scenario-new.1 +3 -3
  99. package/man/canon-scenario.1 +3 -3
  100. package/man/canon-schema.1 +14 -6
  101. package/man/canon-search.1 +3 -3
  102. package/man/canon-selftest.1 +3 -3
  103. package/man/canon-skills-check.1 +3 -3
  104. package/man/canon-skills-doctor.1 +3 -3
  105. package/man/canon-skills-install.1 +3 -3
  106. package/man/canon-skills-show.1 +6 -6
  107. package/man/canon-skills.1 +3 -3
  108. package/man/canon-status.1 +4 -4
  109. package/man/canon-strategy-approval-payload.1 +3 -3
  110. package/man/canon-strategy-promote.1 +3 -3
  111. package/man/canon-strategy-rollback.1 +3 -3
  112. package/man/canon-strategy.1 +3 -3
  113. package/man/canon-subject-adopt.1 +3 -3
  114. package/man/canon-subject-move.1 +3 -3
  115. package/man/canon-subject-new.1 +4 -4
  116. package/man/canon-subject-show.1 +3 -3
  117. package/man/canon-subject-unadopt.1 +3 -3
  118. package/man/canon-subject.1 +3 -3
  119. package/man/canon-task-done.1 +3 -3
  120. package/man/canon-task.1 +3 -3
  121. package/man/canon-tier-age.1 +3 -3
  122. package/man/canon-tier.1 +3 -3
  123. package/man/canon-trace.1 +3 -3
  124. package/man/canon.1 +3 -3
  125. package/package.json +4 -4
@@ -210,22 +210,36 @@ A role is guidance, not enforcement: `--role` on `evidence add` and
210
210
  - Your role guide's **Commands** block (`canon skills show role-<role>`) is
211
211
  your complete command set, every option of every leaf included. Run `canon schema <command> --json` only for a
212
212
  command it does not list, or after a `USAGE` refusal (read `error.field`
213
- and `error.expected` first). `canon schema` alone lists every command.
213
+ 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.
214
220
  - Pass `--json` and read stdout as one document, nothing on stderr. Success is
215
221
  `{"ok":true,"data":…,"meta":…}`; failure is
216
222
  `{"ok":false,"error":{…},"meta":…}` (a check that fails keeps its report in
217
- `data`). `meta` carries `tool`, `toolVersion`, `contract` (`atip/1`), `schemaVersion`,
223
+ `data`). `meta` carries `tool`, `toolVersion`, `contract` (`atip/1`),
224
+ `contractVersion` (`1.2`), `schemaVersion`,
218
225
  `command`, and when present `next`, `warnings` (`[{code, message}]`) and
219
226
  `page`; a repeated write that changed nothing reports `data.unchanged: true`.
220
- Absent members are omitted, never `null` or `[]`. A paged list puts
221
- `meta.page.nextCursor` on the call's `meta.next` as `--cursor <nextCursor>`;
222
- no `nextCursor` means no more.
223
- On failure branch on `error.code`, fix the option `error.field` names
227
+ Absent members are omitted, never `null` or `[]` (a value you sent empty comes back as `received: ""`).
228
+ Read the members you know and ignore the ones you do not: a later canon
229
+ adds members, and a value of a closed set you have not seen (a `severity`, an
230
+ `outcome`, a `reason`) is one item to leave out, never a reason to reject the document. A paged list puts
231
+ `meta.page.nextCursor` in the first entry of the call's `meta.next` as `--cursor=<nextCursor>`
232
+ (run that entry as it is); no `nextCursor` means no more.
233
+ On failure branch on `error.code`, and on `error.reason` where the code has
234
+ reasons (`INPUT_INVALID` says `NOT_JSON`, `UNKNOWN_KEY`, `STDIN_TTY`, … so you
235
+ know whether to fix the JSON or the shell); fix the option `error.field` names
224
236
  using the `error.expected` values (`field` is an option name, or a JSON
225
- pointer such as `/items/2/scenario` into an `--input` document), and
226
- follow `error.hint`: every refusal carries a working invocation. The codes
227
- are stable; `canon schema --errors --json` lists them and
228
- `canon schema --error <CODE>` explains one.
237
+ pointer such as `/2/scenario` into an `--input` document, `/<index>/<key>` in a batch), and
238
+ follow `error.hint`: it is one runnable command line that starts with `canon`, the
239
+ same with `<PLACEHOLDER>`s you fill in, or one sentence. The codes
240
+ are stable; `canon schema --errors --json` lists them with their reasons and
241
+ `canon schema --error <CODE>` explains one. A warning code (`meta.warnings`) is in
242
+ `canon schema --warnings --json`; `canon schema --warning <CODE>` explains one.
229
243
  - **Exit codes:** only 0, 1 and 2 exist: 0 ok; 1 the command ran and its answer
230
244
  is negative or incomplete (a failed check, a violation, `unverifiable` when
231
245
  nothing was examined, a partly failed batch); 2 refused to run (usage,
@@ -244,8 +258,8 @@ A role is guidance, not enforcement: `--role` on `evidence add` and
244
258
  has blockers; the approval step appears only when the brief is clean and
245
259
  unapproved.
246
260
  - Page long lists with `--limit <n>` and `--cursor <nextCursor>` (`--all`
247
- for everything), and project fields with `--fields a,b` (`status`, `export`,
248
- `query`, `finding list`, `divergence list`, `context`).
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`
262
+ (`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.
249
263
  - `--override-reason <text>` is the only way to proceed past a waivable
250
264
  refusal; it is recorded, echoed in `data.overrides[]` and listed by `status`
251
265
  for the human. There is no `--force`. `brief approve`, `policy approve`,
@@ -263,11 +277,12 @@ the positional arguments. An object is one invocation; a JSON array (or NDJSON
263
277
  from a file or stdin) is a batch, run in order and answered with
264
278
  `data.items[]` (`index`, `status` of `written`, `unchanged`, `failed` or
265
279
  `skipped`, and that element's `data` or `error`). A batch is validated whole
266
- before anything is written: an invalid element writes nothing and exits 2,
267
- `error.field` pointing at `/items/<i>/<key>`; a write that fails stops there
268
- (exit 1, `BATCH_INCOMPLETE`) and re-running the same batch reports the finished elements
269
- `unchanged`. A typo in a key is refused (`INPUT_INVALID`, `field` naming the key), never dropped.
270
- `canon schema <command> --json` shows the leaf's input schema and an example.
280
+ before anything is written, and that check reads no state: an invalid element writes nothing and exits 2 with
281
+ its own code, `error.field` pointing at `/<i>/<key>` and no `data`. An element that fails when it runs (the object
282
+ it names does not exist) stops the batch: exit 1, `BATCH_INCOMPLETE`, `error.field` exactly `/<i>`, the element
283
+ `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`.
271
286
 
272
287
  ```text
273
288
  canon scenario new --input '[{"operands":["auth.login.01"],"title":"Logs in","case":"happy"},{"operands":["auth.login.02"],"title":"Wrong password","case":"failure"}]'
@@ -315,12 +330,18 @@ and `canon context` warn when the repo holds 0.27-era state and put
315
330
  ## Setup and provider projection
316
331
 
317
332
  ```bash
318
- canon init # canon.yaml, starter policy (--policy light|standard|strict, default strict; see canon-policy), AGENTS.md block, commented plans examples
319
- canon init --providers=claude,codex # harnesses for the canon skill; without it init asks on a terminal, else takes the detected .claude/.codex/.omp/.pi
320
- canon skills install # follows canon.yaml skills.providers (else the install lock's, else the detected ones; none detected installs nothing); prunes projected files the bundle no longer ships when their bytes match the lock, keeps a modified one and reports `kept:`
321
- canon skills install --providers=claude,codex,omp,pi # any subset or none; rewrites skills.providers
322
- canon skills check # read-only drift check; exit 1 (SKILLS_DRIFT) names each orphan, a projected file the bundle no longer ships
323
- canon skills doctor # 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
333
+ # canon.yaml, starter policy (--policy light|standard|strict, default strict; see canon-policy), AGENTS.md block, commented plans examples
334
+ canon init
335
+ # harnesses for the canon skill; without it init takes the detected .claude/.codex/.omp/.pi (it never asks)
336
+ canon init --providers=claude,codex
337
+ # follows canon.yaml skills.providers (else the install lock's, else the detected ones; none detected installs nothing); prunes projected files the bundle no longer ships when their bytes match the lock, keeps a modified one and reports `kept:`
338
+ canon skills install
339
+ # any subset or none; rewrites skills.providers
340
+ 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
342
+ canon skills check
343
+ # 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
+ canon skills doctor
324
345
  ```
325
346
 
326
347
  `install` writes, per selected harness, a short `SKILL.md` stub (this file's
@@ -9,9 +9,12 @@ verdicts, and writes idempotently.
9
9
  ## `canon ingest records [--repo <dir>] [--watch]`
10
10
 
11
11
  ```bash
12
- canon ingest records # one pass over the repo's artifacts
13
- canon ingest records --repo ../svc # a specific repo
14
- canon ingest records --watch # poll loop
12
+ # one pass over the repo's artifacts
13
+ canon ingest records
14
+ # a specific repo
15
+ canon ingest records --repo ../svc
16
+ # poll loop
17
+ canon ingest records --watch
15
18
  ```
16
19
 
17
20
  Source roots resolve from `canon.yaml`'s `artifacts.sources` config
@@ -92,11 +92,14 @@ a scoped gate.
92
92
  ## The commands — and the one thing you never type
93
93
 
94
94
  ```bash
95
- canon feature new checkout.cart --title "Cart" # once per surface
95
+ # once per surface
96
+ canon feature new checkout.cart --title "Cart"
97
+ # once per behavior
96
98
  canon scenario new checkout.cart.01 --title "Adding an item raises the count" \
97
- --subject checkout-core --lane behavior --case happy # once per behavior
99
+ --subject checkout-core --lane behavior --case happy
100
+ # and its failure path
98
101
  canon scenario new checkout.cart.04 --title "Adding an out-of-stock item is refused" \
99
- --subject checkout-core --lane behavior --case failure # and its failure path
102
+ --subject checkout-core --lane behavior --case failure
100
103
  ```
101
104
 
102
105
  There is no format or sync step after these: the scaffold writes a
@@ -111,8 +114,8 @@ Fix it and the next command indexes the files (see `canon-inventory`).
111
114
  Many scenarios at once: `--input` takes inline JSON, a file or `-`. An
112
115
  array (or NDJSON) is a batch, validated whole before anything is written;
113
116
  each element holds `operands` (the scenario tag) and the other options as
114
- camelCase keys. `canon schema scenario new --json` shows the input schema
115
- and an example. There is no `--from`.
117
+ camelCase keys. `canon schema scenario new --json` shows `input`: the schema of one document
118
+ and a complete `example`. There is no `--from`.
116
119
 
117
120
  ```text
118
121
  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"}]'
@@ -13,9 +13,12 @@ deterministic authoring surface.
13
13
  ## `canon context [--repo <dir>] [--json]`
14
14
 
15
15
  ```bash
16
- canon context # compact human outline (prompt-injectable)
17
- canon context --json # the full machine-readable surface
18
- canon context --repo ../other # resolve a specific repo's surface
16
+ # compact human outline (prompt-injectable)
17
+ canon context
18
+ # the full machine-readable surface
19
+ canon context --json
20
+ # resolve a specific repo's surface
21
+ canon context --repo ../other
19
22
  ```
20
23
 
21
24
  The outline opens with a short header, then a blank line, then the
@@ -34,8 +37,8 @@ authored; `canon status` (see `canon-subject`) says where the repo stands
34
37
  and what to run next. `--json` prints the `{ok, data, meta}` envelope: the
35
38
  header adds no field, and `policy.spec_coverage`/`policy.diagnostics`
36
39
  already carry the same fact. `--fields <list>` keeps only the named
37
- top-level fields (`kinds`, `enums`, `policy`, `review`, …); an unknown name is
38
- `USAGE` (exit 2; `field` is `--fields`, `expected` lists the selectable fields).
40
+ top-level fields (`kinds`, `enums`, `policy`, `review`, …), spelled as the report spells them; an unknown name is
41
+ `USAGE` (exit 2; `field` is `--fields`, `expected` lists the selectable fields), and `meta.fields` lists the kept ones.
39
42
 
40
43
  On a repository that still holds 0.27 canon state, `canon context` warns
41
44
  and puts `canon migrate --json` first in `meta.next`: run `canon migrate`
@@ -9,7 +9,8 @@ rewrites or mutates the corpus — it only validates and reports.
9
9
  ## Running
10
10
 
11
11
  ```bash
12
- canon format spec # e.g. a consumer repo's spec/
12
+ # e.g. a consumer repo's spec/
13
+ canon format spec
13
14
  ```
14
15
 
15
16
  With no operand, `canon format` validates every `specs.roots[]` entry of
@@ -42,8 +42,10 @@ corpus and runs them, printing every violation grouped by failure class.
42
42
  when `--release` is passed.
43
43
 
44
44
  ```bash
45
- canon gate check --repo . # ordinary evaluation
46
- canon gate check --repo . --release # + trust-below-required
45
+ # ordinary evaluation
46
+ canon gate check --repo .
47
+ # + trust-below-required
48
+ canon gate check --repo . --release
47
49
  ```
48
50
 
49
51
  Exit `0` clean, `1` gate-red (any violation), `2` usage/load failure
@@ -314,8 +316,8 @@ check `canon status` and the ship guard use; see
314
316
  `--task`:
315
317
 
316
318
  ```stderr
317
- 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>"`
318
- canon evidence add: refused — stop and get the human's answer to the brief: nothing is built or attested for a subject before its brief is approved. Show it (`canon brief show --body`), ask with the harness question tool, and on their explicit approval record it with `canon brief approve --approver <their name> --note "<their words>" --relayed-via <the question tool> --actor <your actor id> --digest <change>=<the digest shown>` (or they approve on `canon dashboard`); never unset your role to run it; to attest anyway, pass --override-reason <text>, which is recorded on the evidence record
319
+ stderr: 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>"`
320
+ stderr: canon evidence add: refused — stop and get the human's answer to the brief: nothing is built or attested for a subject before its brief is approved. Show it (`canon brief show --body`), ask with the harness question tool, and on their explicit approval record it with `canon brief approve --approver <their name> --note "<their words>" --relayed-via <the question tool> --actor <your actor id> --digest <change>=<the digest shown>` (or they approve on `canon dashboard`); never unset your role to run it; to attest anyway, pass --override-reason <text>, which is recorded on the evidence record
319
321
  ```
320
322
 
321
323
  For a change from an external plan with no canon brief yet, the first
@@ -534,7 +536,7 @@ or `-`; an array (or NDJSON) is a batch, validated element by element before
534
536
  the first write (a refusal writes nothing, exit 2). Keys are the option
535
537
  names in camelCase plus `operands`: one object per scenario, with
536
538
  `scenario`, `class`, `ref` and `role`. `canon schema evidence add --json`
537
- shows the input schema and an example. Each element reports `written`,
539
+ shows `input`: the schema of one document and a complete `example`. Each element reports `written`,
538
540
  `unchanged` or `failed` in `data.items[]`.
539
541
 
540
542
  `--rev <rev>` names the commit the evidence ran against (default `HEAD`).
@@ -553,7 +555,7 @@ current pinned review saw, `canon evidence add` prints one line per
553
555
  review it makes stale (its verdict or checks differ from what the review saw), with the command that re-pins it:
554
556
 
555
557
  ```stderr
556
- 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 root --actor reviewer-2 --role reviewer --original-spec-ref specs/kind=feature/area=cart/add.feature
558
+ stderr: 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 root --actor reviewer-2 --role reviewer --original-spec-ref specs/kind=feature/area=cart/add.feature
557
559
  ```
558
560
 
559
561
  The decision is the gate's own (the same stale-review rule `canon gate
@@ -737,8 +739,10 @@ prints the plan (target path + assigned `run_seq` per candidate) without
737
739
  writing or deleting.
738
740
 
739
741
  ```bash
740
- canon gate promote --repo . # land every staged candidate
741
- canon gate promote --repo . --dry-run # preview only
742
+ # land every staged candidate
743
+ canon gate promote --repo .
744
+ # preview only
745
+ canon gate promote --repo . --dry-run
742
746
  ```
743
747
 
744
748
  Which writes need this step: `evidence add`, `finding add` and `finding
@@ -14,7 +14,7 @@ same code this command runs, scoped to the changed files, and say so in one
14
14
  stderr line:
15
15
 
16
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
+ 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
18
18
  ```
19
19
 
20
20
  `canon status` and `canon gate check` are read commands and write nothing:
@@ -76,14 +76,14 @@ author .feature corpus (canon scenario new)
76
76
  scenario index # refreshed by the writers; `canon inventory sync` rebuilds it
77
77
  │
78
78
  ▼
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
81
- canon gate check [--release] # trust-ladder / staleness / coverage — see canon-gate
79
+ review: canon review scenario # attests a review — see canon-inventory downstream / canon-gate
80
+ diverge: canon divergence add|resolve|defer|accept|list # tracks/resolves a divergence
81
+ gate: canon gate check [--release] # trust-ladder / staleness / coverage — see canon-gate
82
82
  │
83
83
  ▼
84
- canon ingest records # with artifacts.native_records: true in canon.yaml —
85
- # feeds review/divergence verdicts into the flywheel
86
- # (see canon-artifact-ingest)
84
+ ingest: canon ingest records # with artifacts.native_records: true in canon.yaml —
85
+ # feeds review/divergence verdicts into the flywheel
86
+ # (see canon-artifact-ingest)
87
87
  ```
88
88
 
89
89
  Every stage after the index joins on the SAME `(project_id, scenario_id)`
@@ -92,9 +92,12 @@ pair it materializes — one identity scheme, no second one.
92
92
  ## `canon inventory sync [--repo <dir>] [--spec-root <dir>]`
93
93
 
94
94
  ```bash
95
- canon inventory sync # every canon.yaml specs.roots[] entry
96
- canon inventory sync --repo ../svc # a specific repo
97
- canon inventory sync --spec-root ./specs # ad hoc: ignore canon.yaml entirely
95
+ # every canon.yaml specs.roots[] entry
96
+ canon inventory sync
97
+ # a specific repo
98
+ canon inventory sync --repo ../svc
99
+ # ad hoc: ignore canon.yaml entirely
100
+ canon inventory sync --spec-root ./specs
98
101
  ```
99
102
 
100
103
  Per configured root, in order:
@@ -64,9 +64,12 @@ canon ingest plans --dialect openspec --plan-source ../other-repo
64
64
  ## 2. Run the import
65
65
 
66
66
  ```bash
67
- canon ingest plans # .canon/changes/ + every configured source
68
- canon ingest plans --json # machine-readable outcome
69
- canon ingest plans --repo ../svc # a specific repo root
67
+ # .canon/changes/ + every configured source
68
+ canon ingest plans
69
+ # machine-readable outcome
70
+ canon ingest plans --json
71
+ # a specific repo root
72
+ canon ingest plans --repo ../svc
70
73
  ```
71
74
 
72
75
  Per source the summary reports `changes_parsed`/`tasks_parsed`,
@@ -49,9 +49,12 @@ overlays:
49
49
  Drive it through the sync command (never hand-write records):
50
50
 
51
51
  ```bash
52
- canon plugin sync porting # every canon.yaml specs.roots[] entry
53
- canon plugin sync porting --repo ../svc # a specific repo
54
- canon plugin sync porting --spec-root ./specs # ad hoc: ignore canon.yaml
52
+ # every canon.yaml specs.roots[] entry
53
+ canon plugin sync porting
54
+ # a specific repo
55
+ canon plugin sync porting --repo ../svc
56
+ # ad hoc: ignore canon.yaml
57
+ canon plugin sync porting --spec-root ./specs
55
58
  ```
56
59
 
57
60
  `canon plugin sync <plugin-id>` resolves the manifest, then for each
@@ -70,9 +73,12 @@ error; the next step (3) reads the overlay back.
70
73
  ## 3. Project a read
71
74
 
72
75
  ```bash
73
- canon query --kind scenario --plugin porting # human table, projected fields appended
74
- canon query --kind scenario --plugin porting --json # merged record bodies
75
- canon query --kind scenario # no --plugin: byte-identical to the core view
76
+ # human table, projected fields appended
77
+ canon query --kind scenario --plugin porting
78
+ # merged record bodies
79
+ canon query --kind scenario --plugin porting --json
80
+ # no --plugin: byte-identical to the core view
81
+ canon query --kind scenario
76
82
  ```
77
83
 
78
84
  `--plugin <id>` folds the plugin's overlay records (latest-by-
@@ -40,7 +40,8 @@ Renders the current ten-panel report to `<repo>/.canon/REPORT.md`.
40
40
  subcommand uses — omit it (or pass `.`) to run from any subdirectory.
41
41
 
42
42
  ```bash
43
- canon report build # writes .canon/REPORT.md
43
+ # writes .canon/REPORT.md
44
+ canon report build
44
45
  canon report build --repo /path/to/repo
45
46
  ```
46
47
 
@@ -67,7 +68,8 @@ committed `.canon/REPORT.md`:
67
68
  `canon report build`).
68
69
 
69
70
  ```bash
70
- canon report check # wire into CI exactly like canon format / canon gate check
71
+ # wire into CI exactly like canon format / canon gate check
72
+ canon report check
71
73
  ```
72
74
 
73
75
  Wire it as a CI step or pre-push hook the same way `canon gate
@@ -126,7 +128,8 @@ canon report snapshot /tmp/canon-snap
126
128
  Serves the dashboard web app locally, pointed at a real snapshot:
127
129
 
128
130
  ```bash
129
- canon dashboard # regenerates a fresh snapshot every run, serves it
131
+ # regenerates a fresh snapshot every run, serves it
132
+ canon dashboard
130
133
  canon dashboard --snapshot /tmp/canon-snap --port 8080
131
134
  ```
132
135
 
@@ -121,7 +121,7 @@ Outside git, or in a repository with no commit, the write is refused with
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 the input schema and an example. An identical
124
+ finding add --json` shows `input`: the schema of one document and a complete `example`. An identical
125
125
  repeat is `unchanged` (`data.unchanged`) and writes nothing; a different
126
126
  finding under an occupied key is `FINDING_SEQ_OCCUPIED`.
127
127
 
@@ -468,7 +468,7 @@ With `require_review` present, `canon subject move <id> --to <state>`
468
468
  prints on stderr which review checks it ran or skipped:
469
469
 
470
470
  ```stderr
471
- canon subject move: review guard for building → verifying (spec_coverage.require_review, scope: verifying, shipped)
471
+ stderr: canon subject move: review guard for building → verifying (spec_coverage.require_review, scope: verifying, shipped)
472
472
  ran unreviewed-promotion — every owned scenario needs a signed review, current with the spec and evidence it pinned, by an actor, and from a session, other than its evidence's
473
473
  ran open-blocker — 1 adopted change(s) checked for open blocker findings
474
474
  ```
@@ -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
@@ -104,10 +104,12 @@ canon query --kind handoff --since 2026-06-01T00:00:00Z
104
104
  canon query --kind trajectory --json
105
105
 
106
106
  # Page a long result and keep only the fields you need; `meta` carries
107
- # `page` = {returned, total, nextCursor}; a present `nextCursor` means more, and
108
- # `meta.next` repeats the call with `--cursor <nextCursor>` (`--all` returns everything).
107
+ # `page` = {returned, total, nextCursor} and `fields` = the paths kept; a present `nextCursor` means more, and
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.
109
111
  canon query --kind trajectory --json --limit 50 --fields at,kind
110
- canon query --kind trajectory --json --limit 50 --cursor <nextCursor>
112
+ canon query --kind trajectory --json --limit 50 --fields at,kind --cursor <nextCursor>
111
113
  ```
112
114
 
113
115
  ## The ledger is latest-only; the archive holds history
@@ -120,9 +122,12 @@ file names, tracked in git. `status`, the gate, the guards and `canon query`
120
122
  read the ledger only.
121
123
 
122
124
  ```bash
123
- canon query --kind evidence --history # every version, archive included
124
- canon ledger compact # preview; writes nothing
125
- 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"
126
131
  ```
127
132
 
128
133
  A crash between the write and the move leaves a readable ledger that the next
@@ -285,10 +290,10 @@ skills:
285
290
  providers: [claude, codex] # any of claude, codex, omp, pi; [] for none
286
291
  ```
287
292
 
288
- `canon init` writes it: on a terminal it asks, with the harnesses whose
289
- `.claude`, `.codex`, `.omp` or `.pi` directory exists checked;
290
- `--providers a,b` (or `none`) answers without asking; with neither it takes
291
- 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
292
297
  and installs nothing. `.agents` alone never selects Codex. `canon skills
293
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
294
299
  --providers=<list>` rewrites it in place. Without the section they use the
@@ -309,17 +314,22 @@ or when the index holds a scenario of a spec root that no `.feature` file
309
314
  of it declares any more.
310
315
 
311
316
  The commands that write records (`canon subject move`, `canon review
312
- 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
313
320
  run `canon inventory sync`'s code on the changed files (validation still
314
- 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):
315
323
 
316
324
  ```stderr
317
- 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
318
326
  ```
319
327
 
320
328
  The commands that only read (`canon gate check`, `canon status`) write
321
329
  nothing: they grade a stale index as the refresh would leave it, built in
322
- 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
323
333
  report panels read the index on disk as it is.
324
334
 
325
335
  A deleted scenario is not erased: `canon inventory sync` — and a writer's
@@ -330,7 +340,7 @@ a live generation that supersedes the tombstone. A root that fails to
330
340
  sync writes no tombstone either.
331
341
 
332
342
  ```stderr
333
- 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
334
344
  ```
335
345
 
336
346
  `canon inventory sync` and every refresh hold an OS file lock on
@@ -348,7 +358,7 @@ the `verifying → shipped` guard refuses, and `canon status` lists only
348
358
  `canon inventory sync`:
349
359
 
350
360
  ```stderr
351
- 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
352
362
  ```
353
363
 
354
364
  Nothing is written in a directory without `canon.yaml`. `canon inventory
@@ -368,6 +378,26 @@ released when its holder exits, however it exits, so it never goes stale;
368
378
  a writer gives up after 30 s with a message naming the file and saying to
369
379
  retry (the command exits `2` and writes no record).
370
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_WRITE_BUSY` (retryable; run it again in a moment). The first
399
+ call that needs the lock makes the file, in any repository.
400
+
371
401
  ## What `canon init` keeps out of git
372
402
 
373
403
  A fresh `canon init` writes these lines to `.gitignore` (each under a
@@ -382,6 +412,8 @@ comment, only the missing ones):
382
412
  - `.canon/scenario-index.lock` — the lock serializing index rebuilds.
383
413
  - `.canon/artifact-store.lock` — the lock serializing artifact store
384
414
  writes.
415
+ - `.canon/repo-write.lock` — the lock a committed destructive command and
416
+ every writer of what its plan reads hold.
385
417
  - `.canon/dashboard-snapshot/` — the snapshot `canon dashboard`
386
418
  regenerates on every run.
387
419
  - `.canon/learn/` and `.canon/r2/` — local learn scratch and the local