@journeykit/canon 0.34.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 +22 -15
  2. package/dist/skills/reference/canon-artifact-ingest.md +6 -3
  3. package/dist/skills/reference/canon-authoring.md +6 -3
  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 +11 -7
  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 +1 -1
  12. package/dist/skills/reference/canon-session-ingest.md +12 -6
  13. package/dist/skills/reference/canon-storage.md +46 -16
  14. package/dist/skills/reference/canon-subject.md +26 -17
  15. package/dist/skills/reference/role-implementer.md +29 -29
  16. package/dist/skills/reference/role-orchestrator.md +68 -63
  17. package/dist/skills/reference/role-planner.md +22 -22
  18. package/dist/skills/reference/role-reviewer.md +23 -23
  19. package/man/canon-adapter-authorize.1 +2 -2
  20. package/man/canon-adapter-validate.1 +2 -2
  21. package/man/canon-adapter.1 +2 -2
  22. package/man/canon-artifacts-move.1 +2 -2
  23. package/man/canon-artifacts-store.1 +2 -2
  24. package/man/canon-artifacts-verify.1 +2 -2
  25. package/man/canon-artifacts.1 +2 -2
  26. package/man/canon-brief-approve.1 +2 -2
  27. package/man/canon-brief-check.1 +2 -2
  28. package/man/canon-brief-show.1 +2 -2
  29. package/man/canon-brief.1 +2 -2
  30. package/man/canon-change-new.1 +2 -2
  31. package/man/canon-change.1 +2 -2
  32. package/man/canon-completion.1 +2 -2
  33. package/man/canon-context-pack-add.1 +2 -2
  34. package/man/canon-context-pack-show.1 +2 -2
  35. package/man/canon-context-pack-verify.1 +2 -2
  36. package/man/canon-context-pack.1 +2 -2
  37. package/man/canon-context.1 +2 -2
  38. package/man/canon-dashboard.1 +2 -2
  39. package/man/canon-demo-init.1 +2 -2
  40. package/man/canon-demo-review.1 +2 -2
  41. package/man/canon-demo.1 +2 -2
  42. package/man/canon-dispatch-begin.1 +2 -2
  43. package/man/canon-dispatch-diff.1 +2 -2
  44. package/man/canon-dispatch-end.1 +2 -2
  45. package/man/canon-dispatch.1 +2 -2
  46. package/man/canon-divergence-accept.1 +2 -2
  47. package/man/canon-divergence-add.1 +2 -2
  48. package/man/canon-divergence-defer.1 +2 -2
  49. package/man/canon-divergence-list.1 +2 -2
  50. package/man/canon-divergence-resolve.1 +2 -2
  51. package/man/canon-divergence.1 +2 -2
  52. package/man/canon-evidence-add.1 +2 -2
  53. package/man/canon-evidence-approval-payload.1 +2 -2
  54. package/man/canon-evidence-audit.1 +2 -2
  55. package/man/canon-evidence.1 +2 -2
  56. package/man/canon-export.1 +2 -2
  57. package/man/canon-feature-new.1 +2 -2
  58. package/man/canon-feature.1 +2 -2
  59. package/man/canon-finding-add.1 +2 -2
  60. package/man/canon-finding-close.1 +2 -2
  61. package/man/canon-finding-list.1 +2 -2
  62. package/man/canon-finding.1 +2 -2
  63. package/man/canon-format.1 +2 -2
  64. package/man/canon-gate-check.1 +2 -2
  65. package/man/canon-gate-install-hooks.1 +2 -2
  66. package/man/canon-gate-promote.1 +2 -2
  67. package/man/canon-gate.1 +2 -2
  68. package/man/canon-ingest-plans.1 +2 -2
  69. package/man/canon-ingest-records.1 +2 -2
  70. package/man/canon-ingest-sessions.1 +2 -2
  71. package/man/canon-ingest.1 +2 -2
  72. package/man/canon-init.1 +7 -7
  73. package/man/canon-inventory-sync.1 +2 -2
  74. package/man/canon-inventory.1 +2 -2
  75. package/man/canon-ledger-compact.1 +2 -2
  76. package/man/canon-ledger.1 +2 -2
  77. package/man/canon-migrate.1 +2 -2
  78. package/man/canon-plugin-sync.1 +2 -2
  79. package/man/canon-plugin.1 +2 -2
  80. package/man/canon-policy-approve.1 +2 -2
  81. package/man/canon-policy-show.1 +2 -2
  82. package/man/canon-policy.1 +2 -2
  83. package/man/canon-prompt-add.1 +2 -2
  84. package/man/canon-prompt-show.1 +2 -2
  85. package/man/canon-prompt.1 +2 -2
  86. package/man/canon-purge.1 +2 -2
  87. package/man/canon-query.1 +2 -2
  88. package/man/canon-regime-show.1 +2 -2
  89. package/man/canon-regime.1 +2 -2
  90. package/man/canon-report-build.1 +2 -2
  91. package/man/canon-report-check.1 +2 -2
  92. package/man/canon-report-snapshot.1 +2 -2
  93. package/man/canon-report.1 +2 -2
  94. package/man/canon-retrieve.1 +2 -2
  95. package/man/canon-review-change.1 +2 -2
  96. package/man/canon-review-scenario.1 +2 -2
  97. package/man/canon-review.1 +2 -2
  98. package/man/canon-scenario-new.1 +2 -2
  99. package/man/canon-scenario.1 +2 -2
  100. package/man/canon-schema.1 +2 -2
  101. package/man/canon-search.1 +2 -2
  102. package/man/canon-selftest.1 +2 -2
  103. package/man/canon-skills-check.1 +2 -2
  104. package/man/canon-skills-doctor.1 +2 -2
  105. package/man/canon-skills-install.1 +2 -2
  106. package/man/canon-skills-show.1 +5 -5
  107. package/man/canon-skills.1 +2 -2
  108. package/man/canon-status.1 +2 -2
  109. package/man/canon-strategy-approval-payload.1 +2 -2
  110. package/man/canon-strategy-promote.1 +2 -2
  111. package/man/canon-strategy-rollback.1 +2 -2
  112. package/man/canon-strategy.1 +2 -2
  113. package/man/canon-subject-adopt.1 +2 -2
  114. package/man/canon-subject-move.1 +2 -2
  115. package/man/canon-subject-new.1 +2 -2
  116. package/man/canon-subject-show.1 +2 -2
  117. package/man/canon-subject-unadopt.1 +2 -2
  118. package/man/canon-subject.1 +2 -2
  119. package/man/canon-task-done.1 +2 -2
  120. package/man/canon-task.1 +2 -2
  121. package/man/canon-tier-age.1 +2 -2
  122. package/man/canon-tier.1 +2 -2
  123. package/man/canon-trace.1 +2 -2
  124. package/man/canon.1 +2 -2
  125. package/package.json +4 -4
@@ -221,20 +221,20 @@ A role is guidance, not enforcement: `--role` on `evidence add` and
221
221
  `{"ok":true,"data":…,"meta":…}`; failure is
222
222
  `{"ok":false,"error":{…},"meta":…}` (a check that fails keeps its report in
223
223
  `data`). `meta` carries `tool`, `toolVersion`, `contract` (`atip/1`),
224
- `contractVersion` (`1.1`), `schemaVersion`,
224
+ `contractVersion` (`1.2`), `schemaVersion`,
225
225
  `command`, and when present `next`, `warnings` (`[{code, message}]`) and
226
226
  `page`; a repeated write that changed nothing reports `data.unchanged: true`.
227
227
  Absent members are omitted, never `null` or `[]` (a value you sent empty comes back as `received: ""`).
228
228
  Read the members you know and ignore the ones you do not: a later canon
229
229
  adds members, and a value of a closed set you have not seen (a `severity`, an
230
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` on the call's `meta.next` as `--cursor <nextCursor>`;
232
- no `nextCursor` means no more.
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
233
  On failure branch on `error.code`, and on `error.reason` where the code has
234
234
  reasons (`INPUT_INVALID` says `NOT_JSON`, `UNKNOWN_KEY`, `STDIN_TTY`, … so you
235
235
  know whether to fix the JSON or the shell); fix the option `error.field` names
236
236
  using the `error.expected` values (`field` is an option name, or a JSON
237
- pointer such as `/items/2/scenario` into an `--input` document), and
237
+ pointer such as `/2/scenario` into an `--input` document, `/<index>/<key>` in a batch), and
238
238
  follow `error.hint`: it is one runnable command line that starts with `canon`, the
239
239
  same with `<PLACEHOLDER>`s you fill in, or one sentence. The codes
240
240
  are stable; `canon schema --errors --json` lists them with their reasons and
@@ -258,7 +258,7 @@ A role is guidance, not enforcement: `--role` on `evidence add` and
258
258
  has blockers; the approval step appears only when the brief is clean and
259
259
  unapproved.
260
260
  - 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` and `--fields` (`CURSOR_INVALID` with `error.reason` `ARGUMENTS_CHANGED` otherwise). Project fields with `--fields a,b`
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
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.
263
263
  - `--override-reason <text>` is the only way to proceed past a waivable
264
264
  refusal; it is recorded, echoed in `data.overrides[]` and listed by `status`
@@ -277,10 +277,11 @@ the positional arguments. An object is one invocation; a JSON array (or NDJSON
277
277
  from a file or stdin) is a batch, run in order and answered with
278
278
  `data.items[]` (`index`, `status` of `written`, `unchanged`, `failed` or
279
279
  `skipped`, and that element's `data` or `error`). A batch is validated whole
280
- before anything is written: an invalid element writes nothing and exits 2,
281
- `error.field` pointing at `/items/<i>/<key>`; a write that fails stops there
282
- (exit 1, `BATCH_INCOMPLETE`) and re-running the same batch reports the finished elements
283
- `unchanged`. A typo in a key is refused (`INPUT_INVALID`, `field` naming the key), never dropped.
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`.
284
285
  `canon schema <command> --json` shows the leaf's `input`: the schema of one document and a complete `example`.
285
286
 
286
287
  ```text
@@ -329,12 +330,18 @@ and `canon context` warn when the repo holds 0.27-era state and put
329
330
  ## Setup and provider projection
330
331
 
331
332
  ```bash
332
- canon init # canon.yaml, starter policy (--policy light|standard|strict, default strict; see canon-policy), AGENTS.md block, commented plans examples
333
- 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
334
- 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:`
335
- canon skills install --providers=claude,codex,omp,pi # any subset or none; rewrites skills.providers
336
- canon skills check # read-only drift check; exit 1 (SKILLS_DRIFT) names each orphan, a projected file the bundle no longer ships
337
- 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
338
345
  ```
339
346
 
340
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
@@ -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
@@ -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
@@ -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
 
@@ -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
@@ -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,10 +290,10 @@ 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
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
296
299
  --providers=<list>` rewrites it in place. Without the section they use the
@@ -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_WRITE_BUSY` (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" \
@@ -130,7 +131,8 @@ from anywhere). Any other transition is rejected by failure class, exits
130
131
  canon subject move subject-domain-loop --to specced
131
132
  canon subject move subject-domain-loop --to building
132
133
  canon subject move subject-domain-loop --to verifying
133
- canon subject move subject-domain-loop --to shipped # gated — see below
134
+ # gated — see below
135
+ canon subject move subject-domain-loop --to shipped
134
136
  ```
135
137
 
136
138
  On success the status updates in place; `--json` prints the updated
@@ -182,8 +184,8 @@ a policy. The refusal is an `unreviewed-promotion` naming the subject, and
182
184
  says what to do:
183
185
 
184
186
  ```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
187
+ output: 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>"`
188
+ 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
189
  ```
188
190
 
189
191
  For an external change with no canon brief yet, the reason reads
@@ -259,7 +261,7 @@ When `.canon/policy.yaml` sets `spec_coverage.require_review` (see
259
261
  prints on stderr which it ran or skipped:
260
262
 
261
263
  ```output
262
- canon subject move: review guard for building → verifying (spec_coverage.require_review, scope: verifying, shipped)
264
+ output: canon subject move: review guard for building → verifying (spec_coverage.require_review, scope: verifying, shipped)
263
265
  ran unreviewed-promotion — every owned scenario needs a review by an actor, and from a session, other than its evidence's
264
266
  ran open-blocker — 1 adopted change(s) checked for open blocker findings
265
267
  ```
@@ -316,8 +318,8 @@ canon subject move demo-subject --to verifying \
316
318
  ```
317
319
 
318
320
  ```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
321
+ 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
322
+ output: canon subject move: demo-subject → verifying — written directly; nothing to promote
321
323
  ```
322
324
 
323
325
  - `--reason "<one line>"` is required for `shipped → verifying` (exit 2
@@ -353,9 +355,11 @@ Planning docs written outside canon become change/task records via
353
355
  `canon subject adopt` lifts an imported change into a managed Subject:
354
356
 
355
357
  ```bash
356
- canon ingest plans --repo . # import → change/task rows
358
+ # import → change/task rows
359
+ canon ingest plans --repo .
360
+ # link change → subject
357
361
  canon subject adopt subject-domain-loop \
358
- --change subject-domain-loop-plan # link change → subject
362
+ --change subject-domain-loop-plan
359
363
  ```
360
364
 
361
365
  A change made with `canon change new` (below) is recorded and adopted
@@ -377,7 +381,7 @@ second subject adds that subject, never re-points the change, and says
377
381
  so:
378
382
 
379
383
  ```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
384
+ 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
385
  ```
382
386
 
383
387
  Every reader joins a subject to its changes from both sides (the
@@ -434,8 +438,8 @@ canon change new add-login --subject auth --title "Add login"
434
438
  ```
435
439
 
436
440
  ```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
441
+ output: canon change new: wrote .canon/changes/add-login/brief.md
442
+ output: canon change new: recorded change `add-login` adopted into subject `auth` — written directly; nothing to promote
439
443
  ```
440
444
 
441
445
  - **Writes** canon's own change dir, `.canon/changes/<slug>/`, holding
@@ -493,8 +497,10 @@ brief (`data.next`, `meta.next`).
493
497
  ### The brief: `canon brief show`, `canon brief check`, `canon brief approve`
494
498
 
495
499
  ```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
500
+ # state and the next one to three steps; never edits
501
+ canon brief show [<slug>]
502
+ # findings; exit 0 clean, 2 blockers, 1 error
503
+ canon brief check [<slug>] [--json]
498
504
  canon brief approve <slug> <slug> --approver <the human> --note "<their words>"
499
505
  ```
500
506
 
@@ -567,8 +573,10 @@ step that writes it.
567
573
  ## Where are we: `canon status`
568
574
 
569
575
  ```bash
570
- canon status # subjects by status, their gaps, next commands
571
- canon status --json # the same report, stable shape
576
+ # subjects by status, their gaps, next commands
577
+ canon status
578
+ # the same report, stable shape
579
+ canon status --json
572
580
  canon status --repo ../other
573
581
  ```
574
582
 
@@ -818,7 +826,8 @@ and `canon gate check` exits `0`; read `data.subjects[]` for the counts and
818
826
 
819
827
  ```bash
820
828
  canon query --kind subject --domain dev --status building
821
- canon report build --repo . # renders the "Subjects" panel
829
+ # renders the "Subjects" panel
830
+ canon report build --repo .
822
831
  ```
823
832
 
824
833
  - `canon query --kind subject [--domain <d>] [--status <s>]` — the