@journeykit/canon 0.18.0 → 0.21.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 +101 -184
- package/dist/skills/reference/brief-align.md +42 -0
- package/dist/skills/reference/brief-clarify.md +42 -0
- package/dist/skills/reference/brief-harden.md +41 -0
- package/dist/skills/reference/brief-polish.md +42 -0
- package/dist/skills/reference/brief-shape.md +46 -0
- package/dist/skills/reference/canon-authoring.md +1 -1
- package/dist/skills/reference/canon-context.md +3 -3
- package/dist/skills/reference/canon-gate.md +59 -36
- package/dist/skills/reference/canon-inventory.md +2 -2
- package/dist/skills/reference/canon-learn.md +4 -6
- package/dist/skills/reference/canon-plan-import.md +10 -7
- package/dist/skills/reference/canon-policy.md +5 -3
- package/dist/skills/reference/canon-review.md +2 -2
- package/dist/skills/reference/canon-storage.md +18 -4
- package/dist/skills/reference/canon-subject.md +132 -51
- package/dist/skills/reference/role-implementer.md +80 -0
- package/dist/skills/reference/role-orchestrator.md +76 -0
- package/dist/skills/reference/role-planner.md +84 -0
- package/dist/skills/reference/role-reviewer.md +70 -0
- package/package.json +4 -4
|
@@ -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,
|
|
157
|
-
|
|
158
|
-
`
|
|
159
|
-
|
|
160
|
-
|
|
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
|
|
164
|
-
canon subject status: refused — get the human's answer to the brief and record it
|
|
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
|
|
172
|
-
record 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
|
|
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
|
-
|
|
189
|
-
|
|
190
|
-
|
|
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,12 +256,12 @@ 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;
|
|
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
|
|
|
@@ -289,7 +295,8 @@ canon subject adopt subject-domain-loop-plan \
|
|
|
289
295
|
```
|
|
290
296
|
|
|
291
297
|
A change made with `canon change new` (below) is recorded and adopted
|
|
292
|
-
already
|
|
298
|
+
already. A `tasks.md` in it is an optional work note nothing gates on
|
|
299
|
+
(`canon change new` no longer writes one); an existing one is still read:
|
|
293
300
|
`canon evidence add --task` and `canon gate task` read the file as it is,
|
|
294
301
|
and a flip re-imports that change itself. Run `canon ingest plans` for a
|
|
295
302
|
plan written outside canon, or when `canon query --kind task` must list
|
|
@@ -334,16 +341,21 @@ canon change new add-login --subject auth --title "Add login"
|
|
|
334
341
|
|
|
335
342
|
```
|
|
336
343
|
canon change new: wrote .canon/changes/add-login/brief.md
|
|
337
|
-
canon change new: wrote .canon/changes/add-login/tasks.md
|
|
338
344
|
canon change new: recorded change `add-login` adopted into subject `auth` — written directly; nothing to promote
|
|
339
345
|
```
|
|
340
346
|
|
|
341
|
-
- **Writes** canon's own change dir, `.canon/changes/<slug
|
|
342
|
-
`brief.md` (`# <title>`,
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
+
- **Writes** canon's own change dir, `.canon/changes/<slug>/`, holding
|
|
348
|
+
`brief.md` only: the PRD skeleton (`# <title>`, then the sections intent,
|
|
349
|
+
desired outcome, user flow, step detail, acceptance, questions and
|
|
350
|
+
assumptions, out of scope, each opened by a
|
|
351
|
+
`<!-- canon:section <key> role=… -->` marker and a hint comment, with
|
|
352
|
+
the headings in the repo's `language:`), and a comment naming
|
|
353
|
+
`canon brief approve` as where the human's answer is recorded, so the
|
|
354
|
+
file starts unapproved. The intent section's first paragraph holds the
|
|
355
|
+
title, which imports as the change summary. `--kind chore` writes the
|
|
356
|
+
short chore brief instead (intent, category, invariants, checks, out of
|
|
357
|
+
scope; see `canon brief` below). No `tasks.md` is written:
|
|
358
|
+
`canon status` lists each flow step's scenarios instead.
|
|
347
359
|
`.canon/changes/` is canon's native plan source: every plan reader
|
|
348
360
|
reads it with no `plans:` entry, so `canon.yaml` needs no edit and no
|
|
349
361
|
external plans source is needed (see `canon-plan-import`).
|
|
@@ -366,9 +378,10 @@ canon change new: recorded change `add-login` adopted into subject `auth` — wr
|
|
|
366
378
|
exist, or when the slug already has a `.canon/changes/<slug>` dir
|
|
367
379
|
(active or under `.canon/changes/archive/`) or a `change` record.
|
|
368
380
|
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
381
|
+
A `tasks.md` is an optional work note canon never writes and nothing
|
|
382
|
+
gates on. If one exists, rows added as `- [ ] <n> <title>` are flipped with
|
|
383
|
+
`canon gate task <slug>#<n>` once evidence exists; `canon evidence add
|
|
384
|
+
--task` and the flip read the file as it is, so a new row needs no
|
|
372
385
|
`canon ingest plans` run.
|
|
373
386
|
|
|
374
387
|
A change from an external plan (an openspec change listed under
|
|
@@ -376,10 +389,59 @@ A change from an external plan (an openspec change listed under
|
|
|
376
389
|
`canon subject adopt`) gets its brief as a canon file too:
|
|
377
390
|
`.canon/changes/<change>/brief.md` holding a `Source: <path>` line that
|
|
378
391
|
points at the external document and, once the human answers, the
|
|
379
|
-
`Approved:` line. Canon never edits the external document. Such a
|
|
392
|
+
`Approved:` line that `canon brief approve` writes. Canon never edits the external document. Such a
|
|
380
393
|
pointer `brief.md` is not a change of its own: the native reader skips
|
|
381
394
|
it, so the external source keeps the change, its summary and its tasks.
|
|
382
395
|
|
|
396
|
+
### The brief: `canon brief`, `canon brief check`, `canon brief approve`
|
|
397
|
+
|
|
398
|
+
```bash
|
|
399
|
+
canon brief [--change <slug>] # state and the next one to three steps; never edits
|
|
400
|
+
canon brief check [--change <slug>] [--json] # findings; exit 0 clean, 2 blockers, 1 error
|
|
401
|
+
canon brief approve --change <slug> --by <the human> --note "<their words>"
|
|
402
|
+
```
|
|
403
|
+
|
|
404
|
+
Without `--change`, the repo's only live change is read. The brief is the
|
|
405
|
+
PRD `canon change new` writes; sections are found by their
|
|
406
|
+
`<!-- canon:section <key> role=… -->` marker, never by heading text. A
|
|
407
|
+
brief without markers (written before 0.19) checks as `brief.legacy`, an
|
|
408
|
+
advisory, and is read as before.
|
|
409
|
+
|
|
410
|
+
- **Blockers (structure, `brief.*`)**: a missing, duplicated or
|
|
411
|
+
out-of-order section; a flow with no steps or a duplicate step id;
|
|
412
|
+
step detail that does not match the flow ids in order; leftover
|
|
413
|
+
placeholders (a hint comment still there, an empty section,
|
|
414
|
+
`TODO`/`TBD`); a malformed waiver; for a chore, a flow/detail/acceptance
|
|
415
|
+
section, a category outside `refactor|dependency|build|ci|infra`, or
|
|
416
|
+
invariants/checks that say only "none". Once the subject is `building`
|
|
417
|
+
or later, a flow step no scenario points at (`brief.flow-uncovered`) or
|
|
418
|
+
one with no failure scenario (`brief.flow-no-failure`): these two
|
|
419
|
+
**gate shipping, never approval**.
|
|
420
|
+
- **Advisories (prose, `prose.*`)**: phrase lists, sentence and
|
|
421
|
+
paragraph length, and key-sentence marking in `role=normative`
|
|
422
|
+
sections, for `language: en` and `ko`. Never an authorship verdict.
|
|
423
|
+
- **Waivers**: `<!-- canon:waive <rule-id> <section-key> "<reason>" -->`,
|
|
424
|
+
one rule in one section, reason required; listed in the output.
|
|
425
|
+
- **`approve`** records the human's answer: it writes
|
|
426
|
+
`Approved: <who> <when> — "<words>" (brief sha256:<digest>)` into the
|
|
427
|
+
brief and records the policy approval in the same call. It refuses
|
|
428
|
+
(exit 2) while the check has structure blockers. Edit the brief
|
|
429
|
+
afterwards and every reader says `BRIEF_CHANGED_SINCE_APPROVAL` until a
|
|
430
|
+
human answers again and `approve` runs again. Run it only from the
|
|
431
|
+
human's answer, with their words as `--note`.
|
|
432
|
+
|
|
433
|
+
The optional refinement passes that fix findings before the human stop are
|
|
434
|
+
listed in `role-planner.md` in this directory.
|
|
435
|
+
|
|
436
|
+
A **chore** (`canon change new --kind chore`) changes how the subject is
|
|
437
|
+
built or kept, never what it does. Its brief is intent, category,
|
|
438
|
+
invariants, checks, out of scope; it adds no scenarios, and its evidence
|
|
439
|
+
is the subject's suite run again at the chore commit with
|
|
440
|
+
`canon evidence add --all-scenarios --report <kind>:<path> …`. That
|
|
441
|
+
supersedes the old evidence, which makes the pinned reviews stale, so an
|
|
442
|
+
independent re-review follows. Work that changes behavior is a feature
|
|
443
|
+
change, never a chore.
|
|
444
|
+
|
|
383
445
|
## Where are we: `canon status`
|
|
384
446
|
|
|
385
447
|
```bash
|
|
@@ -413,7 +475,7 @@ uses, so status and the gate cannot disagree about a gap:
|
|
|
413
475
|
`require_review` on, a `warning:` line counts the in-effect reviews that
|
|
414
476
|
predate review pinning (0.15), checked against the commit each pinned:
|
|
415
477
|
current, stale (named) and undecidable.
|
|
416
|
-
- **Brief** (0.16, 0.17) — for each `building`, `verifying` or `shipped`
|
|
478
|
+
- **Brief** (0.16, 0.17, 0.19) — for each `building`, `verifying` or `shipped`
|
|
417
479
|
subject, and each `proposed` subject whose newest adopted change has a
|
|
418
480
|
brief (a canon `brief.md`, or an external `proposal.md`), a `brief:`
|
|
419
481
|
line under it says whether the brief is approved:
|
|
@@ -423,10 +485,16 @@ uses, so status and the gate cannot disagree about a gap:
|
|
|
423
485
|
change (the last one adopted; a live dir before an archived one), and
|
|
424
486
|
only there: a line that starts with `Approved:` (indent allowed), says
|
|
425
487
|
something after it, and sits outside HTML comments (`<!-- … -->`,
|
|
426
|
-
multi-line included) and fenced code blocks (```` ``` ```` or `~~~`)
|
|
427
|
-
|
|
428
|
-
`Approved: <who> <when> — "<the human's words>"
|
|
429
|
-
|
|
488
|
+
multi-line included) and fenced code blocks (```` ``` ```` or `~~~`).
|
|
489
|
+
`canon brief approve` writes it from the human's answer, as
|
|
490
|
+
`Approved: <who> <when> — "<the human's words>" (brief sha256:<digest>)`,
|
|
491
|
+
the digest covering the brief without its `Approved:` lines. A brief
|
|
492
|
+
edited after that reads `brief: not approved (<brief> changed after its
|
|
493
|
+
approval (BRIEF_CHANGED_SINCE_APPROVAL))` and names the re-approval
|
|
494
|
+
command: the changed brief needs a new human answer. A line with no
|
|
495
|
+
digest (written before 0.19) stays approved, with an `advisory:` to
|
|
496
|
+
re-approve so it binds to the text. Fixes and follow-ups
|
|
497
|
+
stay in the approved change, so a newly adopted change is a
|
|
430
498
|
new scope: an older change's approval does not cover it. A change from
|
|
431
499
|
an external plan has its brief in the same canon file, with a
|
|
432
500
|
`Source: <path>` line pointing at the external document; an `Approved:`
|
|
@@ -440,13 +508,19 @@ uses, so status and the gate cannot disagree about a gap:
|
|
|
440
508
|
and never edits, so its approval lives in that canon file)``. With a
|
|
441
509
|
canon brief and no line it reads
|
|
442
510
|
``brief: not approved (<brief> has no `Approved:` line)``.
|
|
443
|
-
There is no record kind for it; the file is the record.
|
|
511
|
+
There is no record kind for it; the file is the record. For a brief in
|
|
512
|
+
the PRD format, `brief check: <n> blocker(s), <n> advisory(ies)` follows
|
|
513
|
+
(`canon brief` names the next step), then, per flow step of the
|
|
514
|
+
change, `flow F1` (with ` — <gaps>` when it has no scenario or no
|
|
515
|
+
failure scenario) and under it each scenario that carries
|
|
516
|
+
`@flow:<change>.F1` with its title, `[failure]` when it is, its
|
|
517
|
+
evidence (`yes`/`none`) and its review state.
|
|
444
518
|
- **Red shipped subject** (0.18) — a `shipped` subject that the
|
|
445
519
|
`verifying → shipped` guard would now refuse, by the guard's own checks
|
|
446
520
|
(owned scenarios, verdicts, required cases, audits, and, where
|
|
447
521
|
`require_review` covers `shipped`, reviews and open blockers under
|
|
448
522
|
`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>` ``,
|
|
523
|
+
``gate: red — <n> <class> (first: <subject>), …; reopen with `canon subject status <id> --to verifying --reason "<why it reopens>" --actor-id <unit>` ``,
|
|
450
524
|
carries `red: [...]` in `--json`, and that reopen is its only `next:`
|
|
451
525
|
step. Open blockers count against every subject a change belongs to.
|
|
452
526
|
- **Unowned** — scenarios with no `@subject:` tag.
|
|
@@ -481,7 +555,7 @@ uses, so status and the gate cannot disagree about a gap:
|
|
|
481
555
|
…` (its reason adds "then `canon gate promote`" only under
|
|
482
556
|
`write.promote: manual`); an open blocker → `canon finding close …`; a
|
|
483
557
|
due, unreviewed, evidenced scenario → `canon review add …` from another
|
|
484
|
-
session; none of these → `canon subject status <id> <next status>`.
|
|
558
|
+
session; none of these → `canon subject status <id> --to <next status>`.
|
|
485
559
|
Last, unowned scenarios → `canon status`, after tagging them in their
|
|
486
560
|
`.feature` files (status re-indexes the edit; no sync step). With more
|
|
487
561
|
than one `specs.roots[]` entry, every suggested `canon scenario new`
|
|
@@ -497,15 +571,17 @@ uses, so status and the gate cannot disagree about a gap:
|
|
|
497
571
|
unanswered> (an older change's approval does not cover a new scope),
|
|
498
572
|
and the approval is never assumed: `canon evidence add` refuses <id>'s
|
|
499
573
|
scenarios and tasks until it is recorded; once the human answers,
|
|
500
|
-
<record>
|
|
501
|
-
|
|
502
|
-
<
|
|
503
|
-
``create <brief> holding a `Source: <proposal>` line
|
|
504
|
-
words
|
|
574
|
+
<record> (one call records the brief and the policy approval)``, where
|
|
575
|
+
`<record>` is ``record their words with `canon brief approve --change
|
|
576
|
+
<c> --by <the human> --note "<their words>"` ``, or, for an external
|
|
577
|
+
change, ``create <brief> holding a `Source: <proposal>` line, then
|
|
578
|
+
record their words with `canon brief approve --change <c> --by <the
|
|
579
|
+
human> --note "<their words>"` ``; for a brief edited after its
|
|
580
|
+
approval, ``re-approve with `canon brief approve …` ``.
|
|
505
581
|
It is the subject's only step, whatever its status: nothing suggests
|
|
506
582
|
`canon feature new`, `canon scenario new`, `canon evidence add`, a
|
|
507
583
|
review or a move-on for it before the human answers, and
|
|
508
|
-
`canon subject status <id> shipped` refuses on the same check. The
|
|
584
|
+
`canon subject status <id> --to shipped` refuses on the same check. The
|
|
509
585
|
approval is never assumed. A subject with no brief to answer gets no human step;
|
|
510
586
|
its `brief:` line says
|
|
511
587
|
`not approved (no adopted change; advisory, nothing to approve yet)` or
|
|
@@ -513,7 +589,7 @@ uses, so status and the gate cannot disagree about a gap:
|
|
|
513
589
|
approve yet)`` and nothing is withheld (a `proposed` subject with no
|
|
514
590
|
brief, and a `specced` subject, show no `brief:` line at all). Once nothing blocks, a
|
|
515
591
|
ship-ready verifying subject gets the agent's own
|
|
516
|
-
`canon subject status <id> shipped`; status never asks a human to ship.
|
|
592
|
+
`canon subject status <id> --to shipped`; status never asks a human to ship.
|
|
517
593
|
|
|
518
594
|
Status fails closed rather than suggest progress the gate would refuse:
|
|
519
595
|
|
|
@@ -545,7 +621,7 @@ Status fails closed rather than suggest progress the gate would refuse:
|
|
|
545
621
|
],
|
|
546
622
|
"unowned": ["cart.promo.01"],
|
|
547
623
|
"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
|
|
624
|
+
{ "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
625
|
{ "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
626
|
{ "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
627
|
{ "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 +645,12 @@ plan document the change comes from (the canon brief's `Source:` line,
|
|
|
569
645
|
or, before that file exists, the external `proposal.md`; omitted for a
|
|
570
646
|
canon-written change); `approval` the text after `Approved:`; and
|
|
571
647
|
`enforced` whether the missing approval blocks (a brief exists to
|
|
572
|
-
answer). The 0.16 `proposal` field is gone.
|
|
648
|
+
answer). The 0.16 `proposal` field is gone. `changed` (omitted when
|
|
649
|
+
false) says the brief was edited after its approval, `legacyApproval`
|
|
650
|
+
that the `Approved:` line carries no digest, `findings` is
|
|
651
|
+
`{ "blockers", "advisories" }` from `canon brief check` (PRD briefs
|
|
652
|
+
only), and `flow` lists each flow step as `{ "id", "change",
|
|
653
|
+
"scenarios": [{ id, title, failure, evidenced, review }], "gaps" }`.
|
|
573
654
|
|
|
574
655
|
Each `next` entry's `actor` is `agent` (its `command` is a canon command
|
|
575
656
|
line) or `human` (its `command` says what to get from the human).
|
|
@@ -0,0 +1,80 @@
|
|
|
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.
|
|
43
|
+
2. **Units.** Give each unit its own actor id and session id and pass them on
|
|
44
|
+
everything it records. A delegated unit attests its own work; never attest
|
|
45
|
+
another unit's work under your id.
|
|
46
|
+
3. **Code and tests.** Name tests by scenario id (`game.run.01: …`) so a test
|
|
47
|
+
report binds to its scenario. Record a defect you find in your own work
|
|
48
|
+
with `finding add` the moment you find it, before review.
|
|
49
|
+
4. **Evidence.** One test run that proves several scenarios is bound once with
|
|
50
|
+
`--all-scenarios`: one record per indexed scenario owning a case, each with
|
|
51
|
+
the verdict its own cases show; any refusal writes none. For one scenario
|
|
52
|
+
use `--scenario-id <id>` (repeatable) with `--report` or `--artifact`.
|
|
53
|
+
canon stores the bytes in `.canon/artifacts/sha256/` (commit it with the
|
|
54
|
+
ledger) and `canon gate check` re-checks them, so the working-tree report
|
|
55
|
+
may be gitignored. For a scenario about what users see, bind the screenshot
|
|
56
|
+
or recording (`--artifact <path>`). An external evaluator's audit is
|
|
57
|
+
`--kind audit`; canon never generates one. Evidence for a task of an
|
|
58
|
+
imported plan (`--task <id>`) must bind the artifact or report that proves
|
|
59
|
+
the task; a canon command is never that proof.
|
|
60
|
+
5. **Promote.** Under `write: { promote: auto }` (what `canon init` writes)
|
|
61
|
+
the record is final when `evidence add` returns. Only with
|
|
62
|
+
`write.promote: manual` (or no `write:` key) follow it with
|
|
63
|
+
`gate promote`; `meta.next` names it.
|
|
64
|
+
6. **A new record makes a signed review stale.** The add prints the reviewer
|
|
65
|
+
and the `canon review add … --pin <sha>` that re-pins it; that is the
|
|
66
|
+
reviewer's call. Do not run it yourself.
|
|
67
|
+
|
|
68
|
+
## Retry safety
|
|
69
|
+
|
|
70
|
+
Repeating `evidence add` with identical input returns the existing record
|
|
71
|
+
with `meta.unchanged: true` instead of a duplicate. After a timeout or a lost
|
|
72
|
+
response, run it again.
|
|
73
|
+
|
|
74
|
+
## Next
|
|
75
|
+
|
|
76
|
+
When every scenario has evidence, the review is the reviewer's session, in a
|
|
77
|
+
separate process: do not review your own change. A chore adds no scenarios:
|
|
78
|
+
run the suite again and bind it with `--all-scenarios`, which supersedes the
|
|
79
|
+
old evidence and makes pinned reviews stale. Reference:
|
|
80
|
+
`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 # follow-up on a shipped subject
|
|
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.
|