@journeykit/canon 0.30.0 → 0.31.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 (123) hide show
  1. package/dist/skills/SKILL.src.md +47 -21
  2. package/dist/skills/reference/brief-polish.md +4 -4
  3. package/dist/skills/reference/brief-shape.md +38 -7
  4. package/dist/skills/reference/canon-authoring.md +6 -6
  5. package/dist/skills/reference/canon-gate.md +41 -32
  6. package/dist/skills/reference/canon-learn.md +2 -2
  7. package/dist/skills/reference/canon-plan-import.md +2 -2
  8. package/dist/skills/reference/canon-plugins.md +6 -6
  9. package/dist/skills/reference/canon-report-dashboard.md +10 -6
  10. package/dist/skills/reference/canon-review.md +24 -24
  11. package/dist/skills/reference/canon-storage.md +2 -2
  12. package/dist/skills/reference/canon-subject.md +132 -98
  13. package/dist/skills/reference/role-implementer.md +42 -28
  14. package/dist/skills/reference/role-orchestrator.md +36 -21
  15. package/dist/skills/reference/role-planner.md +32 -17
  16. package/dist/skills/reference/role-reviewer.md +48 -30
  17. package/man/canon-adapter-authorize.1 +2 -2
  18. package/man/canon-adapter-validate.1 +2 -2
  19. package/man/canon-adapter.1 +2 -2
  20. package/man/canon-artifacts-move.1 +2 -2
  21. package/man/canon-artifacts-store.1 +2 -2
  22. package/man/canon-artifacts-verify.1 +2 -2
  23. package/man/canon-artifacts.1 +2 -2
  24. package/man/canon-brief-approve.1 +7 -4
  25. package/man/canon-brief-check.1 +2 -2
  26. package/man/canon-brief-show.1 +2 -2
  27. package/man/canon-brief.1 +8 -7
  28. package/man/canon-change-new.1 +4 -4
  29. package/man/canon-change.1 +3 -3
  30. package/man/canon-completion.1 +2 -2
  31. package/man/canon-context-pack-add.1 +2 -2
  32. package/man/canon-context-pack-show.1 +2 -2
  33. package/man/canon-context-pack-verify.1 +2 -2
  34. package/man/canon-context-pack.1 +2 -2
  35. package/man/canon-context.1 +2 -2
  36. package/man/canon-dashboard.1 +2 -2
  37. package/man/canon-demo-init.1 +2 -2
  38. package/man/canon-demo-review.1 +2 -2
  39. package/man/canon-demo.1 +2 -2
  40. package/man/canon-dispatch-begin.1 +10 -10
  41. package/man/canon-dispatch-diff.1 +2 -2
  42. package/man/canon-dispatch-end.1 +2 -2
  43. package/man/canon-dispatch.1 +2 -2
  44. package/man/canon-divergence-accept.1 +6 -6
  45. package/man/canon-divergence-add.1 +6 -6
  46. package/man/canon-divergence-defer.1 +6 -6
  47. package/man/canon-divergence-list.1 +2 -2
  48. package/man/canon-divergence-resolve.1 +6 -6
  49. package/man/canon-divergence.1 +2 -2
  50. package/man/canon-evidence-add.1 +16 -16
  51. package/man/canon-evidence-approval-payload.1 +10 -10
  52. package/man/canon-evidence-audit.1 +10 -10
  53. package/man/canon-evidence.1 +3 -3
  54. package/man/canon-export.1 +2 -2
  55. package/man/canon-feature-new.1 +6 -6
  56. package/man/canon-feature.1 +2 -2
  57. package/man/canon-finding-add.1 +11 -11
  58. package/man/canon-finding-close.1 +8 -8
  59. package/man/canon-finding-list.1 +4 -4
  60. package/man/canon-finding.1 +4 -4
  61. package/man/canon-format.1 +2 -2
  62. package/man/canon-gate-check.1 +2 -2
  63. package/man/canon-gate-install-hooks.1 +2 -2
  64. package/man/canon-gate-promote.1 +2 -2
  65. package/man/canon-gate.1 +2 -2
  66. package/man/canon-ingest-plans.1 +2 -2
  67. package/man/canon-ingest-records.1 +4 -4
  68. package/man/canon-ingest-sessions.1 +2 -2
  69. package/man/canon-ingest.1 +2 -2
  70. package/man/canon-init.1 +2 -2
  71. package/man/canon-inventory-sync.1 +2 -2
  72. package/man/canon-inventory.1 +2 -2
  73. package/man/canon-ledger-compact.1 +2 -2
  74. package/man/canon-ledger.1 +2 -2
  75. package/man/canon-migrate.1 +7 -2
  76. package/man/canon-plugin-sync.1 +2 -2
  77. package/man/canon-plugin.1 +2 -2
  78. package/man/canon-policy-approve.1 +2 -2
  79. package/man/canon-policy-show.1 +2 -2
  80. package/man/canon-policy.1 +2 -2
  81. package/man/canon-prompt-add.1 +2 -2
  82. package/man/canon-prompt-show.1 +2 -2
  83. package/man/canon-prompt.1 +2 -2
  84. package/man/canon-purge.1 +2 -2
  85. package/man/canon-query.1 +5 -5
  86. package/man/canon-regime-show.1 +2 -2
  87. package/man/canon-regime.1 +2 -2
  88. package/man/canon-report-build.1 +2 -2
  89. package/man/canon-report-check.1 +2 -2
  90. package/man/canon-report-snapshot.1 +2 -2
  91. package/man/canon-report.1 +2 -2
  92. package/man/canon-retrieve.1 +5 -5
  93. package/man/canon-review-change.1 +6 -6
  94. package/man/canon-review-scenario.1 +6 -6
  95. package/man/canon-review.1 +2 -2
  96. package/man/canon-scenario-new.1 +10 -10
  97. package/man/canon-scenario.1 +2 -2
  98. package/man/canon-schema.1 +2 -2
  99. package/man/canon-search.1 +104 -0
  100. package/man/canon-selftest.1 +2 -2
  101. package/man/canon-skills-check.1 +2 -2
  102. package/man/canon-skills-doctor.1 +2 -2
  103. package/man/canon-skills-install.1 +2 -2
  104. package/man/canon-skills-show.1 +2 -2
  105. package/man/canon-skills.1 +2 -2
  106. package/man/canon-status.1 +7 -2
  107. package/man/canon-strategy-approval-payload.1 +2 -2
  108. package/man/canon-strategy-promote.1 +2 -2
  109. package/man/canon-strategy-rollback.1 +5 -5
  110. package/man/canon-strategy.1 +2 -2
  111. package/man/canon-subject-adopt.1 +4 -4
  112. package/man/canon-subject-move.1 +5 -5
  113. package/man/canon-subject-new.1 +5 -5
  114. package/man/canon-subject-show.1 +2 -2
  115. package/man/canon-subject-unadopt.1 +5 -5
  116. package/man/canon-subject.1 +2 -2
  117. package/man/canon-task-done.1 +2 -2
  118. package/man/canon-task.1 +2 -2
  119. package/man/canon-tier-age.1 +2 -2
  120. package/man/canon-tier.1 +2 -2
  121. package/man/canon-trace.1 +56 -0
  122. package/man/canon.1 +8 -2
  123. package/package.json +4 -4
@@ -34,8 +34,9 @@ a feature, a one-line request included. A human is needed once per scope:
34
34
  approves the brief and the policy, `.canon/policy.yaml`, which is what
35
35
  the gate grades the work against. The human may also approve it on the `canon dashboard` briefs page; you never use that form.
36
36
  The human records the answer, never you:
37
- `canon brief approve <slug> --approver <their name> --note "<their words>"`
38
- refuses every agent role (`HUMAN_ONLY`). Report that exact call, slug filled in, and
37
+ `canon brief approve <slug> <slug> --approver <their name> --note "<their words>"`
38
+ (one or more slugs, one answer for all, nothing written if any is refused)
39
+ refuses every agent role (`HUMAN_ONLY`). Report that exact call, every slug filled in, and
39
40
  wait; never remove or unset your role (`env -u CANON_ROLE`, dropping `--as`) to run it yourself.
40
41
  Never edit the policy to make the gate pass: a policy that differs from
41
42
  the approved one fails closed (`POLICY_CHANGED_SINCE_APPROVAL`, not
@@ -43,16 +44,20 @@ a feature, a one-line request included. A human is needed once per scope:
43
44
  approval is not approved (`BRIEF_CHANGED_SINCE_APPROVAL`): it needs a new
44
45
  answer. The ship guard and `canon evidence add` refuse an unapproved or
45
46
  edited brief, but those are backstops: the stop itself is at step 1.
46
- - **Approval is scoped.** Evidence for a scenario needs the approval of the
47
- changes its `@flow:<change>.<F-id>` tags name, so a new unapproved change
48
- on a live subject waits on its own scenarios only; `change new` and
49
- `subject adopt` warn when the subject is live. `canon subject unadopt <subject-id> --change-id <change>`
50
- takes a draft change back off (refused once approved). The subject's scope
51
- is its newest adopted change, walking back past pending drafts (brief not
52
- approved, no scenario of the subject tagged `@flow:<change>.*`): reopen,
53
- evidence and ship read the newest approved or tagged change, and the
54
- transition prints one line naming every draft set aside; a draft a scenario
55
- is tagged for blocks until it is approved. `canon skills show canon-subject`
47
+ - **Approval is scoped.** Evidence for a scenario tagged `@flow:<change>.<F-id>`
48
+ needs the approval of the changes its tags name only, so a new unapproved
49
+ change on a live subject waits on its own scenarios; an untagged scenario
50
+ answers to every change with an `Approved:` line (an edited one refuses); `change new` and
51
+ `subject adopt` warn when the subject is live. `canon subject unadopt <subject-id> --change <change>`
52
+ takes a draft change back off (refused once approved). The subject
53
+ answers for every adopted change that is tagged (a scenario of the subject
54
+ is tagged `@flow:<change>.*`), has an `Approved:` line, or is a chore:
55
+ reopen and ship pass only when all of them are approved now, whatever the
56
+ order they were adopted or approved in, and `canon status` lists one entry
57
+ per such change in `briefs`. A pending draft (never approved, not a chore,
58
+ tagged by no scenario) blocks nothing and the transition prints one line
59
+ naming every draft; a draft a scenario is tagged for blocks until it is
60
+ approved. `canon skills show canon-subject`
56
61
  - **Inside an approved scope, never stop for a human.** Fixes, follow-ups,
57
62
  review rounds and re-reviews stay in the approved change (never
58
63
  `canon change new`: a new change is a new scope) and go straight on: scenario,
@@ -110,7 +115,7 @@ needs its change-level review alone. What counts as code is `code.exclude` in
110
115
  `canon status`, `meta.next` and the refusal hint lead with one batch over
111
116
  every report, for example
112
117
  `canon evidence add --all-scenarios --report junit:a.xml --report junit:b.xml --role implementer`
113
- (with the real report paths, and `--project-id <id>` when the repo has several
118
+ (with the real report paths, and `--project <id>` when the repo has several
114
119
  spec roots), never a loop per scenario. With a `--report`, `--class` defaults
115
120
  to `test-run` and `--ref` to the report paths, and the bound cases decide the
116
121
  verdict: any failed case `divergent`, a skipped one and none failed
@@ -137,6 +142,25 @@ refuse, never pass, when it cannot be reached. Moving an existing store with
137
142
  `canon artifacts move --tier <tier>` is the repo owner's decision, not a step
138
143
  of the loop.
139
144
 
145
+ ### Search and trace
146
+
147
+ `canon search "<text>"` finds a record, a brief section or flow step, or a
148
+ scenario's text (title, steps, tables, `Examples`, the Background and Rule it
149
+ inherits) and lists each hit with its neighbors in the record graph: the
150
+ records it names or that name it, the review loop around a finding, the brief
151
+ step a scenario covers. A reference no committed source holds is a neighbor
152
+ marked `placeholder`, never an error. `--kind` (repeatable) keeps hits of those
153
+ kinds, `--expand <N>` sets the hops (default 1), `--limit <N>` the hits (default
154
+ 20). Korean is found from three characters; a shorter text is found as a part of
155
+ a node's text, in id order. The text is read as typed: quotes and `AND` are not
156
+ syntax. `canon trace <finding key|review id|sha>` follows the review, finding,
157
+ fix, re-review and evidence chain of a loop in time order, the archive's
158
+ earlier versions included. Both read, and first refresh, a derived index,
159
+ `.canon/index/search.db`: gitignored, never a source of records, safe to delete
160
+ (the next run rebuilds it, and only `data.index.rebuilt` differs). Records still
161
+ staged, and those in postgres or an object store, are not in it; `data.index`
162
+ names the stores not read.
163
+
140
164
  ## Know your role
141
165
 
142
166
  canon cannot detect a role; you declare it. Pass `--as <role>` or set
@@ -175,7 +199,7 @@ A role is guidance, not enforcement: `--role` on `evidence add` and
175
199
  `warnings` and, for a repeated write that changed nothing, `unchanged: true`.
176
200
  On failure branch on `error.code`, fix the option `error.field` names
177
201
  using the `error.expected` values (`field` is an option name, or a JSON
178
- pointer such as `/items/2/scenarioId` into an `--input` document), and
202
+ pointer such as `/items/2/scenario` into an `--input` document), and
179
203
  follow `error.hint`: every refusal carries a working invocation. The codes
180
204
  are stable; `canon schema --errors --json` lists them and
181
205
  `canon schema --error <CODE>` explains one.
@@ -185,9 +209,11 @@ A role is guidance, not enforcement: `--role` on `evidence add` and
185
209
  A refusal leaves the tree unchanged.
186
210
  - Repeating a write is safe: a record that already exists with identical
187
211
  content is reported `unchanged` (`meta.unchanged`), not duplicated.
188
- - Flags that name an id are spelled `--<noun>-id` everywhere (`--change-id`,
189
- `--project-id`, `--subject-id`, `--scenario-id`, `--actor-id`,
190
- `--session-id`), the primary object of a command is its operand
212
+ - An option that takes an id is the bare noun (`--change add-login`,
213
+ `--scenario auth.login.01`, `--actor reviewer-1`, `--project root`,
214
+ `--session s-1`), and an `--input` document key is that name without `Id`
215
+ (`scenario`, `actor`; the stored record and report JSON keep `scenario_id`,
216
+ `scenarioId`). The primary object of a command is its operand
191
217
  (`canon subject move <id> --to verifying`, `canon change new <slug>`), and
192
218
  `--kind` values carry no `_record` suffix (`--kind evidence`).
193
219
  - `canon status` sends the planner to write the brief while `brief check`
@@ -218,8 +244,8 @@ before anything is written: an invalid element writes nothing and exits 2,
218
244
 
219
245
  ```text
220
246
  canon scenario new --input '[{"operands":["auth.login.01"],"title":"Logs in","case":"happy"},{"operands":["auth.login.02"],"title":"Wrong password","case":"failure"}]'
221
- canon evidence add --input '[{"scenarioId":"auth.login.01","class":"test-run","ref":"npm test","role":"implementer"},{"scenarioId":"auth.login.02","class":"test-run","ref":"npm test","role":"implementer"}]'
222
- canon finding add --input '[{"changeId":"add-login","round":1,"seq":1,"severity":"note","actorId":"reviewer-1","summary":"Token is logged"},{"changeId":"add-login","round":1,"seq":2,"severity":"blocker","introducedUnknown":true,"actorId":"reviewer-1","summary":"No rate limit"}]'
247
+ canon evidence add --input '[{"scenario":"auth.login.01","class":"test-run","ref":"npm test","role":"implementer"},{"scenario":"auth.login.02","class":"test-run","ref":"npm test","role":"implementer"}]'
248
+ canon finding add --input '[{"change":"add-login","round":1,"seq":1,"severity":"note","actor":"reviewer-1","summary":"Token is logged"},{"change":"add-login","round":1,"seq":2,"severity":"blocker","introducedUnknown":true,"actor":"reviewer-1","summary":"No rate limit"}]'
223
249
  ```
224
250
 
225
251
  `--input` never carries `--commit` or `--plan-digest`, and the destructive
@@ -293,10 +319,10 @@ on the list.
293
319
 
294
320
  - **Fix a bug:** the approved change if there is one, else the planner (`canon change new`); first a failing run of the scenario (`canon scenario new --case failure`, then `canon evidence add`), then the fix. `canon skills show role-implementer`.
295
321
  - **Add or change a feature:** `canon subject new`, `canon change new`, then the brief. `canon skills show role-planner`.
296
- - **Pure refactor, dependency, build, CI or infra:** `canon change new <slug> --subject-id <id> --change-kind chore`; data or interface changes are not chores. `canon skills show role-planner`.
322
+ - **Pure refactor, dependency, build, CI or infra:** `canon change new <slug> --subject <id> --change-kind chore`; data or interface changes are not chores. `canon skills show role-planner`.
297
323
  - **Review someone's change:** `CANON_ROLE=reviewer`, `canon brief check <slug>`. `canon skills show role-reviewer`.
298
324
  - **Resume:** `canon status --json`, then follow `meta.next`. `canon skills show role-orchestrator`.
299
- - **Plan more work on a live subject:** `canon change new <slug> --subject-id <id>` (it warns until the brief is approved; `canon subject unadopt` parks a draft). `canon skills show canon-subject`, `canon skills show role-planner`.
325
+ - **Plan more work on a live subject:** `canon change new <slug> --subject <id>` (it warns until the brief is approved; `canon subject unadopt` parks a draft). `canon skills show canon-subject`, `canon skills show role-planner`.
300
326
  - **Upgrade canon in a repo:** `canon migrate` (previews), then `canon migrate --commit --plan-digest <digest>`, then `canon skills install`. See "Upgrading a repo from 0.27" above and `canon skills show role-orchestrator`.
301
327
 
302
328
  **Done when:** you have named your role and loaded its guide, and `canon status --json` lists your next step in `meta.next`.
@@ -151,7 +151,7 @@ still draws a finding. Never waive the rule for the whole section.
151
151
 
152
152
  - en. Before: "On 3 March the team agreed on a grid as the default view." After: "The default view is a grid."
153
153
  - ko. 전: "3월 3일 회의에서 기본 보기를 격자로 하기로 했다." 후: "기본 보기는 격자다."
154
- - waive one date: `<!-- canon:waive prose.decision-history detail "연체 요금은 2026-12-01부터 적용되는 동작이다" match="2026-12-01" -->`
154
+ - waive one date: frontmatter entry `rule: prose.decision-history`, `section: detail`, `reason: "연체 요금은 2026-12-01부터 적용되는 동작이다"`, `match: "2026-12-01"`
155
155
 
156
156
  What it misses: a decision written in other words ("회의에서 정한 대로" is not
157
157
  listed on purpose, it reads as behavior). Over-reports: a date that is
@@ -209,8 +209,8 @@ uses the section key `brief`.
209
209
 
210
210
  - `canon brief check --json`: read the `prose.*` findings, then the sections they sit in. Fix by section, not by rule.
211
211
  - A finding is a prompt to reread, not a verdict. When the flagged text is the precise term (a state name, a quoted label, a command), keep it and waive that rule in that section:
212
- `<!-- canon:waive <rule-id> <section-key> "<reason>" -->`
213
- To waive one finding and keep the rest of the rule live in that section, add the text the finding names: `<!-- canon:waive <rule-id> <section-key> "<reason>" match="<text>" -->` (after the reason's closing quote only a whole `match="<text>"` is allowed; `match =`, `MATCH=`, an unquoted or empty match or extra text is a blocker, and a reason holds no double quote).
212
+ an entry of the frontmatter `waive` list: `- rule: <rule-id>`, `section: <section-key>`, `reason: "<reason>"`.
213
+ To waive one finding and keep the rest of the rule live in that section, add the text the finding names as `match: "<text>"` (an empty reason or match, an unknown rule id or an unknown key is a blocker).
214
214
  A waiver is right when the text is a quotation or a fixed term, and wrong when it excuses a sentence you could simply rewrite. One rule, one section, a reason the human can read.
215
215
 
216
216
  ## The loop
@@ -224,7 +224,7 @@ uses the section key `brief`.
224
224
 
225
225
  - Never change meaning: no flow step, rule, limit or failure path added, removed or softened while rewording.
226
226
  - Never rewrite to dodge a rule (a near synonym, a list used to hide a long sentence); say the concrete thing.
227
- - Never touch flow ids or subheadings, and never polish the markers.
227
+ - Never touch flow ids or subheadings, and never polish the headings.
228
228
  - Never translate the brief to match a `language:` setting.
229
229
  - Never treat a clean run as a claim about who wrote the brief.
230
230
 
@@ -8,26 +8,57 @@ check reports `brief.section-missing`, `brief.section-duplicate`,
8
8
 
9
9
  ## Scope
10
10
 
11
- `.canon/changes/<slug>/brief.md`: section markers, order, and leftover
11
+ `.canon/changes/<slug>/brief.md`: section headings, order, and leftover
12
12
  template text. A feature brief runs intent → outcome → flow → detail →
13
13
  acceptance → questions → out-of-scope; a chore brief runs intent → category →
14
- invariants → checks → out-of-scope (`<!-- canon:kind chore -->` under the
15
- title). Sections are found by their `<!-- canon:section <key> role=… -->`
16
- marker, never by heading text.
14
+ invariants → checks → out-of-scope (`kind: chore` in the frontmatter). Sections
15
+ are found by their English `##` heading, whatever the brief's language; a
16
+ `##` line that is not one of the headings below is body text of the section
17
+ before it. `language:` only picks the language of the prose rules and the
18
+ guidance.
19
+
20
+ | Section | Heading | Role | What goes in it |
21
+ | --- | --- | --- | --- |
22
+ | intent | `## Intent` | context | What problem this solves, for whom, and why now. One to three sentences. |
23
+ | outcome | `## Desired outcome` | normative | What is observably true once this ships, from the user's side. |
24
+ | flow | `## User flow` | normative | A numbered list; each item starts with its id and reads sees → does → becomes: `1. F1 The user sees … → does … → becomes …`. |
25
+ | detail | `## Step detail` | normative | One `### F1 …` subheading per flow step, in the flow's order: rules, states and edge cases of that step. |
26
+ | acceptance | `## Acceptance` | normative | Per step: what must be observable, and which failure path is covered. Do not write scenario ids here. |
27
+ | questions | `## Questions and assumptions` | question | What is unresolved and what you assumed; anything that could change policy goes here. Write `None.` if nothing. |
28
+ | out-of-scope | `## Out of scope` | context | What this change deliberately does not do. Write `None.` if nothing. |
29
+ | category (chore) | `## Category` | context | Exactly one of `refactor`, `dependency`, `build`, `ci`, `infra`. |
30
+ | invariants (chore) | `## Invariants` | normative | What must not change: behavior, public interfaces, data, outputs. A change that alters any of them is a feature, not a chore. |
31
+ | checks (chore) | `## Checks` | normative | How you show the invariants held: the suite that is run again at the chore commit, and anything else you compare. |
32
+
33
+ `canon change new` writes the brief with these headings and nothing under
34
+ them: an empty section is a section not yet written, and `canon brief show`
35
+ prints the guidance of each empty one (in `language:`). A wrong heading
36
+ (`## Intnet`, `## intent`) is reported with the nearest heading.
17
37
 
18
38
  ## Inspect
19
39
 
20
40
  - `canon brief check --json`: each finding names the rule, section key, line and hint.
21
- - Every section present once, in order, with its marker; no `<!-- canon:hint … -->` comment left; no `TODO`/`TBD`; no empty section (write `None.` where nothing applies).
41
+ - Every section present once, in order, with its exact English heading; no `TODO`/`TBD`; no empty section (write `None.` where nothing applies).
22
42
  - The flow is a numbered list with ids `F1`, `F2`, … each reading sees → does → becomes.
23
43
  - Intent describes what the product does today and Outcome what is true after; leftover implementation paths belong in a `Source:` line or nowhere.
24
44
  - A chore's category is exactly one of `refactor`, `dependency`, `build`, `ci`, `infra`; its invariants and checks say something.
25
- - A `canon:waive <rule-id> <section-key> "<reason>"` comment (optionally with `match="<text>"`) is well formed with a real reason.
45
+ - Waivers live in the frontmatter, visible on the page, and the approval covers them:
46
+ ```
47
+ ---
48
+ waive:
49
+ - rule: prose.length
50
+ section: brief
51
+ reason: "why the rule does not apply"
52
+ match: "optional text the finding contains"
53
+ ---
54
+ ```
55
+ Each entry has a known `rule`, a `section` key (or `brief`) and a non-empty `reason`; an unknown key is a blocker. Adding one after the approval is a change after the approval.
56
+ - A brief that still has `<!-- canon:… -->` comments or Korean headings is in the old format: `canon migrate` (preview first) rewrites it, and the human runs `--commit` when it re-binds an approval.
26
57
 
27
58
  ## NEVER
28
59
 
29
60
  - Never fill a section with text you made up to silence a placeholder: write what the request and the repo support, and put the rest in Questions.
30
- - Never delete a section, retag a marker's role, or add a global waiver to pass. A waiver is one rule in one section with a reason the human can read.
61
+ - Never delete a section, rename a heading to another role, or add a global waiver to pass. A waiver is one rule in one section with a reason the human can read.
31
62
  - Never turn a chore's missing invariants into "none" to pass: behavior-changing work is a feature change.
32
63
 
33
64
  ## Verify
@@ -84,7 +84,7 @@ enum, like `lane`.
84
84
  ## Subject pinning
85
85
 
86
86
  Every scenario belongs to a Subject (the durable product unit, see
87
- `canon-subject`). Write the Subject first, then pin with `--subject-id`;
87
+ `canon-subject`). Write the Subject first, then pin with `--subject`;
88
88
  the scenario index carries the tag onto the index record and
89
89
  `spec_coverage.scope` filters on it. An unpinned scenario is invisible to
90
90
  a scoped gate.
@@ -94,9 +94,9 @@ a scoped gate.
94
94
  ```bash
95
95
  canon feature new checkout.cart --title "Cart" # once per surface
96
96
  canon scenario new checkout.cart.01 --title "Adding an item raises the count" \
97
- --subject-id checkout-core --lane behavior --case happy # once per behavior
97
+ --subject checkout-core --lane behavior --case happy # once per behavior
98
98
  canon scenario new checkout.cart.04 --title "Adding an out-of-stock item is refused" \
99
- --subject-id checkout-core --lane behavior --case failure # and its failure path
99
+ --subject checkout-core --lane behavior --case failure # and its failure path
100
100
  ```
101
101
 
102
102
  There is no format or sync step after these: the scaffold writes a
@@ -115,10 +115,10 @@ camelCase keys. `canon schema scenario new --json` shows the input schema
115
115
  and an example. There is no `--from`.
116
116
 
117
117
  ```text
118
- canon scenario new --input '[{"operands":["checkout.cart.01"],"title":"Adding an item raises the count","subjectId":"checkout-core","lane":"behavior","case":"happy"},{"operands":["checkout.cart.04"],"title":"Adding an out-of-stock item is refused","subjectId":"checkout-core","lane":"behavior","case":"failure"}]'
118
+ 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"}]'
119
119
  ```
120
120
 
121
- - `--actor-id <id>` (or `CANON_ACTOR` in the environment) puts YOUR id on the
121
+ - `--actor <id>` (or `CANON_ACTOR` in the environment) puts YOUR id on the
122
122
  provenance line. Default is `canon-scaffold`.
123
123
  - **Keep the `# canon:` lines when you fill in the scenario.** Dropping one
124
124
  makes `canon inventory sync` abort on `missing-provenance`; restore it
@@ -160,7 +160,7 @@ wait for evidence, which `canon gate check` reports.
160
160
  - **A Feature title that is a theme.** "Fidelity of the original
161
161
  experience" names no surface. Name the screen.
162
162
  - **Hand-stamped provenance.** Eighty lines with one timestamp and the
163
- same `agent_id`. Use `canon scenario new --actor-id`.
163
+ same `agent_id`. Use `canon scenario new --actor`.
164
164
  - **Coverage by splitting.** One behavior as two scenarios so two rows go
165
165
  green. One behavior, one id.
166
166
  - **Runner tags as classification.** `@design`, `@p2`, `@wip` are
@@ -129,11 +129,11 @@ A present section, even `require_evidence: false` or a malformed one
129
129
 
130
130
  - **Unimplemented** — no `EvidenceRecord` carries the scenario's
131
131
  `(project_id, scenario_id)`. Author one with
132
- `canon evidence add --scenario-id <id> --project-id <root-id>`; the
132
+ `canon evidence add --scenario <id> --project <root-id>`; the
133
133
  root id is needed once more than one spec root is configured, because
134
134
  two roots may carry the same scenario id. `--summary` is allowed here too (scanned for fabrication
135
135
  markers like a task's); it is a row suffix only when the record also
136
- names a `--task-id`. A record carrying both `--task-id` and `--scenario-id`
136
+ names a `--task`. A record carrying both `--task` and `--scenario`
137
137
  is one attestation answering both joins: it counts for the task cell
138
138
  and for the scenario cell (verdicts, the shipped gate, the review gate).
139
139
  - **Mismatched** — the folded divergence state is `open`,
@@ -161,7 +161,7 @@ A present section, even `require_evidence: false` or a malformed one
161
161
  (a review written before 0.15 has no pins and skips this check), and,
162
162
  when `distinct_actor` (default `true`), is by a reviewer and actor
163
163
  other than the scenario's evidence actor and from a session other than
164
- any evidence session when both carry one (`--session-id`);
164
+ any evidence session when both carry one (`--session`);
165
165
  gaps are `unreviewed-promotion`, and the detail says whether the
166
166
  review is missing, a self-review, stale (naming whether the spec, the
167
167
  evidence or both changed), or not signed. With `block_on_findings`
@@ -218,7 +218,7 @@ report such files.
218
218
  ### Accepted divergence and re-verification
219
219
 
220
220
  ```bash
221
- canon divergence accept cart.add.01 --project-id root --reason "known gap" --tracking "issue#12" --expiry 2026-12-31 --actor-id reviewer-2 --role reviewer --session-id review-session-1
221
+ canon divergence accept cart.add.01 --project root --reason "known gap" --tracking "issue#12" --expiry 2026-12-31 --actor reviewer-2 --role reviewer --session review-session-1
222
222
  ```
223
223
 
224
224
  Accepting a scenario whose latest evidence is `divergent` as a known gap
@@ -240,10 +240,10 @@ Re-verification is one rule with two triggers: a blocker whose latest
240
240
  disposition is `fixed`, and a chore change once approved, keep the subject
241
241
  from shipping until every one of its evidenced scenarios has evidence
242
242
  recorded after the close or the approval. A blocker that names its
243
- scenarios (`finding add --scenario-id`) asks only those, and the refusal
244
- names them with the per-scenario command (`canon evidence add --project-id
245
- <root-id> --scenario-id <id> --report <format>:<path>` plus `--class`, `--role` and `--ref`). Otherwise the
246
- refusal names the scenarios and the batch command: `canon evidence add --project-id <root-id>
243
+ scenarios (`finding add --scenario`) asks only those, and the refusal
244
+ names them with the per-scenario command (`canon evidence add --project
245
+ <root-id> --scenario <id> --report <format>:<path>` plus `--class`, `--role` and `--ref`). Otherwise the
246
+ refusal names the scenarios and the batch command: `canon evidence add --project <root-id>
247
247
  --all-scenarios --report <format>:<path> --class test-run --role
248
248
  implementer --ref "<test command>"`. Reviews pin what the evidence says
249
249
  (its verdict and the identity of its checks), not which record it is, so
@@ -259,17 +259,17 @@ canon never generates or runs an audit. An external evaluator does, and
259
259
  canon records its result:
260
260
 
261
261
  ```bash
262
- canon evidence audit cart.add.01 --project-id root \
262
+ canon evidence audit cart.add.01 --project root \
263
263
  --ref "judge run 42" --role reviewer \
264
- --evaluator-id judge --evaluator-version 1.2 \
264
+ --evaluator judge --evaluator-version 1.2 \
265
265
  --evaluator-digest 9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08 \
266
266
  --audit-result refuted --report junit:audit/counterexample.xml
267
267
  ```
268
268
 
269
- - `evidence audit` requires the scenario operand, `--ref`, `--evaluator-id`,
269
+ - `evidence audit` requires the scenario operand, `--ref`, `--evaluator`,
270
270
  `--evaluator-version`, `--evaluator-digest` (sha256, 64 lowercase hex)
271
- and `--audit-result confirmed|refuted|indeterminate`, with `--project-id`
272
- where several spec roots exist. It takes no `--task-id`, `--verdict`,
271
+ and `--audit-result confirmed|refuted|indeterminate`, with `--project`
272
+ where several spec roots exist. It takes no `--task`, `--verdict`,
273
273
  `--surface-ref` or `--approval-*`; the evaluator flags exist on no other
274
274
  command. All refusals exit `2`.
275
275
  - The record carries `audit: {evaluator_id, evaluator_version,
@@ -303,15 +303,15 @@ The approval is never assumed: the agent writes the brief, stops and
303
303
  reports it, and builds nothing for that scope until the human answers.
304
304
  `canon evidence add` is the backstop for that stop (0.16 M7). It
305
305
  refuses (exit `1`, nothing staged or stored) while a subject the record
306
- attests for has an enforced, unapproved brief: the newest adopted
307
- change's canon `.canon/changes/<change>/brief.md` has no `Approved:`
306
+ attests for has an enforced, unapproved brief: a change the record
307
+ answers to (below) has a canon `.canon/changes/<change>/brief.md` with no `Approved:`
308
308
  line, was edited after its approval (`BRIEF_CHANGED_SINCE_APPROVAL`), or
309
309
  there is no canon `brief.md` yet but the change comes from an
310
310
  external openspec plan with a `proposal.md` under the plan sources (the
311
311
  check `canon status` and the ship guard use; see
312
312
  `canon-subject`; which changes a scenario answers to is scoped by its `@flow:<change>.<F-id>` tags, described there). The subjects are the scenario's `@subject` owner for
313
- `--scenario-id`, and every subject adopting the task's change for
314
- `--task-id`:
313
+ `--scenario`, and every subject adopting the task's change for
314
+ `--task`:
315
315
 
316
316
  ```stderr
317
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>"`
@@ -341,7 +341,7 @@ brief_override:
341
341
  reason: the human approved in chat; the line follows
342
342
  waived:
343
343
  - { class: unreviewed-promotion, subject: demo-subject }
344
- actor: { agent_id: lead } # from --actor-id
344
+ actor: { agent_id: lead } # from --actor
345
345
  ```
346
346
 
347
347
  Under `--json` the same waiver is `data.overrides[]` (`code` of the waived
@@ -376,8 +376,9 @@ review waivers: 1 advisory(ies) — not failing the gate:
376
376
  The waiver lasts only until the subject's next status write.
377
377
 
378
378
  `canon subject move <id> --to shipped` also refuses where a brief exists and
379
- is not approved (the subject's scope, its newest adopted change walking back past pending drafts
380
- no scenario is tagged `@flow:<change>.*` for, has a canon
379
+ is not approved (a change the subject answers for, that is one a scenario of
380
+ the subject is tagged `@flow:<change>.*` for, one with an `Approved:` line,
381
+ or a chore, has a canon
381
382
  `brief.md` with no `Approved:` line or one the brief's text no longer
382
383
  matches, or an external `proposal.md` and no canon `brief.md` yet;
383
384
  see `canon-subject`), as an `unreviewed-promotion` naming
@@ -437,14 +438,14 @@ what backed the claim:
437
438
 
438
439
  ```bash
439
440
  # any file: a Playwright trace, screenshots, an agent QA log — by sha256
440
- canon evidence add --scenario-id cart.add.04 --project-id root \
441
+ canon evidence add --scenario cart.add.04 --project root \
441
442
  --class agent-qa --ref "stagehand run 7" --role implementer \
442
443
  --artifact test-results/cart-trace.zip
443
444
  # a JUnit XML or Cucumber JSON report: also records the matched case + outcome
444
- canon evidence add --scenario-id cart.add.04 --project-id root --class test-run --ref "cargo nextest run" --role implementer --report junit:target/nextest/default/junit.xml
445
- canon evidence add --scenario-id cart.add.04 --project-id root --class test-run --ref "cucumber-js" --role implementer --report cucumber:reports/cucumber.json
445
+ canon evidence add --scenario cart.add.04 --project root --class test-run --ref "cargo nextest run" --role implementer --report junit:target/nextest/default/junit.xml
446
+ canon evidence add --scenario cart.add.04 --project root --class test-run --ref "cucumber-js" --role implementer --report cucumber:reports/cucumber.json
446
447
  # a Swift Testing event stream (swift test --experimental-event-stream-output <path>)
447
- canon evidence add --scenario-id cart.add.04 --project-id root --class test-run --ref "swift test" --role implementer --report swift-testing:.build/events.jsonl
448
+ canon evidence add --scenario cart.add.04 --project root --class test-run --ref "swift test" --role implementer --report swift-testing:.build/events.jsonl
448
449
  ```
449
450
 
450
451
  A case is bound when the scenario id is its OWN: the first
@@ -496,12 +497,12 @@ not close it.
496
497
  One test run proves many scenarios. Bind it once:
497
498
 
498
499
  ```bash
499
- canon evidence add --project-id root --all-scenarios \
500
+ canon evidence add --project root --all-scenarios \
500
501
  --report junit:target/nextest/default/junit.xml \
501
502
  --class test-run --ref "cargo nextest run" --role implementer
502
503
  ```
503
504
 
504
- Each indexed scenario of `--project-id` that owns a case (the rule above)
505
+ Each indexed scenario of `--project` that owns a case (the rule above)
505
506
  gets its own record, with only its own cases. Its verdict comes from
506
507
  them: `faithful` when every case passed, `divergent` when one failed,
507
508
  `not-applicable` when one was skipped and none failed. `--verdict <v>`
@@ -521,18 +522,18 @@ It is all or nothing: a claimed `faithful` verdict over a failed case,
521
522
  a fabrication marker in `--summary`, an unapproved brief for any
522
523
  scenario's subject refuses the whole batch, and nothing is written. Each
523
524
  record binds only what the batch names (nothing is carried forward).
524
- `--scenario-id`, `--task-id`,
525
+ `--scenario`, `--task`,
525
526
  `--report-case`, `canon evidence audit` and the approval flags name one subject
526
527
  and are refused with `--all-scenarios`.
527
528
 
528
- A task's evidence with `--report` and no `--scenario-id` binds EVERY case
529
+ A task's evidence with `--report` and no `--scenario` binds EVERY case
529
530
  in the report, so a `faithful` verdict needs all of them to pass.
530
531
 
531
532
  Many records at once without a report: `--input` takes inline JSON, a file
532
533
  or `-`; an array (or NDJSON) is a batch, validated element by element before
533
534
  the first write (a refusal writes nothing, exit 2). Keys are the option
534
535
  names in camelCase plus `operands`: one object per scenario, with
535
- `scenarioId`, `class`, `ref` and `role`. `canon schema evidence add --json`
536
+ `scenario`, `class`, `ref` and `role`. `canon schema evidence add --json`
536
537
  shows the input schema and an example. Each element reports `written`,
537
538
  `unchanged` or `failed` in `data.items[]`.
538
539
 
@@ -552,7 +553,7 @@ current pinned review saw, `canon evidence add` prints one line per
552
553
  review it makes stale (its verdict or checks differ from what the review saw), with the command that re-pins it:
553
554
 
554
555
  ```stderr
555
- canon evidence add: review by `reviewer-2` of cart.add.01 is now stale (this record's verdict or checks differ from what it saw); re-pin it: canon review scenario cart.add.01 --project-id root --actor-id reviewer-2 --role reviewer --original-spec-ref specs/kind=feature/area=cart/add.feature
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
556
557
  ```
557
558
 
558
559
  The decision is the gate's own (the same stale-review rule `canon gate
@@ -684,7 +685,7 @@ task at all is a loud failure naming every source consulted. A repo with
684
685
  no `.canon/changes/` and `sources: []` is refused: "this repo configures
685
686
  no plan sources (no `.canon/changes` and no `plans.sources` entries in
686
687
  canon.yaml) … write the change with `canon change new`, or list your
687
- external plans under `plans.sources`" (`canon dispatch begin --task-id` says the
688
+ external plans under `plans.sources`" (`canon dispatch begin --task` says the
688
689
  same).
689
690
  It requires
690
691
  a matching non-`Divergent` evidence record, and flips `- [ ]` → `- [x]`
@@ -706,7 +707,7 @@ A flip on evidence that cannot prove the task still succeeds, but warns
706
707
  on stderr: when none of the task's evidence records binds a file and one
707
708
  of them cites a canon command as its `--ref` (`canon gate check`,
708
709
  `canon status`), the warning says to bind the artifact or report that
709
- proves the task (`canon evidence add --task-id <id> --artifact <path>` or
710
+ proves the task (`canon evidence add --task <id> --artifact <path>` or
710
711
  `--report <format>:<path>`). A canon command reports on canon's own
711
712
  records, so it is never the proof of a task.
712
713
 
@@ -779,6 +780,14 @@ canon gate install-hooks --repo .
779
780
  canon gate install-hooks --repo . --event PreToolUse --matcher Edit --command "canon gate check" --timeout 30
780
781
  ```
781
782
 
783
+ It installs no git hook: copy the script into git's `pre-commit` hook
784
+ (`.git/hooks/pre-commit`) or call it from a hook runner. The output names the two
785
+ agent hook files and the script, also under `--dry-run`. In `--json`,
786
+ `installed` is true when an agent hook file changed and
787
+ `preCommitScriptWritten` when the script was written. A `--repo` that is not an
788
+ existing directory exits 2 with `HOOKS_INSTALL_FAILED` (also under `--dry-run`)
789
+ and creates nothing.
790
+
782
791
  Prefer this over hand-editing `settings.json`/`hooks.json`.
783
792
 
784
793
  ## `canon selftest --suite trust-spine-gate`
@@ -183,9 +183,9 @@ record.
183
183
  An operator-requested rollback is a distinct authenticated action:
184
184
 
185
185
  ```text
186
- canon strategy rollback <strategy_id> --reason "<reason>" --actor-id alice \
186
+ canon strategy rollback <strategy_id> --reason "<reason>" --actor alice \
187
187
  --signature-file rollback.sig --approval-at 2026-10-02T12:00:00Z \
188
- [--contradicting-trajectory-id <trajectory_id>] --repo .
188
+ [--contradicting-trajectory <trajectory_id>] --repo .
189
189
  ```
190
190
 
191
191
  `--signature-file` is an armored SSH detached signature over the
@@ -13,8 +13,8 @@ one, and neither can `canon gate`.
13
13
  one `<slug>/` directory per change holding `brief.md` (and a `tasks.md`
14
14
  only when someone adds one: an optional work note), written by
15
15
  `canon change new` (see `canon-subject`). Every plan reader —
16
- `canon ingest plans`, `canon task done`, `canon evidence add --task-id`,
17
- `canon dispatch begin --task-id`, `canon status` and the brief check — reads it
16
+ `canon ingest plans`, `canon task done`, `canon evidence add --task`,
17
+ `canon dispatch begin --task`, `canon status` and the brief check — reads it
18
18
  first whenever the directory exists, with no `plans:` entry. Being first,
19
19
  it owns a change id an external source also carries (the external copy
20
20
  is the later duplicate, skipped and counted under `duplicate_change_id`).
@@ -70,12 +70,12 @@ error; the next step (3) reads the overlay back.
70
70
  ## 3. Project a read
71
71
 
72
72
  ```bash
73
- canon query --kind scenario --plugin-id porting # human table, projected fields appended
74
- canon query --kind scenario --plugin-id porting --json # merged record bodies
75
- canon query --kind scenario # no --plugin-id: byte-identical to the core view
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
76
  ```
77
77
 
78
- `--plugin-id <id>` folds the plugin's overlay records (latest-by-
78
+ `--plugin <id>` folds the plugin's overlay records (latest-by-
79
79
  `(join_key, at)`) onto each queried core record. Fail-soft everywhere:
80
80
 
81
81
  - No overlay record for a core key → that record projects UNMODIFIED (no
@@ -85,7 +85,7 @@ canon query --kind scenario # no --plugin-id: byte-ide
85
85
  SKIPPED + diagnosed; siblings still project.
86
86
  - An absent plugin/manifest → the unmodified core view + a diagnostic,
87
87
  never a process error.
88
- - Omitting `--plugin-id` is byte-identical to `canon query`'s output without
88
+ - Omitting `--plugin` is byte-identical to `canon query`'s output without
89
89
  plugins — it's a purely additive layer.
90
90
 
91
91
  **Done when:** the projected table or JSON shows the plugin's fields on the
@@ -108,7 +108,7 @@ scenarios you synced.
108
108
  (zero new files) and a genuine coverage edit supersedes the prior
109
109
  overlay via latest-`at`-wins.
110
110
  3. `canon plugin sync porting` writes/dedupes those candidates.
111
- 4. `canon query --kind scenario --plugin-id porting [--json]` projects
111
+ 4. `canon query --kind scenario --plugin porting [--json]` projects
112
112
  `covered`/`surface_ref` back onto each scenario.
113
113
 
114
114
  `canon gate check`'s verdicts are byte-identical with and without a
@@ -151,12 +151,16 @@ canon dashboard --snapshot /tmp/canon-snap --port 8080
151
151
  `/briefs.html` lists every live change whose brief awaits the human (no
152
152
  `Approved:` line, or edited since) with its intent, outcome, flow steps,
153
153
  acceptance, questions and assumptions, and its `canon brief check`
154
- blockers and advisories. The human types their name and their answer and
155
- clicks Approve: that records exactly what `canon brief approve
156
- <id> --approver <name> --note <text>` records, the `Approved:` line bound to the
157
- brief digest and the policy approval. It is refused when the brief changed
158
- after the page loaded (`BRIEF_CHANGED_SINCE_PAGE_LOAD`: reload) or while it
159
- has blockers (`BRIEF_APPROVE_REFUSED`). The server is localhost only and
154
+ blockers and advisories. Each brief that has no blocker has a selection box
155
+ and the page shows how many are selected. The human types their name and their
156
+ answer once and clicks Approve: one request carries every selected brief with
157
+ the sha256 of the file the page read, and records exactly what `canon brief
158
+ approve <id>+ --approver <name> --note <text>` records, the `Approved:` line
159
+ bound to the brief digest and the policy approval, for each. A brief that
160
+ changed after the page loaded (`BRIEF_CHANGED_SINCE_PAGE_LOAD`) or that has
161
+ blockers refuses the whole request (409 `BRIEF_APPROVE_REFUSED`, naming each
162
+ refused brief): nothing is written and the page says to read it again. A request
163
+ naming one brief the old way (`changeId`, `sha256`) is still accepted. The server is localhost only and
160
164
  refuses requests from other origins (`DASHBOARD_ORIGIN_REFUSED`).
161
165
 
162
166
  For an agent the human stop is unchanged: when clean briefs are waiting,