@journeykit/canon 0.29.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.
- package/dist/skills/SKILL.src.md +48 -22
- package/dist/skills/reference/brief-polish.md +4 -4
- package/dist/skills/reference/brief-shape.md +38 -7
- package/dist/skills/reference/canon-authoring.md +6 -6
- package/dist/skills/reference/canon-gate.md +49 -34
- package/dist/skills/reference/canon-learn.md +2 -2
- package/dist/skills/reference/canon-plan-import.md +2 -2
- package/dist/skills/reference/canon-plugins.md +6 -6
- package/dist/skills/reference/canon-report-dashboard.md +12 -8
- package/dist/skills/reference/canon-review.md +26 -25
- package/dist/skills/reference/canon-storage.md +20 -3
- package/dist/skills/reference/canon-subject.md +132 -98
- package/dist/skills/reference/role-implementer.md +42 -28
- package/dist/skills/reference/role-orchestrator.md +40 -24
- package/dist/skills/reference/role-planner.md +32 -17
- package/dist/skills/reference/role-reviewer.md +48 -30
- package/man/canon-adapter-authorize.1 +2 -2
- package/man/canon-adapter-validate.1 +2 -2
- package/man/canon-adapter.1 +2 -2
- package/man/canon-artifacts-move.1 +2 -2
- package/man/canon-artifacts-store.1 +2 -2
- package/man/canon-artifacts-verify.1 +2 -2
- package/man/canon-artifacts.1 +2 -2
- package/man/canon-brief-approve.1 +7 -4
- package/man/canon-brief-check.1 +2 -2
- package/man/canon-brief-show.1 +2 -2
- package/man/canon-brief.1 +8 -7
- package/man/canon-change-new.1 +4 -4
- package/man/canon-change.1 +3 -3
- package/man/canon-completion.1 +2 -2
- package/man/canon-context-pack-add.1 +2 -2
- package/man/canon-context-pack-show.1 +2 -2
- package/man/canon-context-pack-verify.1 +2 -2
- package/man/canon-context-pack.1 +2 -2
- package/man/canon-context.1 +2 -2
- package/man/canon-dashboard.1 +2 -2
- package/man/canon-demo-init.1 +2 -2
- package/man/canon-demo-review.1 +2 -2
- package/man/canon-demo.1 +2 -2
- package/man/canon-dispatch-begin.1 +10 -10
- package/man/canon-dispatch-diff.1 +2 -2
- package/man/canon-dispatch-end.1 +2 -2
- package/man/canon-dispatch.1 +2 -2
- package/man/canon-divergence-accept.1 +6 -6
- package/man/canon-divergence-add.1 +6 -6
- package/man/canon-divergence-defer.1 +6 -6
- package/man/canon-divergence-list.1 +2 -2
- package/man/canon-divergence-resolve.1 +6 -6
- package/man/canon-divergence.1 +2 -2
- package/man/canon-evidence-add.1 +16 -16
- package/man/canon-evidence-approval-payload.1 +10 -10
- package/man/canon-evidence-audit.1 +10 -10
- package/man/canon-evidence.1 +3 -3
- package/man/canon-export.1 +2 -2
- package/man/canon-feature-new.1 +6 -6
- package/man/canon-feature.1 +2 -2
- package/man/canon-finding-add.1 +23 -11
- package/man/canon-finding-close.1 +8 -8
- package/man/canon-finding-list.1 +4 -4
- package/man/canon-finding.1 +4 -4
- package/man/canon-format.1 +2 -2
- package/man/canon-gate-check.1 +2 -2
- package/man/canon-gate-install-hooks.1 +2 -2
- package/man/canon-gate-promote.1 +2 -2
- package/man/canon-gate.1 +2 -2
- package/man/canon-ingest-plans.1 +2 -2
- package/man/canon-ingest-records.1 +4 -4
- package/man/canon-ingest-sessions.1 +2 -2
- package/man/canon-ingest.1 +2 -2
- package/man/canon-init.1 +2 -2
- package/man/canon-inventory-sync.1 +2 -2
- package/man/canon-inventory.1 +2 -2
- package/man/canon-ledger-compact.1 +2 -2
- package/man/canon-ledger.1 +2 -2
- package/man/canon-migrate.1 +25 -3
- package/man/canon-plugin-sync.1 +2 -2
- package/man/canon-plugin.1 +2 -2
- package/man/canon-policy-approve.1 +2 -2
- package/man/canon-policy-show.1 +2 -2
- package/man/canon-policy.1 +2 -2
- package/man/canon-prompt-add.1 +2 -2
- package/man/canon-prompt-show.1 +2 -2
- package/man/canon-prompt.1 +2 -2
- package/man/canon-purge.1 +2 -2
- package/man/canon-query.1 +5 -5
- package/man/canon-regime-show.1 +2 -2
- package/man/canon-regime.1 +2 -2
- package/man/canon-report-build.1 +2 -2
- package/man/canon-report-check.1 +2 -2
- package/man/canon-report-snapshot.1 +2 -2
- package/man/canon-report.1 +2 -2
- package/man/canon-retrieve.1 +5 -5
- package/man/canon-review-change.1 +6 -6
- package/man/canon-review-scenario.1 +6 -6
- package/man/canon-review.1 +2 -2
- package/man/canon-scenario-new.1 +10 -10
- package/man/canon-scenario.1 +2 -2
- package/man/canon-schema.1 +2 -2
- package/man/canon-search.1 +104 -0
- package/man/canon-selftest.1 +2 -2
- package/man/canon-skills-check.1 +2 -2
- package/man/canon-skills-doctor.1 +2 -2
- package/man/canon-skills-install.1 +2 -2
- package/man/canon-skills-show.1 +2 -2
- package/man/canon-skills.1 +2 -2
- package/man/canon-status.1 +7 -2
- package/man/canon-strategy-approval-payload.1 +2 -2
- package/man/canon-strategy-promote.1 +2 -2
- package/man/canon-strategy-rollback.1 +5 -5
- package/man/canon-strategy.1 +2 -2
- package/man/canon-subject-adopt.1 +4 -4
- package/man/canon-subject-move.1 +5 -5
- package/man/canon-subject-new.1 +5 -5
- package/man/canon-subject-show.1 +2 -2
- package/man/canon-subject-unadopt.1 +5 -5
- package/man/canon-subject.1 +2 -2
- package/man/canon-task-done.1 +2 -2
- package/man/canon-task.1 +2 -2
- package/man/canon-tier-age.1 +2 -2
- package/man/canon-tier.1 +2 -2
- package/man/canon-trace.1 +56 -0
- package/man/canon.1 +8 -2
- package/package.json +4 -4
|
@@ -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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
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 (
|
|
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
|
-
**
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
317
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
395
|
+
<subject_id> --change <change_id>` to complete it.
|
|
383
396
|
|
|
384
|
-
`canon change new <slug> --subject
|
|
385
|
-
<S> --change
|
|
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
|
|
392
|
-
|
|
393
|
-
and ship again while the draft waits; `canon status` still
|
|
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
|
|
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
|
|
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
|
|
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
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
491
|
-
|
|
492
|
-
|
|
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 (
|
|
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**:
|
|
511
|
-
one rule in one section, reason required; add `match
|
|
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
|
|
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
|
|
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
|
|
577
|
-
|
|
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
|
|
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
|
|
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>.<
|
|
644
|
-
unevidenced or divergent scenario → `canon evidence add --scenario
|
|
645
|
-
|
|
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
|
|
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":
|
|
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
|
-
"
|
|
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
|
-
"
|
|
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
|
|
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
|
-
`
|
|
744
|
-
and for a `proposed` subject whose
|
|
745
|
-
canon `brief.md` or an external `proposal.md`)
|
|
746
|
-
|
|
747
|
-
`.canon/changes/<change>/brief.md`, the one file
|
|
748
|
-
from (omitted when the change has no brief to answer);
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
canon
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
`
|
|
759
|
-
|
|
760
|
-
|
|
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
|