@journeykit/canon 0.18.0 → 0.21.1

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.
@@ -103,7 +103,7 @@ this subject and domain — the exact memory step 2 retrieves next time.
103
103
 
104
104
  ## Status lifecycle + the shipped evidence gate
105
105
 
106
- `canon subject status <id> <state> [--override-reason <text>] [--reason <text>] [--actor-id <id>]`
106
+ `canon subject status <id> --to <state> [--override-reason <text>] [--reason <text>] [--actor-id <id>]`
107
107
  performs a policy-gated transition.
108
108
  The legal chain:
109
109
 
@@ -117,10 +117,10 @@ from anywhere). Any other transition is rejected by failure class, exits
117
117
  1, and leaves the record UNCHANGED (fail closed).
118
118
 
119
119
  ```bash
120
- canon subject status subject-domain-loop specced
121
- canon subject status subject-domain-loop building
122
- canon subject status subject-domain-loop verifying
123
- canon subject status subject-domain-loop shipped # gated — see below
120
+ canon subject status subject-domain-loop --to specced
121
+ canon subject status subject-domain-loop --to building
122
+ canon subject status subject-domain-loop --to verifying
123
+ canon subject status subject-domain-loop --to shipped # gated — see below
124
124
  ```
125
125
 
126
126
  On success the status updates in place; `--json` prints the updated
@@ -149,29 +149,34 @@ empty set; so is one whose `scenario` records route away from the
149
149
  gate's rung. Each refusal prints by failure class (`uncovered-cell`),
150
150
  exits 1, and the status stays `verifying`. `retired` is not gated.
151
151
 
152
- **The brief (0.16, 0.17):** `verifying → shipped` also refuses where a brief
152
+ **The brief (0.16, 0.17, 0.19):** `verifying → shipped` also refuses where a brief
153
153
  exists and is not approved, read exactly as `canon status` reads it (one
154
154
  shared check, so status says `ship` only when this passes): the subject's
155
155
  newest adopted change has a canon `.canon/changes/<change>/brief.md`
156
- with no `Approved:` line outside HTML comments and code fences, or has no
157
- canon `brief.md` yet but comes from an external openspec plan with a
158
- `proposal.md` under the plan sources. It runs with or without a policy.
159
- The refusal is an `unreviewed-promotion` naming the subject, and says
160
- what to do:
156
+ with no `Approved:` line outside HTML comments and code fences, whose
157
+ `Approved:` line carries a digest the brief's text no longer matches
158
+ (`BRIEF_CHANGED_SINCE_APPROVAL`; the brief was edited after the human
159
+ answered), or has no canon `brief.md` yet but comes from an external
160
+ openspec plan with a `proposal.md` under the plan sources. It also
161
+ refuses while `canon brief check` has blocker findings for that change,
162
+ which include a flow step with no scenario or no failure scenario (the
163
+ coverage rules gate shipping, never approval). It runs with or without
164
+ a policy. The refusal is an `unreviewed-promotion` naming the subject, and
165
+ says what to do:
161
166
 
162
167
  ```
163
- canon subject status: unreviewed-promotion demo-subject — verifying → shipped: brief not approved — .canon/changes/c-demo/brief.md has no `Approved:` line; a human must answer the brief, and only from that answer the agent will record their words as `Approved: <who> <when> — "<their words>"` in .canon/changes/c-demo/brief.md
164
- canon subject status: refused — get the human's answer to the brief and record it as its `Approved:` line, or pass --override-reason <text> to move anyway; the waiver is recorded on the subject
168
+ canon subject status: unreviewed-promotion demo-subject — verifying → shipped: brief not approved — .canon/changes/c-demo/brief.md has no `Approved:` line; a human must answer the brief, and only from that answer the agent will record their words with `canon brief approve --change c-demo --by <the human> --note "<their words>"`
169
+ canon subject status: refused — get the human's answer to the brief and record it with `canon brief approve`, or pass --override-reason <text> to move anyway; the waiver is recorded on the subject
165
170
  ```
166
171
 
167
172
  For an external change with no canon brief yet, the reason reads
168
173
  ``.canon/changes/<c>/brief.md does not exist yet: change `<c>` comes from
169
174
  the external plan <proposal>, which canon reads and never edits, so its
170
175
  approval lives in that canon file`` and the agent will ``create
171
- .canon/changes/<c>/brief.md holding a `Source: <proposal>` line and
172
- record their words there as `Approved: <who> <when> — "<their words>"` ``.
176
+ .canon/changes/<c>/brief.md holding a `Source: <proposal>` line, then
177
+ record their words with `canon brief approve --change <c> --by <the human> --note "<their words>"` ``.
173
178
 
174
- Never write the line without the human's answer. When there is no brief to
179
+ Never record the approval without the human's answer. When there is no brief to
175
180
  answer (no adopted change, or a newest change with neither a canon
176
181
  `brief.md` nor an external `proposal.md`), the move is not refused: it
177
182
  prints ``canon subject status: advisory: <id>'s brief is not approved —
@@ -185,9 +190,10 @@ the transition, not the check.
185
190
 
186
191
  This refusal is a backstop, and so is the evidence one: shipping is not
187
192
  the first thing an unapproved brief stops. The agent writes the brief,
188
- then stops and reports it, and nothing is built or attested for the
189
- subject until the human answers. `canon evidence add` refuses (exit 1,
190
- nothing staged) a scenario whose `@subject` owner, or a task whose change
193
+ runs `canon brief check` until it has no blockers, then stops and reports
194
+ it, and nothing is built or attested for the subject until the human
195
+ answers. `canon evidence add` refuses (exit 1, nothing staged) a
196
+ scenario whose `@subject` owner, or a task whose change
191
197
  an adopting subject, has an enforced, unapproved brief, through the same
192
198
  check; see `canon-gate`.
193
199
 
@@ -214,7 +220,7 @@ also refuses a `false-success` (a scenario whose latest audit is
214
220
  sampled audit; see `canon-gate`.
215
221
 
216
222
  ```bash
217
- canon subject status demo-subject verifying \
223
+ canon subject status demo-subject --to verifying \
218
224
  --override-reason "reviewer out until Monday" --actor-id lead
219
225
  ```
220
226
 
@@ -250,20 +256,22 @@ gate now misses) reopens it: reopen, add tasks to the approved change,
250
256
  scenario, evidence, review, ship.
251
257
 
252
258
  ```bash
253
- canon subject status demo-subject verifying \
259
+ canon subject status demo-subject --to verifying \
254
260
  --reason "follow-up: the retry path leaks a file handle" --actor-id impl
255
261
  ```
256
262
 
257
263
  ```
258
- canon subject status: reopened `demo-subject` from shipped, recorded by `impl`: follow-up: the retry path leaks a file handle; add the follow-up as tasks in its approved change `c-demo`, then scenario, evidence and review; shipping again runs every guard
264
+ canon subject status: reopened `demo-subject` from shipped, recorded by `impl`: follow-up: the retry path leaks a file handle; keep the follow-up in its approved change `c-demo`, then scenario, evidence and review; shipping again runs every guard
259
265
  canon subject status: demo-subject → verifying — written directly; nothing to promote
260
266
  ```
261
267
 
262
- - `--reason "<one line>"` is required for `shipped → verifying` and
263
- refused on any other transition (exit 2, as is a blank one). The new
264
- record carries it with the actor and the change the work goes into:
268
+ - `--reason "<one line>"` is required for `shipped → verifying` (exit 2
269
+ without it, as for a blank one) and optional on every other transition,
270
+ where it is an audit note. The new record carries it with the actor as
271
+ `status_reason: { reason: …, actor: { agent_id: impl } }`; a reopen also
272
+ records the change the work goes into:
265
273
  `reopen: { reason: …, change: c-demo, actor: { agent_id: impl } }`. The
266
- next status write clears it, as it clears a waiver.
274
+ next status write clears both, as it clears a waiver.
267
275
  - Inside the approved scope it needs no human: the subject's newest
268
276
  adopted change's brief is approved, or there is no brief to answer.
269
277
  Otherwise it refuses, exit 1, naming the brief to approve, and
@@ -289,7 +297,8 @@ canon subject adopt subject-domain-loop-plan \
289
297
  ```
290
298
 
291
299
  A change made with `canon change new` (below) is recorded and adopted
292
- already, and editing its `tasks.md` needs no import either:
300
+ already. A `tasks.md` in it is an optional work note nothing gates on
301
+ (`canon change new` no longer writes one); an existing one is still read:
293
302
  `canon evidence add --task` and `canon gate task` read the file as it is,
294
303
  and a flip re-imports that change itself. Run `canon ingest plans` for a
295
304
  plan written outside canon, or when `canon query --kind task` must list
@@ -334,16 +343,21 @@ canon change new add-login --subject auth --title "Add login"
334
343
 
335
344
  ```
336
345
  canon change new: wrote .canon/changes/add-login/brief.md
337
- canon change new: wrote .canon/changes/add-login/tasks.md
338
346
  canon change new: recorded change `add-login` adopted into subject `auth` — written directly; nothing to promote
339
347
  ```
340
348
 
341
- - **Writes** canon's own change dir, `.canon/changes/<slug>/`:
342
- `brief.md` (`# <title>`, `## Why` holding the title, which imports as
343
- the change summary, `## What Changes` with a comment saying what goes
344
- there, and a comment naming where the human's `Approved:` line goes,
345
- so the file starts unapproved) and `tasks.md` (no rows yet, so the
346
- change imports as `proposed`; the openspec checkbox row grammar).
349
+ - **Writes** canon's own change dir, `.canon/changes/<slug>/`, holding
350
+ `brief.md` only: the PRD skeleton (`# <title>`, then the sections intent,
351
+ desired outcome, user flow, step detail, acceptance, questions and
352
+ assumptions, out of scope, each opened by a
353
+ `<!-- canon:section <key> role=… -->` marker and a hint comment, with
354
+ the headings in the repo's `language:`), and a comment naming
355
+ `canon brief approve` as where the human's answer is recorded, so the
356
+ file starts unapproved. The intent section's first paragraph holds the
357
+ title, which imports as the change summary. `--kind chore` writes the
358
+ short chore brief instead (intent, category, invariants, checks, out of
359
+ scope; see `canon brief` below). No `tasks.md` is written:
360
+ `canon status` lists each flow step's scenarios instead.
347
361
  `.canon/changes/` is canon's native plan source: every plan reader
348
362
  reads it with no `plans:` entry, so `canon.yaml` needs no edit and no
349
363
  external plans source is needed (see `canon-plan-import`).
@@ -366,9 +380,10 @@ canon change new: recorded change `add-login` adopted into subject `auth` — wr
366
380
  exist, or when the slug already has a `.canon/changes/<slug>` dir
367
381
  (active or under `.canon/changes/archive/`) or a `change` record.
368
382
 
369
- Add task rows to `tasks.md` as `- [ ] <n> <title>`, and flip each with
370
- `canon gate task <slug>#<n>` once evidence exists. `canon evidence add
371
- --task` and the flip read `tasks.md` as it is, so a new row needs no
383
+ A `tasks.md` is an optional work note canon never writes and nothing
384
+ gates on. If one exists, rows added as `- [ ] <n> <title>` are flipped with
385
+ `canon gate task <slug>#<n>` once evidence exists; `canon evidence add
386
+ --task` and the flip read the file as it is, so a new row needs no
372
387
  `canon ingest plans` run.
373
388
 
374
389
  A change from an external plan (an openspec change listed under
@@ -376,10 +391,59 @@ A change from an external plan (an openspec change listed under
376
391
  `canon subject adopt`) gets its brief as a canon file too:
377
392
  `.canon/changes/<change>/brief.md` holding a `Source: <path>` line that
378
393
  points at the external document and, once the human answers, the
379
- `Approved:` line. Canon never edits the external document. Such a
394
+ `Approved:` line that `canon brief approve` writes. Canon never edits the external document. Such a
380
395
  pointer `brief.md` is not a change of its own: the native reader skips
381
396
  it, so the external source keeps the change, its summary and its tasks.
382
397
 
398
+ ### The brief: `canon brief`, `canon brief check`, `canon brief approve`
399
+
400
+ ```bash
401
+ canon brief [--change <slug>] # state and the next one to three steps; never edits
402
+ canon brief check [--change <slug>] [--json] # findings; exit 0 clean, 2 blockers, 1 error
403
+ canon brief approve --change <slug> --by <the human> --note "<their words>"
404
+ ```
405
+
406
+ Without `--change`, the repo's only live change is read. The brief is the
407
+ PRD `canon change new` writes; sections are found by their
408
+ `<!-- canon:section <key> role=… -->` marker, never by heading text. A
409
+ brief without markers (written before 0.19) checks as `brief.legacy`, an
410
+ advisory, and is read as before.
411
+
412
+ - **Blockers (structure, `brief.*`)**: a missing, duplicated or
413
+ out-of-order section; a flow with no steps or a duplicate step id;
414
+ step detail that does not match the flow ids in order; leftover
415
+ placeholders (a hint comment still there, an empty section,
416
+ `TODO`/`TBD`); a malformed waiver; for a chore, a flow/detail/acceptance
417
+ section, a category outside `refactor|dependency|build|ci|infra`, or
418
+ invariants/checks that say only "none". Once the subject is `building`
419
+ or later, a flow step no scenario points at (`brief.flow-uncovered`) or
420
+ one with no failure scenario (`brief.flow-no-failure`): these two
421
+ **gate shipping, never approval**.
422
+ - **Advisories (prose, `prose.*`)**: phrase lists, sentence and
423
+ paragraph length, and key-sentence marking in `role=normative`
424
+ sections, for `language: en` and `ko`. Never an authorship verdict.
425
+ - **Waivers**: `<!-- canon:waive <rule-id> <section-key> "<reason>" -->`,
426
+ one rule in one section, reason required; listed in the output.
427
+ - **`approve`** records the human's answer: it writes
428
+ `Approved: <who> <when> — "<words>" (brief sha256:<digest>)` into the
429
+ brief and records the policy approval in the same call. It refuses
430
+ (exit 2) while the check has structure blockers. Edit the brief
431
+ afterwards and every reader says `BRIEF_CHANGED_SINCE_APPROVAL` until a
432
+ human answers again and `approve` runs again. Run it only from the
433
+ human's answer, with their words as `--note`.
434
+
435
+ The optional refinement passes that fix findings before the human stop are
436
+ listed in `role-planner.md` in this directory.
437
+
438
+ A **chore** (`canon change new --kind chore`) changes how the subject is
439
+ built or kept, never what it does. Its brief is intent, category,
440
+ invariants, checks, out of scope; it adds no scenarios, and its evidence
441
+ is the subject's suite run again at the chore commit with
442
+ `canon evidence add --all-scenarios --report <kind>:<path> …`. That
443
+ supersedes the old evidence, which makes the pinned reviews stale, so an
444
+ independent re-review follows. Work that changes behavior is a feature
445
+ change, never a chore.
446
+
383
447
  ## Where are we: `canon status`
384
448
 
385
449
  ```bash
@@ -413,7 +477,7 @@ uses, so status and the gate cannot disagree about a gap:
413
477
  `require_review` on, a `warning:` line counts the in-effect reviews that
414
478
  predate review pinning (0.15), checked against the commit each pinned:
415
479
  current, stale (named) and undecidable.
416
- - **Brief** (0.16, 0.17) — for each `building`, `verifying` or `shipped`
480
+ - **Brief** (0.16, 0.17, 0.19) — for each `building`, `verifying` or `shipped`
417
481
  subject, and each `proposed` subject whose newest adopted change has a
418
482
  brief (a canon `brief.md`, or an external `proposal.md`), a `brief:`
419
483
  line under it says whether the brief is approved:
@@ -423,10 +487,16 @@ uses, so status and the gate cannot disagree about a gap:
423
487
  change (the last one adopted; a live dir before an archived one), and
424
488
  only there: a line that starts with `Approved:` (indent allowed), says
425
489
  something after it, and sits outside HTML comments (`<!-- … -->`,
426
- multi-line included) and fenced code blocks (```` ``` ```` or `~~~`),
427
- written by the agent once the human answers, as
428
- `Approved: <who> <when> — "<the human's words>"`. Fixes and follow-ups
429
- live as tasks in the approved change, so a newly adopted change is a
490
+ multi-line included) and fenced code blocks (```` ``` ```` or `~~~`).
491
+ `canon brief approve` writes it from the human's answer, as
492
+ `Approved: <who> <when> — "<the human's words>" (brief sha256:<digest>)`,
493
+ the digest covering the brief without its `Approved:` lines. A brief
494
+ edited after that reads `brief: not approved (<brief> changed after its
495
+ approval (BRIEF_CHANGED_SINCE_APPROVAL))` and names the re-approval
496
+ command: the changed brief needs a new human answer. A line with no
497
+ digest (written before 0.19) stays approved, with an `advisory:` to
498
+ re-approve so it binds to the text. Fixes and follow-ups
499
+ stay in the approved change, so a newly adopted change is a
430
500
  new scope: an older change's approval does not cover it. A change from
431
501
  an external plan has its brief in the same canon file, with a
432
502
  `Source: <path>` line pointing at the external document; an `Approved:`
@@ -440,13 +510,19 @@ uses, so status and the gate cannot disagree about a gap:
440
510
  and never edits, so its approval lives in that canon file)``. With a
441
511
  canon brief and no line it reads
442
512
  ``brief: not approved (<brief> has no `Approved:` line)``.
443
- There is no record kind for it; the file is the record.
513
+ There is no record kind for it; the file is the record. For a brief in
514
+ the PRD format, `brief check: <n> blocker(s), <n> advisory(ies)` follows
515
+ (`canon brief` names the next step), then, per flow step of the
516
+ change, `flow F1` (with ` — <gaps>` when it has no scenario or no
517
+ failure scenario) and under it each scenario that carries
518
+ `@flow:<change>.F1` with its title, `[failure]` when it is, its
519
+ evidence (`yes`/`none`) and its review state.
444
520
  - **Red shipped subject** (0.18) — a `shipped` subject that the
445
521
  `verifying → shipped` guard would now refuse, by the guard's own checks
446
522
  (owned scenarios, verdicts, required cases, audits, and, where
447
523
  `require_review` covers `shipped`, reviews and open blockers under
448
524
  `block_on_findings`; a gap its waiver recorded is not counted), prints
449
- ``gate: red — <n> <class> (first: <subject>), …; reopen with `canon subject status <id> verifying --reason "<why it reopens>" --actor-id <unit>` ``,
525
+ ``gate: red — <n> <class> (first: <subject>), …; reopen with `canon subject status <id> --to verifying --reason "<why it reopens>" --actor-id <unit>` ``,
450
526
  carries `red: [...]` in `--json`, and that reopen is its only `next:`
451
527
  step. Open blockers count against every subject a change belongs to.
452
528
  - **Unowned** — scenarios with no `@subject:` tag.
@@ -481,7 +557,7 @@ uses, so status and the gate cannot disagree about a gap:
481
557
  …` (its reason adds "then `canon gate promote`" only under
482
558
  `write.promote: manual`); an open blocker → `canon finding close …`; a
483
559
  due, unreviewed, evidenced scenario → `canon review add …` from another
484
- session; none of these → `canon subject status <id> <next status>`.
560
+ session; none of these → `canon subject status <id> --to <next status>`.
485
561
  Last, unowned scenarios → `canon status`, after tagging them in their
486
562
  `.feature` files (status re-indexes the edit; no sync step). With more
487
563
  than one `specs.roots[]` entry, every suggested `canon scenario new`
@@ -497,15 +573,17 @@ uses, so status and the gate cannot disagree about a gap:
497
573
  unanswered> (an older change's approval does not cover a new scope),
498
574
  and the approval is never assumed: `canon evidence add` refuses <id>'s
499
575
  scenarios and tasks until it is recorded; once the human answers,
500
- <record> and run `canon policy approve --by <the human> --note "<their
501
- words>"` ``, where `<record>` is ``record their words as `Approved: <who>
502
- <when> — "<their words>"` in <brief>``, or, for an external change,
503
- ``create <brief> holding a `Source: <proposal>` line and record their
504
- words there as `Approved: <who> <when> — "<their words>"` ``.
576
+ <record> (one call records the brief and the policy approval)``, where
577
+ `<record>` is ``record their words with `canon brief approve --change
578
+ <c> --by <the human> --note "<their words>"` ``, or, for an external
579
+ change, ``create <brief> holding a `Source: <proposal>` line, then
580
+ record their words with `canon brief approve --change <c> --by <the
581
+ human> --note "<their words>"` ``; for a brief edited after its
582
+ approval, ``re-approve with `canon brief approve …` ``.
505
583
  It is the subject's only step, whatever its status: nothing suggests
506
584
  `canon feature new`, `canon scenario new`, `canon evidence add`, a
507
585
  review or a move-on for it before the human answers, and
508
- `canon subject status <id> shipped` refuses on the same check. The
586
+ `canon subject status <id> --to shipped` refuses on the same check. The
509
587
  approval is never assumed. A subject with no brief to answer gets no human step;
510
588
  its `brief:` line says
511
589
  `not approved (no adopted change; advisory, nothing to approve yet)` or
@@ -513,7 +591,7 @@ uses, so status and the gate cannot disagree about a gap:
513
591
  approve yet)`` and nothing is withheld (a `proposed` subject with no
514
592
  brief, and a `specced` subject, show no `brief:` line at all). Once nothing blocks, a
515
593
  ship-ready verifying subject gets the agent's own
516
- `canon subject status <id> shipped`; status never asks a human to ship.
594
+ `canon subject status <id> --to shipped`; status never asks a human to ship.
517
595
 
518
596
  Status fails closed rather than suggest progress the gate would refuse:
519
597
 
@@ -545,7 +623,7 @@ Status fails closed rather than suggest progress the gate would refuse:
545
623
  ],
546
624
  "unowned": ["cart.promo.01"],
547
625
  "next": [
548
- { "command": "stop and report the brief in openspec/changes/c-promo/proposal.md; build and attest nothing for promo until the human answers (one answer approves brief and policy)", "why": "promo: .canon/changes/c-promo/brief.md does not exist yet: change `c-promo` comes from the external plan openspec/changes/c-promo/proposal.md, which canon reads and never edits, so its approval lives in that canon file (an older change's approval does not cover a new scope), and the approval is never assumed: … once the human answers, create .canon/changes/c-promo/brief.md holding a `Source: openspec/changes/c-promo/proposal.md` line and record their words there as `Approved: <who> <when> — \"<their words>\"` and run `canon policy approve --by <the human> --note \"<their words>\"`", "actor": "human" },
626
+ { "command": "stop and report the brief in openspec/changes/c-promo/proposal.md; build and attest nothing for promo until the human answers (one answer approves brief and policy)", "why": "promo: .canon/changes/c-promo/brief.md does not exist yet: change `c-promo` comes from the external plan openspec/changes/c-promo/proposal.md, which canon reads and never edits, so its approval lives in that canon file (an older change's approval does not cover a new scope), and the approval is never assumed: … once the human answers, create .canon/changes/c-promo/brief.md holding a `Source: openspec/changes/c-promo/proposal.md` line, then record their words with `canon brief approve --change c-promo --by <the human> --note \"<their words>\"` (one call records the brief and the policy approval)", "actor": "human" },
549
627
  { "command": "canon finding close --change-id c-cart --round 1 --seq 1 --disposition fixed --resolution-sha <sha> --actor-id <unit> --session-id <session>", "why": "cart: 1 open blocker finding(s) (first: …); fix it, commit, then close it", "actor": "agent" },
550
628
  { "command": "canon review add --project-id root --scenario-id cart.add.02 --reviewer <reviewer> --actor-id <reviewer> --session-id <review-session> --role reviewer --pin <sha> --original-spec-ref \"<feature file>\"", "why": "cart: 1 of 2 scenario(s) lack a qualifying review; …", "actor": "agent" },
551
629
  { "command": "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" }
@@ -569,7 +647,12 @@ plan document the change comes from (the canon brief's `Source:` line,
569
647
  or, before that file exists, the external `proposal.md`; omitted for a
570
648
  canon-written change); `approval` the text after `Approved:`; and
571
649
  `enforced` whether the missing approval blocks (a brief exists to
572
- answer). The 0.16 `proposal` field is gone.
650
+ answer). The 0.16 `proposal` field is gone. `changed` (omitted when
651
+ false) says the brief was edited after its approval, `legacyApproval`
652
+ that the `Approved:` line carries no digest, `findings` is
653
+ `{ "blockers", "advisories" }` from `canon brief check` (PRD briefs
654
+ only), and `flow` lists each flow step as `{ "id", "change",
655
+ "scenarios": [{ id, title, failure, evidenced, review }], "gaps" }`.
573
656
 
574
657
  Each `next` entry's `actor` is `agent` (its `command` is a canon command
575
658
  line) or `human` (its `command` says what to get from the human).
@@ -0,0 +1,81 @@
1
+ # Role: implementer
2
+
3
+ You build inside an approved scope: scenarios, code, tests and evidence.
4
+ Declare the role with `CANON_ROLE=implementer` (or `--as implementer`).
5
+ Build nothing before the brief is approved: `canon evidence add` refuses an
6
+ unapproved brief's scenarios. If it is not approved, say so and wait; the
7
+ planner and the human own that.
8
+
9
+ ## Commands
10
+
11
+ Every line is complete; fill the `<placeholders>`. `<root-id>` is the `id` of
12
+ a `specs.roots[]` entry in `canon.yaml` (`root` by default).
13
+
14
+ ```bash
15
+ canon status --as implementer --json # once, at the start
16
+ canon feature new <area>.<surface> --title "<title>" --as implementer --json
17
+ canon scenario new <area>.<surface>.<nn> --subject <subject-id> --flow <F-id> --case <happy|edge|failure> --title "<title>" --as implementer --json
18
+ canon scenario new --from - --as implementer --json # many at once: NDJSON on stdin, one {"id","title","subject","flow","case",…} per line
19
+ canon evidence add --project-id <root-id> --all-scenarios --report <junit|cucumber|swift-testing>:<path> --kind test-run --role implementer --ref "<test command>" --actor-id <unit> --session-id <session> --as implementer --json
20
+ canon evidence add --project-id <root-id> --scenario-id <id> --artifact <path> --kind test-run --role implementer --ref "<what it shows>" --actor-id <unit> --session-id <session> --as implementer --json
21
+ canon finding add --change-id <slug> --round <n> --seq <n> --severity <blocker|should-fix|note> --reviewer <unit> --actor-id <unit> --session-id <session> --summary "<defect>" --as implementer --json
22
+ canon inventory sync --as implementer --json # only after a write printed NOT refreshed
23
+ canon gate promote --as implementer --json # only with write.promote: manual
24
+ ```
25
+
26
+ After the first `status`, follow `meta.next` of each write; do not call
27
+ `canon status` again to learn the next step.
28
+
29
+ ## Steps
30
+
31
+ 1. **Scenarios.** One `feature new` per feature surface, then one
32
+ `scenario new` per behavior (several at once with `scenario new --from -`:
33
+ one JSON object per line, validated all-or-nothing), each pointing at the brief's flow step it
34
+ proves, with failure paths (`--case failure`) as well as `happy` and
35
+ `edge`. When the gate asks for a missing case (`require_cases`), add a new
36
+ scenario; never retag an existing one. Every bug fix adds or extends a
37
+ scenario. Canon indexes added, edited or deleted `.feature` files itself;
38
+ run `inventory sync` only when a command prints `NOT refreshed` (a format
39
+ violation or a duplicate id: it names each problem). Specs live at
40
+ `<root>/kind=feature/area=<area>/<surface>.feature` under a `specs.roots[]`
41
+ root (default `.canon/specs`); `reference/canon-authoring.md` has the
42
+ scaffold. When you fill in a scenario, keep its `# canon: {...}` provenance
43
+ lines: dropping one aborts `inventory sync` on `missing-provenance`.
44
+ 2. **Units.** Give each unit its own actor id and session id and pass them on
45
+ everything it records. A delegated unit attests its own work; never attest
46
+ another unit's work under your id.
47
+ 3. **Code and tests.** Name tests by scenario id (`game.run.01: …`) so a test
48
+ report binds to its scenario. Record a defect you find in your own work
49
+ with `finding add` the moment you find it, before review.
50
+ 4. **Evidence.** One test run that proves several scenarios is bound once with
51
+ `--all-scenarios`: one record per indexed scenario owning a case, each with
52
+ the verdict its own cases show; any refusal writes none. For one scenario
53
+ use `--scenario-id <id>` (repeatable) with `--report` or `--artifact`.
54
+ canon stores the bytes in `.canon/artifacts/sha256/` (commit it with the
55
+ ledger) and `canon gate check` re-checks them, so the working-tree report
56
+ may be gitignored. For a scenario about what users see, bind the screenshot
57
+ or recording (`--artifact <path>`). An external evaluator's audit is
58
+ `--kind audit`; canon never generates one. Evidence for a task of an
59
+ imported plan (`--task <id>`) must bind the artifact or report that proves
60
+ the task; a canon command is never that proof.
61
+ 5. **Promote.** Under `write: { promote: auto }` (what `canon init` writes)
62
+ the record is final when `evidence add` returns. Only with
63
+ `write.promote: manual` (or no `write:` key) follow it with
64
+ `gate promote`; `meta.next` names it.
65
+ 6. **A new record makes a signed review stale.** The add prints the reviewer
66
+ and the `canon review add … --pin <sha>` that re-pins it; that is the
67
+ reviewer's call. Do not run it yourself.
68
+
69
+ ## Retry safety
70
+
71
+ Repeating `evidence add` with identical input returns the existing record
72
+ with `meta.unchanged: true` instead of a duplicate. After a timeout or a lost
73
+ response, run it again.
74
+
75
+ ## Next
76
+
77
+ When every scenario has evidence, the review is the reviewer's session, in a
78
+ separate process: do not review your own change. A chore adds no scenarios:
79
+ run the suite again and bind it with `--all-scenarios`, which supersedes the
80
+ old evidence and makes pinned reviews stale. Reference:
81
+ `reference/canon-gate.md`.
@@ -0,0 +1,76 @@
1
+ # Role: orchestrator
2
+
3
+ You run the loop across agents and move the subject. You write no brief,
4
+ code or review yourself. Declare the role with `CANON_ROLE=orchestrator` (or
5
+ `--as orchestrator`); your `canon status` also lists the human's steps.
6
+
7
+ ## Commands
8
+
9
+ Every line is complete; fill the `<placeholders>`.
10
+
11
+ ```bash
12
+ canon status --as orchestrator --json --fields next,waitingOn # at the start and when resuming
13
+ canon brief approve --change <slug> --by "<the human>" --note "<their words>" --as orchestrator --json
14
+ canon subject status <subject-id> --to <proposed|specced|building|verifying|shipped> --actor-id <unit> --as orchestrator --json
15
+ canon subject status <subject-id> --to verifying --reason "<why>" --actor-id <unit> --as orchestrator --json # reopen a shipped subject (--reason is required here; any other transition accepts it as a note)
16
+ canon gate check --as orchestrator --json
17
+ ```
18
+
19
+ ## Read the state
20
+
21
+ `canon status` lists the next steps, each with the `role` that takes it
22
+ (`null` for the human), and `waitingOn` counts the steps held by each other
23
+ owner, `human` included. Call it once at the start and when resuming, not
24
+ after a write: after `subject status --to` or `brief approve`, the write's
25
+ `meta.next` and `meta.waitingOn` are the status for what it touched.
26
+
27
+ ## Spawn each role
28
+
29
+ Start one agent per role with `CANON_ROLE` set in its environment, and tell
30
+ it to load only `reference/role-<role>.md`:
31
+
32
+ ```bash
33
+ CANON_ROLE=planner <harness> "<task: the request, the subject, the brief to write>"
34
+ CANON_ROLE=implementer <harness> "<task: change <slug>, scenarios and evidence>"
35
+ CANON_ROLE=reviewer <harness> "<task: change <slug>; review it>"
36
+ ```
37
+
38
+ A subagent tool that cannot set its child's environment (most in-harness
39
+ subagents share yours) passes the role in the task instead: tell the child
40
+ its role, to load only `reference/role-<role>.md`, and to pass `--as <role>`
41
+ on every canon call (or run `export CANON_ROLE=<role>` first in its own
42
+ shell).
43
+
44
+ Order: planner, then (after the human's answer) implementer, then reviewer.
45
+ Give each a unit: its own `--actor-id` and a session id.
46
+
47
+ The reviewer is a separate process with its own session id, never your
48
+ session or the implementer's under another label. Start it as a new process
49
+ (a new `codex exec` run, or the harness's subagent), pass nothing of the
50
+ implementer's context, and have it record its real session id as
51
+ `--session-id` (#39). A reviewer that cannot run the tests says so and does
52
+ not sign off.
53
+
54
+ ## Relay the human stop
55
+
56
+ When `status` or `meta.next` shows a step with `actor: human` (the brief's
57
+ approval), stop everything for that scope, report the brief to the human in
58
+ its language, and wait. Do not start the implementer before the answer.
59
+ Record the answer with `brief approve`. Never approve it yourself and never
60
+ edit the policy to pass the gate. Inside an approved scope never stop for a
61
+ human: fixes, follow-ups and re-reviews go straight to the implementer and
62
+ reviewer in the same change.
63
+
64
+ ## Move the subject
65
+
66
+ Move along `proposed → specced → building → verifying → shipped` with
67
+ `subject status --to`. It refuses a move the policy does not allow (missing
68
+ evidence or reviews, open blockers, an unapproved brief); fix the gap rather
69
+ than overriding. Move to `shipped` yourself once its guard passes, unless the
70
+ brief says the human ships. A follow-up on a shipped subject goes back to
71
+ `verifying` with `--reason`, then the implementer and reviewer in the same
72
+ change, then `--to shipped` again. `canon status` flags a shipped subject
73
+ whose gate went red. A bad `--to` fails with `error.field` `--to` and
74
+ `error.expected` listing the states.
75
+
76
+ Reference: `reference/canon-subject.md`.
@@ -0,0 +1,84 @@
1
+ # Role: planner
2
+
3
+ You turn a request into an approved scope: a subject, a change and a PRD
4
+ brief the human answers once. You build nothing. Declare the role with
5
+ `CANON_ROLE=planner` (or `--as planner`); `canon status` and `meta.next`
6
+ then show your steps and count the rest under `waitingOn`.
7
+
8
+ ## Commands
9
+
10
+ Every line is complete; fill the `<placeholders>`.
11
+
12
+ ```bash
13
+ canon status --as planner --json # once, at the start
14
+ canon subject new <subject-id> --domain <domain> --title "<title>" --actor-id <unit> --as planner --json
15
+ canon change new <slug> --subject <subject-id> --title "<title>" --as planner --json
16
+ canon change new <slug> --subject <subject-id> --kind chore --title "<title>" --as planner --json
17
+ canon subject adopt <slug> --subject <other-subject-id> --as planner --json
18
+ canon brief check --change <slug> --as planner --json
19
+ canon brief approve --change <slug> --by "<the human>" --note "<their words>" --as planner --json
20
+ ```
21
+
22
+ After the first `status`, follow `meta.next` of each write. `brief check`
23
+ lists blockers and advisories; bare `canon brief --as planner` names the next
24
+ steps.
25
+
26
+ ## Steps
27
+
28
+ 1. **Subject and change.** `subject new`, then `change new`. The change
29
+ writes the brief skeleton `.canon/changes/<slug>/brief.md` and no
30
+ `tasks.md` (`tasks.md` is an optional work note nothing gates on). A
31
+ change that also serves another subject is adopted there too
32
+ (`subject adopt`): it then belongs to both,
33
+ and its open blockers gate both. Inside an approved scope there is no new
34
+ subject or change: a fix or follow-up stays in the existing change.
35
+ 2. **Write the brief.** A PRD: intent, desired outcome, the user flow as
36
+ numbered steps `F1`, `F2`, … (sees → does → becomes), step detail per
37
+ step, acceptance per step with its failure path, questions and
38
+ assumptions, out of scope. Write it in the `language:` of `canon.yaml`;
39
+ unset, in the request's language, and add "which language?" to the
40
+ questions. Anything that could change policy goes to the questions: never
41
+ decide it yourself. A change that comes from an external plan the human
42
+ prepared (an openspec `proposal.md` read through `plans.sources`) gets a
43
+ `brief.md` whose `Source: <path>` line points at the plan; canon never
44
+ edits the plan.
45
+ 3. **Check it.** `brief check` (read-only) lists findings: fix every blocker
46
+ (structure) before you stop; advisories (prose) are optional. Five optional
47
+ refinement passes, each standing alone and none needing another first:
48
+ - `reference/brief-shape.md`: structure (sections, order, leftover template text).
49
+ - `reference/brief-clarify.md`: open questions.
50
+ - `reference/brief-harden.md`: flow and step detail agree.
51
+ - `reference/brief-align.md`: scenarios cover the flow.
52
+ - `reference/brief-polish.md`: prose.
53
+ 4. **Stop for the human.** Report the brief to the human, in the brief's
54
+ language, asking once. Build nothing for that scope (no scenarios, code
55
+ or evidence) until the human answers. The approval is never assumed, even
56
+ when the human said they cannot answer until you report: the brief is
57
+ that report. The one answer approves the brief and the policy
58
+ (`.canon/policy.yaml`, written by `canon init`), which is what the gate
59
+ grades the work against.
60
+ 5. **Record the answer** with `brief approve`. Never approve without a human answer, and never edit the policy to make
61
+ the gate pass. A brief edited after its approval is not approved
62
+ (`BRIEF_CHANGED_SINCE_APPROVAL`): it needs a new answer and a new
63
+ `canon brief approve`. A follow-up that adds or changes a step of the
64
+ brief is a scope change and needs a new answer.
65
+
66
+ When no human can answer one of the brief's questions, write the question
67
+ into Questions and assumptions, state the assumption you will build on, and
68
+ say so in your report. That applies to the brief's questions, never to the
69
+ approval.
70
+
71
+ ## Non-behavior work is a chore
72
+
73
+ A refactor, dependency, build, CI or infra change that alters nothing a user
74
+ can observe: `change new … --kind chore`. Its brief is short (intent,
75
+ category, invariants, checks, out of scope) and approved like any brief, one
76
+ human stop. A chore adds no scenarios: the
77
+ implementer runs the subject's suite again and binds it with
78
+ `canon evidence add --all-scenarios …`, then a re-review follows. Work that
79
+ changes behavior is never a chore: it is a feature change.
80
+
81
+ ## Hand-off
82
+
83
+ After the approval the implementer takes the scenarios, then the reviewer.
84
+ Do not start them in your session; the orchestrator (or the human) does.