@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
@@ -17,7 +17,7 @@ state they are in.
17
17
  The order an agent follows to claim a subject done:
18
18
 
19
19
  1. **Test-run evidence** per owned scenario (`canon evidence add
20
- --scenario-id <id> --project-id <root-id> --session-id <session>` and the
20
+ --scenario <id> --project <root-id> --session <session>` and the
21
21
  rest of its options;
22
22
  final when it returns under `write: { promote: auto }`, which
23
23
  `canon init` writes, and followed by `canon gate promote` only under
@@ -26,9 +26,9 @@ The order an agent follows to claim a subject done:
26
26
  each issue the reviewer raises as a Finding, fix it, then close it:
27
27
 
28
28
  ```bash
29
- canon finding add --change-id c-demo --round 1 --seq 1 \
30
- --severity blocker --introduced-unknown --disposition open --actor-id reviewer-2 \
31
- --summary "unbounded subprocess wait" --session-id review-session-1
29
+ canon finding add --change c-demo --round 1 --seq 1 \
30
+ --severity blocker --introduced-unknown --disposition open --actor reviewer-2 \
31
+ --summary "unbounded subprocess wait" --session review-session-1
32
32
  # ... fix, commit ...
33
33
  canon finding close c-demo --round 1 --seq 1 \
34
34
  --disposition fixed --resolution-rev HEAD
@@ -42,9 +42,9 @@ The order an agent follows to claim a subject done:
42
42
  the evidence is in (the review pins what it saw):
43
43
 
44
44
  ```bash
45
- canon review scenario world.demo.01 --project-id root \
46
- --actor-id reviewer-2 --role reviewer \
47
- --session-id review-session-1 \
45
+ canon review scenario world.demo.01 --project root \
46
+ --actor reviewer-2 --role reviewer \
47
+ --session review-session-1 \
48
48
  --rev HEAD --upstream-ref "pr#41"
49
49
  ```
50
50
 
@@ -72,12 +72,12 @@ key, because two different findings under one key are two reviewers'
72
72
  work collapsed into one identity.
73
73
 
74
74
  ```text
75
- canon finding add --change-id <c> --round <n> --seq <n> \
75
+ canon finding add --change <c> --round <n> --seq <n> \
76
76
  --severity blocker|should-fix|note --disposition open \
77
- --actor-id <who> --summary "<one line>" \
78
- [--rev <rev>] [--resolution-rev <rev>] [--introduced-rev <rev> | --introduced-unknown] [--file-ref path/to/file.rs:120-134] [--scenario-id <id>] [--session-id <id>]
77
+ --actor <who> --summary "<one line>" \
78
+ [--rev <rev>] [--resolution-rev <rev>] [--introduced-rev <rev> | --introduced-unknown] [--file-ref path/to/file.rs:120-134] [--scenario <id>] [--session <id>]
79
79
  canon finding close <c> --round <n> --seq <n> \
80
- --disposition fixed --resolution-rev <rev> [--actor-id <id>] [--session-id <id>] # or rejected / deferred / open
80
+ --disposition fixed --resolution-rev <rev> [--actor <id>] [--session <id>] # or rejected / deferred / open
81
81
  canon finding close <c> --round <n> --seq <n> \
82
82
  --disposition rejected|deferred --reason "<why, one line>"
83
83
  ```
@@ -88,7 +88,7 @@ on the closing version, shown by `finding list` as `[reason: …]` and carried
88
88
  in the JSON. Give the reason when you reject or defer: it is the only record
89
89
  of why the finding was not fixed.
90
90
 
91
- `--scenario-id` (repeatable, each a scenario the index knows; refused with
91
+ `--scenario` (repeatable, each a scenario the index knows; refused with
92
92
  `FINDING_UNKNOWN_SCENARIO` otherwise) names the scenarios the finding is
93
93
  about. After a `fixed` close, fresh evidence for those scenarios alone
94
94
  re-verifies the fix; a finding naming none asks every evidenced scenario of
@@ -103,7 +103,7 @@ run; nothing to promote``); under `write.promote: manual`
103
103
  changes only the disposition (and `resolution_sha` with it); severity,
104
104
  reviewer, summary, and refs are copied from the committed record. It
105
105
  appends a second version at the same natural key — the first stays. Its
106
- `--actor-id` and `--session-id` name who authored the transition; the
106
+ `--actor` and `--session` name who authored the transition; the
107
107
  raiser's stay on the first version.
108
108
 
109
109
  Every commit option (`--rev`, `--resolution-rev`, `--introduced-rev`)
@@ -120,7 +120,7 @@ Outside git, or in a repository with no commit, the write is refused with
120
120
 
121
121
  Many findings at once: `--input` takes inline JSON, a file or `-`; an array
122
122
  (or NDJSON) is a batch validated before any write. Keys are the option
123
- names in camelCase (`changeId`, `actorId`, `resolutionRev`); `canon schema
123
+ names in camelCase (`change`, `actor`, `resolutionRev`); `canon schema
124
124
  finding add --json` shows the input schema and an example. An identical
125
125
  repeat is `unchanged` (`meta.unchanged`) and writes nothing; a different
126
126
  finding under an occupied key is `FINDING_SEQ_OCCUPIED`.
@@ -128,13 +128,13 @@ finding under an occupied key is `FINDING_SEQ_OCCUPIED`.
128
128
  ### A defect the author found in its own work
129
129
 
130
130
  Record it like any other finding, at the moment you find it and before
131
- review, with `--actor-id` set to your own id; never ask
131
+ review, with `--actor` set to your own id; never ask
132
132
  the reviewer to record it later under theirs. Nothing refuses a finding
133
133
  whose reviewer is also the implementer, and an open `blocker` the author
134
134
  raised blocks under `block_on_findings` exactly like a reviewer's.
135
135
  `--introduced-rev` names the commit that introduced the defect, not who
136
136
  found it: `--introduced-rev self` is a usage error (exit 2) that points at
137
- `--actor-id`.
137
+ `--actor`.
138
138
 
139
139
  ### Severities
140
140
 
@@ -169,20 +169,20 @@ fixed finding reopened with `--disposition open` counts as open again.
169
169
  ## `canon review scenario`
170
170
 
171
171
  ```text
172
- canon review scenario <scenario-id> [<scenario-id>] --project-id <root-id> \
173
- --actor-id <who> [--rev <rev>] --role <role> \
174
- (--upstream-ref <ref> | --original-spec-ref <ref>) [--session-id <id>] \
172
+ canon review scenario <scenario-id> [<scenario-id>] --project <root-id> \
173
+ --actor <who> [--rev <rev>] --role <role> \
174
+ (--upstream-ref <ref> | --original-spec-ref <ref>) [--session <id>] \
175
175
  [--review-outcome signed|indeterminate|unavailable] \
176
176
  [--reviewer-provider <p>] [--reviewer-model <m>] [--reviewer-harness <h>] \
177
177
  [--artifact <path>] [--artifact <path>] [--max-artifact-mib <MiB>]
178
- canon review change <slug> --project-id <root> --actor-id <who> [--rev <rev>] \
178
+ canon review change <slug> --project <root> --actor <who> [--rev <rev>] \
179
179
  --role reviewer (--upstream-ref <ref> | --original-spec-ref <ref>) # a chore, below
180
180
  ```
181
181
 
182
182
  One attributed Review record per scenario, joined on
183
183
  `(project_id, scenario_id)` — both are required because two spec roots
184
184
  may carry the same scenario id. Exactly one provenance ref is required.
185
- `--actor-id` is the reviewer and is required; `--session-id` fills `actor.session_id`.
185
+ `--actor` is the reviewer and is required; `--session` fills `actor.session_id`.
186
186
  A Review carries no verdict about the behavior; the `verifying → shipped`
187
187
  ship gate still refuses missing or `Divergent` evidence verdicts on its
188
188
  own (a `Divergent` one passes only while a human's `canon divergence
@@ -321,7 +321,7 @@ sign-off or a rejection, and `review scenario` says so. `--reviewer-provider`,
321
321
  `--reviewer-model` and `--reviewer-harness` record the runtime that
322
322
  produced the review, as given. An empty or padded label is a usage error.
323
323
 
324
- `--session-id` (on `evidence add`, `review scenario`, `finding add` and
324
+ `--session` (on `evidence add`, `review scenario`, `finding add` and
325
325
  `finding close`) takes one opaque token: non-empty, no surrounding
326
326
  whitespace, no control characters. Anything else is a usage error
327
327
  (exit 2) and nothing is written.
@@ -346,7 +346,7 @@ spec_coverage:
346
346
  `spec_coverage.scope`. `spec_coverage.exclude_lanes` also exempts
347
347
  scenarios from review.
348
348
  - **`distinct_actor`** — a review only counts when both its `reviewer`
349
- and its actor (`--actor-id`) differ from every actor on that
349
+ and its actor (`--actor`) differ from every actor on that
350
350
  scenario's evidence records, and, when the review carries a session,
351
351
  that session differs from every session on those evidence records. An
352
352
  agent cannot review its own attested work, under another id or from
@@ -484,7 +484,7 @@ routing. The new Subject record lists exactly the violations it let
484
484
  through:
485
485
  `status_override: {to, reason, waived: [{class, subject}], actor: {agent_id}}`
486
486
  (`subject` is the scenario id, or `<change_id>#<round>.<seq>` for a
487
- blocker; actor from `--actor-id`, default `canon`). The command prints
487
+ blocker; actor from `--actor`, default `canon`). The command prints
488
488
  the `waived ...` lines and `override recorded by `lead` for the N
489
489
  violation(s) above: <reason>`. A blank or multi-line reason exits 2. The
490
490
  waiver covers only what it recorded, and clears on the subject's next
@@ -265,8 +265,8 @@ write:
265
265
  `--promote` / `--no-promote` decide for one write and win over the config
266
266
  without reading it; both at once is a usage error (exit `2`). If the
267
267
  promotion refuses the command's own record, the record stays staged, the
268
- line ends ``— run `canon gate promote` to commit it`` (`canon divergence
269
- promote` for a divergence), and the command exits `1`. With neither flag,
268
+ line ends ``— run `canon gate promote` to commit it`` (`canon gate promote
269
+ --kind divergence` for a divergence), and the command exits `1`. With neither flag,
270
270
  a malformed `write:` section (anything but `promote: auto | manual`)
271
271
  refuses the write, exit `2`, before anything is staged. `canon init
272
272
  --check` reports `[PASS] write (promote: auto)`, `[not configured]
@@ -64,7 +64,7 @@ canon subject new subject-domain-loop \
64
64
  `domain` enum has no constraint and accepts any slug. Run
65
65
  `canon context` to see the set in force.
66
66
  - `--summary` is optional; `--owner-role` defaults to `implementer`;
67
- `--actor-id` defaults to `canon`. A freshly-authored Subject with no
67
+ `--actor` defaults to `canon`. A freshly-authored Subject with no
68
68
  adopted changes and no tagged scenarios is a valid minimal record and
69
69
  starts at status `proposed`. `--json` prints the written record. Several
70
70
  subjects at once: `--input` takes inline JSON, a file or `-`; an array (or
@@ -76,12 +76,12 @@ canon subject new subject-domain-loop \
76
76
  ### 2. Retrieve subject-scoped strategy memory BEFORE working
77
77
 
78
78
  ```bash
79
- canon retrieve --for-role dev --domain dev --subject-id subject-domain-loop --limit 5
79
+ canon retrieve --for-role dev --domain dev --subject subject-domain-loop --limit 5
80
80
  ```
81
81
 
82
- - `--regime <key>` XOR the derived pair `--domain <d> [--subject-id <id>]`:
82
+ - `--regime <key>` XOR the derived pair `--domain <d> [--subject <id>]`:
83
83
  give the full regime key directly, OR let retrieval derive it. Never both.
84
- - With `--domain`/`--subject-id`, retrieval tries `<domain>-<subject_id>`
84
+ - With `--domain`/`--subject`, retrieval tries `<domain>-<subject_id>`
85
85
  first, then `<domain>` — a subject's own lessons win, falling back to
86
86
  the domain's shared memory when the subject is new.
87
87
  - Fail-soft: an empty/missing store degrades to an empty guidance list,
@@ -89,7 +89,7 @@ canon retrieve --for-role dev --domain dev --subject-id subject-domain-loop --li
89
89
 
90
90
  ### 3. Work → pin every artifact to `subject_id`
91
91
 
92
- - **Scenarios**: `canon scenario new <tag> --subject-id <subject-id>` emits
92
+ - **Scenarios**: `canon scenario new <tag> --subject <subject-id>` emits
93
93
  the `@subject:<subject-id>` tag; the scenario index maps the tag onto
94
94
  the scenario's `subject_id` the next time a command reads it (no sync
95
95
  step; see `canon-inventory`). A malformed/absent tag leaves it unset
@@ -97,7 +97,7 @@ canon retrieve --for-role dev --domain dev --subject-id subject-domain-loop --li
97
97
  - **Changes**: adopt imported plan changes under the Subject (see
98
98
  [Adopt flow](#adopt-flow)).
99
99
  - **Reviews / evidence / divergences**: authored as usual (`canon review
100
- add`, `canon finding add`, `canon gate`, `canon divergence …`) against
100
+ scenario`, `canon finding add`, `canon gate check`, the `canon divergence` leaves) against
101
101
  the Subject's scenarios and adopted changes — the join spine carries
102
102
  them back through `scenario_id` and `change_id`. See `canon-review`
103
103
  for the finish-a-subject order.
@@ -113,7 +113,7 @@ this subject and domain — the exact memory step 2 retrieves next time.
113
113
 
114
114
  ## Status lifecycle + the shipped evidence gate
115
115
 
116
- `canon subject move <id> --to <state> [--override-reason <text>] [--reason <text>] [--actor-id <id>]`
116
+ `canon subject move <id> --to <state> [--override-reason <text>] [--reason <text>] [--actor <id>]`
117
117
  performs a policy-gated transition.
118
118
  The legal chain:
119
119
 
@@ -169,7 +169,7 @@ exits 1, and the status stays `verifying`. `retired` is not gated.
169
169
  **The brief (0.16, 0.17, 0.19):** `verifying → shipped` also refuses where a brief
170
170
  exists and is not approved, read exactly as `canon status` reads it (one
171
171
  shared check, so status says `ship` only when this passes): the subject's
172
- scope (its newest adopted change, except pending drafts, see below) has a canon `.canon/changes/<change>/brief.md`
172
+ scope (every change the subject answers for, see below) has a canon `.canon/changes/<change>/brief.md`
173
173
  with no `Approved:` line outside HTML comments and code fences, whose
174
174
  `Approved:` line carries a digest the brief's text no longer matches
175
175
  (`BRIEF_CHANGED_SINCE_APPROVAL`; the brief was edited after the human
@@ -214,29 +214,43 @@ scenario whose `@subject` owner, or a task whose change
214
214
  an adopting subject, has an enforced, unapproved brief, through the same
215
215
  check; see `canon-gate`.
216
216
 
217
- **Approval is scoped to the scenarios a change names.** The evidence
218
- refusal asks the approval of each change the scenario's `@flow:<change>.<F-id>`
219
- tags name. An untagged scenario answers to the subject's newest adopted
220
- change whose brief carries an `Approved:` line; if no adopted change ever
221
- did, a scenario with evidence recorded before the subject first adopted a
222
- change (and without a brief override) needs no brief, and one without
223
- answers to the newest adopted change. A task answers to its own
224
- change. So planning the next change for a `building`, `verifying` or
225
- `shipped` subject does not freeze re-verification of its existing
226
- scenarios; scenarios written for the new change (`canon scenario new
227
- --flow <F-id>` tags them) wait for its approval. The ship guard above is
228
- unchanged, except for a pending draft: the subject cannot ship until its
229
- scope is approved, where the scope is found walking the adopted changes from
230
- the newest back: a change whose brief was never approved and that no scenario
231
- of the subject is tagged `@flow:<change>.*` for is a pending draft and is
232
- skipped; the first approved or tagged change is the scope. A tagged unapproved
233
- draft anywhere above the newest approved change therefore blocks. The reopen
234
- and ship transitions print one line naming every change set aside (stderr, and
235
- `meta.warnings` with `--json`): ``<change> is a pending draft (brief not
236
- approved, no scenario tagged `@flow:<change>.*`): not part of this transition,
237
- which reads the subject's newest approved or tagged change``, and `canon status`
238
- marks them "not yet part of the subject's scope" (`pendingDrafts` in JSON)
239
- while still asking the human to answer a draft's brief.
217
+ **A subject answers for the changes it tags, approved or adopted as chores
218
+ (0.31).** Reopen, ship and `canon status` judge every adopted change that is
219
+ (1) *tagged*: a scenario the subject owns is tagged `@flow:<change>.*`;
220
+ (2) *approved*: its brief has an `Approved:` line, even if the brief was
221
+ edited since, and even if the tags were removed; or (3) a *chore*, which has
222
+ no scenario to tag and so governs from the moment it is adopted. They pass
223
+ only when every one of them reads as approved now; an unapproved one and one
224
+ edited after its approval (`BRIEF_CHANGED_SINCE_APPROVAL`) are named for
225
+ different reasons, and the answer does not depend on the order the changes
226
+ were adopted or approved. The ship guard's brief-structure check runs over
227
+ all of them. When several block, the hint is one
228
+ ``canon brief approve <id>+ --approver <the human> --note "<their words>"``
229
+ line naming every blocking change; a reopen refusal cannot be passed with
230
+ `--override-reason`, a ship brief refusal can. A change never approved, not a
231
+ chore and tagged by no scenario is a *pending draft* and blocks nothing: the
232
+ reopen and ship transitions print one line naming every pending draft
233
+ (stderr, and `meta.warnings` with `--json`): ``<change> is a pending draft
234
+ (never approved, not a chore, no scenario tagged `@flow:<change>.*`): not part
235
+ of this transition, which judges the subject's tagged, approved and chore
236
+ changes``, and `canon status` marks them "not yet part of the subject's
237
+ scope" (`pendingDrafts` in JSON) while still asking the human to answer a
238
+ draft's brief. A subject with no such change at all keeps the older rule: its
239
+ newest adopted change awaits its first approval.
240
+
241
+ **Evidence follows the scenario.** A scenario tagged `@flow:<change>.<F-id>`
242
+ answers only to the changes its tags name (counting only changes the subject
243
+ adopted); another change's edit after its approval never blocks it. An untagged
244
+ scenario (one whose tags were removed reads the same) answers to every adopted
245
+ change with an `Approved:` line, a chore included, so an approved change edited
246
+ since refuses it (`BRIEF_CHANGED_SINCE_APPROVAL`); an unapproved chore blocks
247
+ reopen and ship, never evidence. When no adopted change has an approved line,
248
+ a scenario with evidence recorded before the subject first adopted a change
249
+ (and without a brief override) needs no brief, and one without answers to the
250
+ newest adopted change. A task answers to its own change. So planning the next
251
+ change for a `building`, `verifying` or `shipped` subject does not freeze
252
+ re-verification of its existing scenarios; scenarios written for the new
253
+ change (`canon scenario new --flow <F-id>` tags them) wait for its approval.
240
254
 
241
255
  ### The review guard (`spec_coverage.require_review`, opt-in)
242
256
 
@@ -262,7 +276,7 @@ sampled audit; see `canon-gate`.
262
276
 
263
277
  ```bash
264
278
  canon subject move demo-subject --to verifying \
265
- --override-reason "reviewer out until Monday" --actor-id lead
279
+ --override-reason "reviewer out until Monday" --actor lead
266
280
  ```
267
281
 
268
282
  `--override-reason "<one line>"` waives ONLY `unreviewed-promotion` (the
@@ -279,7 +293,7 @@ status_override:
279
293
  reason: reviewer out until Monday
280
294
  waived:
281
295
  - { class: unreviewed-promotion, subject: world.demo.01 }
282
- actor: { agent_id: lead } # from --actor-id, default `canon`
296
+ actor: { agent_id: lead } # from --actor, default `canon`
283
297
  ```
284
298
 
285
299
  A blank or multi-line reason exits 2. The waiver is cleared on the
@@ -298,7 +312,7 @@ scenario, evidence, review, ship.
298
312
 
299
313
  ```bash
300
314
  canon subject move demo-subject --to verifying \
301
- --reason "follow-up: the retry path leaks a file handle" --actor-id impl
315
+ --reason "follow-up: the retry path leaks a file handle" --actor impl
302
316
  ```
303
317
 
304
318
  ```output
@@ -313,9 +327,8 @@ canon subject move: demo-subject → verifying — written directly; nothing to
313
327
  records the change the work goes into:
314
328
  `reopen: { reason: …, change: c-demo, actor: { agent_id: impl } }`. The
315
329
  next status write clears both, as it clears a waiver.
316
- - Inside the approved scope it needs no human: the subject's scope (its
317
- newest adopted change, walking back past pending drafts no scenario is
318
- tagged for) has an approved brief, or there
330
+ - Inside the approved scope it needs no human: every change the subject
331
+ answers for (tagged, approved or chore) has an approved brief, or there
319
332
  is no brief to answer.
320
333
  Otherwise it refuses, exit 1, naming the brief to approve, and
321
334
  `--override-reason` does not waive it:
@@ -342,19 +355,19 @@ Planning docs written outside canon become change/task records via
342
355
  ```bash
343
356
  canon ingest plans --repo . # import → change/task rows
344
357
  canon subject adopt subject-domain-loop \
345
- --change-id subject-domain-loop-plan # link change → subject
358
+ --change subject-domain-loop-plan # link change → subject
346
359
  ```
347
360
 
348
361
  A change made with `canon change new` (below) is recorded and adopted
349
362
  already. A `tasks.md` in it is an optional work note nothing gates on
350
363
  (`canon change new` no longer writes one); an existing one is still read:
351
- `canon evidence add --task-id` and `canon task done` read the file as it is,
364
+ `canon evidence add --task` and `canon task done` read the file as it is,
352
365
  and a flip re-imports that change itself. Run `canon ingest plans` for a
353
366
  plan written outside canon, or when `canon query --kind task` must list
354
367
  rows nobody has flipped yet.
355
368
 
356
369
  `adopt` adds the subject to the change's `subject_ids` and the change to
357
- the Subject's `change_ids`, so `canon query --kind change --change-id <id>`
370
+ the Subject's `change_ids`, so `canon query --kind change --change <id>`
358
371
  and `canon query --kind subject` agree on the link from both ends. It
359
372
  writes only the side that lacks the link, so rerunning it on a linked
360
373
  pair writes nothing and exits `0`.
@@ -379,27 +392,28 @@ The two records cannot be written atomically, so the subject is written
379
392
  first: a half-done adoption still has that change's open blockers
380
393
  counted. If the change write then fails, `adopt` exits `2`, names the
381
394
  subject that now lists the change, and prints `canon subject adopt
382
- <subject_id> --change-id <change_id>` to complete it.
395
+ <subject_id> --change <change_id>` to complete it.
383
396
 
384
- `canon change new <slug> --subject-id <S>` and `canon subject adopt
385
- <S> --change-id <change>` print a warning on stderr, and in JSON
397
+ `canon change new <slug> --subject <S>` and `canon subject adopt
398
+ <S> --change <change>` print a warning on stderr, and in JSON
386
399
  `meta.warnings`, when `<S>` is `building`, `verifying` or `shipped` and the
387
400
  change's brief is not approved. The warning says the subject's existing
388
401
  scenarios keep accepting evidence and the subject can still be reopened and
389
402
  shipped for its approved scope while no scenario is tagged `@flow:<change>.*`;
390
403
  once one is, the subject cannot ship until the brief is approved. The draft is
391
- pending until then: the reopen and ship guards read the newest adopted change
392
- with an `Approved:` line instead, so a shipped subject can reopen, re-verify
393
- and ship again while the draft waits; `canon status` still lists it.
404
+ pending until then: the reopen and ship guards judge only the changes the
405
+ subject answers for (tagged, approved or chore), so a shipped subject can
406
+ reopen, re-verify and ship again while the draft waits; `canon status` still
407
+ lists it.
394
408
 
395
409
  **Done when:** `canon subject show <id>` lists the change in the subject's
396
- `change_ids` and `canon query --kind change --change-id <change>` lists the
410
+ `change_ids` and `canon query --kind change --change <change>` lists the
397
411
  subject in the change's `subject_ids`.
398
412
 
399
413
  ### Taking a draft change back out: `canon subject unadopt`
400
414
 
401
415
  ```text
402
- canon subject unadopt <S> --change-id <change> --json
416
+ canon subject unadopt <S> --change <change> --json
403
417
  ```
404
418
 
405
419
  Removes the link on both records and keeps the change and its brief, so the
@@ -416,7 +430,7 @@ For a change that does not exist yet, one command writes it and records
416
430
  it adopted:
417
431
 
418
432
  ```bash
419
- canon change new add-login --subject-id auth --title "Add login"
433
+ canon change new add-login --subject auth --title "Add login"
420
434
  ```
421
435
 
422
436
  ```output
@@ -427,11 +441,10 @@ canon change new: recorded change `add-login` adopted into subject `auth` — wr
427
441
  - **Writes** canon's own change dir, `.canon/changes/<slug>/`, holding
428
442
  `brief.md` only: the PRD skeleton (`# <title>`, then the sections intent,
429
443
  desired outcome, user flow, step detail, acceptance, questions and
430
- assumptions, out of scope, each opened by a
431
- `<!-- canon:section <key> role=… -->` marker and a hint comment, with
432
- the headings in the repo's `language:`), and a comment naming
433
- `canon brief approve` as where the human's answer is recorded, so the
434
- file starts unapproved. The intent section's first paragraph holds the
444
+ assumptions, out of scope, each an empty `##` section under its English
445
+ heading; no comment, no hint: `canon brief show` and the brief-shape skill
446
+ carry each section's guidance, in the repo's `language:`), so the file
447
+ starts unapproved. The intent section's first paragraph holds the
435
448
  title, which imports as the change summary. `--change-kind chore` writes the
436
449
  short chore brief instead (intent, category, invariants, checks, out of
437
450
  scope; see `canon brief` below). No `tasks.md` is written:
@@ -452,7 +465,7 @@ canon change new: recorded change `add-login` adopted into subject `auth` — wr
452
465
  cannot be written atomically. If the change record fails after the
453
466
  subject record was written, the command exits `2`, keeps the change
454
467
  dir and that one subject record, and prints the repair, `canon ingest
455
- plans && canon subject adopt <id> --change-id <slug>`, which completes
468
+ plans && canon subject adopt <id> --change <slug>`, which completes
456
469
  the link and is safe to rerun.
457
470
  - **Refuses** with exit `2`, writing nothing, when the subject does not
458
471
  exist, or when the slug already has a `.canon/changes/<slug>` dir
@@ -461,7 +474,7 @@ canon change new: recorded change `add-login` adopted into subject `auth` — wr
461
474
  A `tasks.md` is an optional work note canon never writes and nothing
462
475
  gates on. If one exists, rows added as `- [ ] <n> <title>` are flipped with
463
476
  `canon task done <slug>#<n>` once evidence exists; `canon evidence add
464
- --task-id` and the flip read the file as it is, so a new row needs no
477
+ --task` and the flip read the file as it is, so a new row needs no
465
478
  `canon ingest plans` run.
466
479
 
467
480
  A change from an external plan (an openspec change listed under
@@ -482,19 +495,35 @@ brief (`data.next`, `meta.next`).
482
495
  ```text
483
496
  canon brief show [<slug>] # state and the next one to three steps; never edits
484
497
  canon brief check [<slug>] [--json] # findings; exit 0 clean, 2 blockers, 1 error
485
- canon brief approve <slug> --approver <the human> --note "<their words>"
498
+ canon brief approve <slug> <slug> --approver <the human> --note "<their words>"
486
499
  ```
487
500
 
501
+ `canon brief approve` takes one or more slugs and records the same name, the
502
+ same answer and one timestamp on every brief and on the policy approval
503
+ (0.31). Every slug is checked before anything is written (a brief that
504
+ exists and reads, no blocker, a one-line name and answer, a body that does not
505
+ end inside an open code fence); one refused slug refuses the call, exit 2
506
+ `BRIEF_APPROVE_REFUSED` naming each refused slug with its own code, and
507
+ nothing is written. A repeated slug counts once. With one slug the output is as
508
+ before; with several, `--json` carries one entry per slug (`written`,
509
+ `unchanged`, `failed`, `skipped`). A disk error while writing exits 1 and
510
+ leaves the slugs already written approved. The call is human-only for every
511
+ slug; the `HUMAN_ONLY` hint gives the human one line with all the slugs.
512
+
488
513
  Without the change operand, the repo's only live change is read. The brief is the
489
- PRD `canon change new` writes; sections are found by their
490
- `<!-- canon:section <key> role=… -->` marker, never by heading text. A
491
- brief without markers (written before 0.19) checks as `brief.legacy`, an
492
- advisory, and is read as before.
514
+ PRD `canon change new` writes; sections are found by their English `##`
515
+ heading, never by a comment. A brief with fewer than two known headings
516
+ (written before 0.19) checks as `brief.legacy`, an advisory, and is read as
517
+ before. A brief with `<!-- canon:… -->` comments or Korean headings is in the
518
+ 0.30 format: `brief check` gives it one blocker (`brief.old-format`) and
519
+ `canon migrate` rewrites it; `status`, `gate check`, `context` and `brief show`
520
+ warn, and a command that writes and judges it is refused
521
+ (`OLD_BRIEF_FORMAT`).
493
522
 
494
523
  - **Blockers (structure, `brief.*`)**: a missing, duplicated or
495
524
  out-of-order section; a flow with no steps or a duplicate step id;
496
525
  step detail that does not match the flow ids in order; leftover
497
- placeholders (a hint comment still there, an empty section,
526
+ placeholders (an empty section,
498
527
  `TODO`/`TBD`); a malformed waiver; for a chore, a flow/detail/acceptance
499
528
  section, a category outside `refactor|dependency|build|ci|infra`, or
500
529
  invariants/checks that say only "none". Once the subject is `building`
@@ -507,8 +536,8 @@ advisory, and is read as before.
507
536
  length budgets, in the normative sections plus Intent and Questions;
508
537
  sentence, paragraph and key-sentence rules in normative sections.
509
538
  Never an authorship verdict. `canon skills show brief-polish` has each rule.
510
- - **Waivers**: `<!-- canon:waive <rule-id> <section-key> "<reason>" -->`,
511
- one rule in one section, reason required; add `match="<text>"` to waive
539
+ - **Waivers**: entries of the frontmatter `waive` list (`rule`, `section`,
540
+ `reason`), one rule in one section, reason required; add `match` to waive
512
541
  only the findings whose matched text contains it. Listed in the output.
513
542
  - **`approve`** records the human's answer: it writes
514
543
  `Approved: <who> <when> — "<words>" (brief sha256:<digest>)` into the
@@ -568,14 +597,14 @@ uses, so status and the gate cannot disagree about a gap:
568
597
  `require_review` on, a `warning:` line counts the in-effect reviews that
569
598
  predate 0.23 review pinning and wait for `canon migrate`.
570
599
  - **Brief** (0.16, 0.17, 0.19) — for each `building`, `verifying` or `shipped`
571
- subject, and each `proposed` subject whose newest adopted change has a
600
+ subject, and each `proposed` subject whose adopted change has a
572
601
  brief (a canon `brief.md`, or an external `proposal.md`), a `brief:`
573
- line under it says whether the brief is approved:
602
+ line per change the subject answers for (tagged, approved or chore) says
603
+ whether that brief is approved:
574
604
  `brief: approved in <brief> — <the recorded words>`, or
575
605
  `brief: not approved (…)`. Approval is an `Approved:` line in
576
- `.canon/changes/<change>/brief.md` for the subject's newest adopted
577
- change (the last one adopted; a live dir before an archived one), and
578
- only there: a line that starts with `Approved:` (indent allowed), says
606
+ `.canon/changes/<change>/brief.md` for the change (a live dir before an
607
+ archived one), and only there: a line that starts with `Approved:` (indent allowed), says
579
608
  something after it, and sits outside HTML comments (`<!-- … -->`,
580
609
  multi-line included) and fenced code blocks (```` ``` ```` or `~~~`).
581
610
  `canon brief approve` writes it from the human's answer, as
@@ -612,7 +641,7 @@ uses, so status and the gate cannot disagree about a gap:
612
641
  (owned scenarios, verdicts, required cases, audits, and, where
613
642
  `require_review` covers `shipped`, reviews and open blockers under
614
643
  `block_on_findings`; a gap its waiver recorded is not counted), prints
615
- ``gate: red — <n> <class> (first: <subject>), …; reopen with `canon subject move <id> --to verifying --reason "<why it reopens>" --actor-id <unit>` ``,
644
+ ``gate: red — <n> <class> (first: <subject>), …; reopen with `canon subject move <id> --to verifying --reason "<why it reopens>" --actor <unit>` ``,
616
645
  carries `red: [...]` in `--json`, and that reopen is its only `next:`
617
646
  step. Open blockers count against every subject a change belongs to.
618
647
  - **Unowned** — scenarios with no `@subject:` tag.
@@ -637,19 +666,19 @@ uses, so status and the gate cannot disagree about a gap:
637
666
  first, then building, specced, proposed, shipped: an unapproved brief
638
667
  (building and later, and proposed once its change has a brief)
639
668
  → the human step below, which is then the subject's
640
- only step; no scenario → `canon scenario new --subject-id <id>` (and, when no
669
+ only step; no scenario → `canon scenario new --subject <id>` (and, when no
641
670
  `.feature` file holds a scenario yet, `canon feature new` first, unless
642
671
  every subject waits on its brief); a missing case → `canon scenario new
643
- <surface>.<next nn> … --case <case>`; (building and later) an
644
- unevidenced or divergent scenario → `canon evidence add --scenario-id
645
- …` (its reason adds "then `canon gate promote`" only under
672
+ <surface>.<nn> --title "<what happens on this path>" --subject <id> --case <case>`; (building and later) an
673
+ unevidenced or divergent scenario → `canon evidence add --scenario <id>
674
+ --project <root> --class test-run --role implementer --ref "<test command>"` (its reason adds "then `canon gate promote`" only under
646
675
  `write.promote: manual`); an open blocker → `canon finding close <change>`; a
647
676
  due, unreviewed, evidenced scenario → `canon review scenario <id>` from another
648
677
  session; none of these → `canon subject move <id> --to <next status>`.
649
678
  Last, unowned scenarios → `canon status`, after tagging them in their
650
679
  `.feature` files (status re-indexes the edit; no sync step). With more
651
680
  than one `specs.roots[]` entry, every suggested `canon scenario new`
652
- carries `--project-id`: the owning root for a missing case, `<root-id>`
681
+ carries `--project`: the owning root for a missing case, `<root-id>`
653
682
  (the reason lists the roots) for a subject that owns no scenario yet.
654
683
  - **The one human step.** Every step is the agent's to run except one,
655
684
  and it comes only once the brief is clean: while `canon brief check
@@ -701,17 +730,17 @@ Status fails closed rather than suggest progress the gate would refuse:
701
730
 
702
731
  ```json
703
732
  {
704
- "statusVersion": 3,
733
+ "statusVersion": 4,
705
734
  "canonVersion": "0.29.0",
706
735
  "policy": { "present": true, "spec_coverage": { "require_evidence": true, "scope": [], "exclude_lanes": [], "require_cases": ["failure"], "require_review": { "scope": ["verifying", "shipped"], "distinct_actor": true, "block_on_findings": true } } },
707
736
  "warnings": [],
708
737
  "subjects": [
709
738
  { "id": "cart", "title": "Cart", "status": "building", "scenarios": 2, "excluded": 0, "evidenced": 2, "divergent": 0,
710
739
  "reviewed": 1, "staleReviews": 0, "unsignedReviews": 0, "reviewDue": true, "openBlockers": 1, "missingCases": [],
711
- "brief": { "approved": true, "enforced": false, "change": "c-cart", "brief": ".canon/changes/c-cart/brief.md", "briefExists": true, "approval": "Journey 2026-10-11 — \"approved\"" } },
740
+ "briefs": [{ "status": "approved", "reason": "tagged", "enforced": false, "change": "c-cart", "brief": ".canon/changes/c-cart/brief.md", "briefExists": true, "approval": "Journey 2026-10-11 — \"approved\"" }] },
712
741
  { "id": "promo", "title": "Promo", "status": "verifying", "scenarios": 1, "excluded": 0, "evidenced": 1, "divergent": 0,
713
742
  "reviewed": 1, "staleReviews": 0, "unsignedReviews": 0, "reviewDue": true, "openBlockers": 0, "missingCases": [],
714
- "brief": { "approved": false, "enforced": true, "change": "c-promo", "brief": ".canon/changes/c-promo/brief.md", "briefExists": false, "source": "openspec/changes/c-promo/proposal.md" } }
743
+ "briefs": [{ "status": "unapproved", "reason": "tagged", "enforced": true, "change": "c-promo", "brief": ".canon/changes/c-promo/brief.md", "briefExists": false, "source": "openspec/changes/c-promo/proposal.md" }] }
715
744
  ],
716
745
  "unowned": ["cart.promo.01"]
717
746
  }
@@ -724,7 +753,7 @@ first, with `meta.nextOmitted` counting what was cut and `meta.waitingOn`
724
753
  ```json
725
754
  "next": [
726
755
  { "command": "stop and report the brief in openspec/changes/c-promo/proposal.md; build and attest nothing for promo until the human answers; then record their words with `canon brief approve c-promo --approver <the human> --note \"<their words>\"`", "argv": [], "why": "promo: the brief is not approved", "actor": "human", "role": null },
727
- { "command": "canon finding close c-cart --round 1 --seq 1 --disposition fixed --actor-id <unit> --session-id <session>", "argv": [], "why": "cart: 1 open blocker finding(s); fix it, commit, then close it", "actor": "agent", "role": "implementer" },
756
+ { "command": "canon finding close c-cart --round 1 --seq 1 --disposition fixed --actor <unit> --session <session>", "argv": [], "why": "cart: 1 open blocker finding(s); fix it, commit, then close it", "actor": "agent", "role": "implementer" },
728
757
  { "command": "canon status", "argv": ["canon", "status"], "why": "1 scenario(s) carry no @subject tag (first: cart.promo.01); add `@subject:<id>` above each in its .feature file, then check again", "actor": "agent", "role": "planner" }
729
758
  ]
730
759
  ```
@@ -740,24 +769,28 @@ skills install`.
740
769
  Each `missingCases` entry is `{ "projectId", "surface", "case" }`: the
741
770
  spec root, the `<area>.<surface>`, and the required case it lacks.
742
771
 
743
- `brief` is present for `building`, `verifying` and `shipped` subjects,
744
- and for a `proposed` subject whose newest adopted change has a brief (a
745
- canon `brief.md` or an external `proposal.md`). `change` is the newest
746
- adopted change (omitted when the subject adopts none); `brief` its canon
747
- `.canon/changes/<change>/brief.md`, the one file the approval is read
748
- from (omitted when the change has no brief to answer); `briefExists`
749
- (always present) whether that file exists (`false` for an external
750
- change whose canon brief is still to be created); `source` the external
751
- plan document the change comes from (the canon brief's `Source:` line,
752
- or, before that file exists, the external `proposal.md`; omitted for a
753
- canon-written change); `approval` the text after `Approved:`; and
754
- `enforced` whether the missing approval blocks (a brief exists to
755
- answer). The 0.16 `proposal` field is gone. `changed` (omitted when
756
- false) says the brief was edited after its approval, `legacyApproval`
757
- that the `Approved:` line carries no digest, `findings` is
758
- `{ "blockers", "advisories" }` from `canon brief check` (PRD briefs
759
- only), and `flow` lists each flow step as `{ "id", "change",
760
- "scenarios": [{ id, title, failure, evidenced, review }], "gaps" }`.
772
+ `briefs` is present for `building`, `verifying` and `shipped` subjects,
773
+ and for a `proposed` subject whose adopted change has a brief (a
774
+ canon `brief.md` or an external `proposal.md`); it is a list with one entry
775
+ per change the subject answers for, in adoption order (0.31). Each entry has
776
+ `change`; `brief`, its canon `.canon/changes/<change>/brief.md`, the one file
777
+ the approval is read from (omitted when the change has no brief to answer);
778
+ `status`: `approved`, `unapproved` or `changed` (edited after its approval);
779
+ `reason` why the subject answers for it: `tagged` (a scenario it owns is
780
+ tagged `@flow:<change>.*`), `approved` (its brief has an `Approved:` line) or
781
+ `chore`; `briefExists` (always present) whether that file exists (`false` for
782
+ an external change whose canon brief is still to be created); `source` the
783
+ external plan document the change comes from (the canon brief's `Source:`
784
+ line, or, before that file exists, the external `proposal.md`; omitted for a
785
+ canon-written change); `approval` the text after `Approved:`; `enforced`
786
+ whether the missing approval blocks (a brief exists to answer);
787
+ `legacyApproval` that the `Approved:` line carries no digest; and `findings`,
788
+ `{ "blockers", "advisories" }` from `canon brief check` (PRD briefs only).
789
+ A subject with no such change has one entry, its newest adopted change, with no
790
+ `reason` (or, adopting none, an entry with no `change`). `pendingDrafts` lists
791
+ only the pending drafts, and `flow` lists each flow step of every tagged
792
+ change as `{ "id", "change", "scenarios": [{ id, title, failure, evidenced,
793
+ review }], "gaps" }`.
761
794
 
762
795
  Each `meta.next` entry's `actor` is `agent` (its `command` is a canon command
763
796
  line) or `human` (its `command` says what to get from the human).
@@ -768,7 +801,8 @@ changes only when a field changes meaning or is removed. It became `2`
768
801
  in 0.16, when `next[].command` stopped always being a command line
769
802
  (`actor` tells which), and `brief` was added alongside. It is `3` since
770
803
  0.17, when `brief.proposal` was removed: the canon `brief.md` is
771
- `brief.brief`, with `briefExists` and `source`.
804
+ `brief.brief`, with `briefExists` and `source`. It is `4` since 0.31, when the
805
+ `brief` object became the `briefs` list.
772
806
 
773
807
  `subject new`, `adopt`, `unadopt` and `move` write their records directly; the
774
808
  human-readable success line ends `— written directly; nothing to