@journeykit/canon 0.22.0 → 0.24.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 +26 -2
- package/dist/skills/reference/canon-authoring.md +5 -5
- package/dist/skills/reference/canon-gate.md +78 -86
- package/dist/skills/reference/canon-learn.md +1 -1
- package/dist/skills/reference/canon-review.md +47 -44
- package/dist/skills/reference/canon-storage.md +44 -6
- package/dist/skills/reference/canon-subject.md +26 -28
- package/dist/skills/reference/role-implementer.md +38 -15
- package/dist/skills/reference/role-orchestrator.md +23 -3
- package/dist/skills/reference/role-planner.md +8 -7
- package/dist/skills/reference/role-reviewer.md +23 -5
- package/package.json +4 -4
package/dist/skills/SKILL.src.md
CHANGED
|
@@ -24,7 +24,7 @@ a feature, a one-line request included. A human is needed once per scope:
|
|
|
24
24
|
answer until you report: the brief is that report. The one answer
|
|
25
25
|
approves the brief and the policy, `.canon/policy.yaml`, which is what
|
|
26
26
|
the gate grades the work against. Record it only from the human's answer:
|
|
27
|
-
`canon brief approve --change <slug> --by <the human> --note "<their words>"`.
|
|
27
|
+
`canon brief approve --change-id <slug> --by <the human> --note "<their words>"`.
|
|
28
28
|
Never edit the policy to make the gate pass: a policy that differs from
|
|
29
29
|
the approved one fails closed (`POLICY_CHANGED_SINCE_APPROVAL`, not
|
|
30
30
|
waivable); stop and ask the human. A brief edited after its
|
|
@@ -39,7 +39,9 @@ a feature, a one-line request included. A human is needed once per scope:
|
|
|
39
39
|
new answer. A follow-up on a shipped subject reopens it
|
|
40
40
|
(`canon subject status <id> --to verifying --reason "<why>"`); a fix
|
|
41
41
|
closed `fixed`, and an approved chore, then need the whole suite evidenced
|
|
42
|
-
again (`evidence add --all-scenarios
|
|
42
|
+
again (`evidence add --all-scenarios`, for a chore after its approval)
|
|
43
|
+
before it ships, and a chore also needs one change-level review
|
|
44
|
+
(`review add --change-id <slug>`).
|
|
43
45
|
- When no human can answer one of the brief's questions, write your
|
|
44
46
|
assumption into the brief's Questions and assumptions, build on it, and say
|
|
45
47
|
so in your report. That covers the brief's questions and work inside an
|
|
@@ -58,6 +60,24 @@ Every write command says whether the record is final (`written directly`,
|
|
|
58
60
|
or `staged and promoted in this run`) or staged; only a staged record
|
|
59
61
|
needs `canon gate promote`, and only `write.promote: manual` leaves one.
|
|
60
62
|
|
|
63
|
+
The ledger is latest-only. It holds the current record of each key; writing a
|
|
64
|
+
newer version moves what it supersedes to `.canon/archive`, and `status`,
|
|
65
|
+
`gate check`, the guards and `query` read the ledger only. Earlier versions
|
|
66
|
+
are one flag away: `canon query --kind <k> --history`. A ledger written
|
|
67
|
+
before 0.23, or one whose move was interrupted, is brought to one file per
|
|
68
|
+
key by `canon ledger compact` (`canon migrate` runs it). A record binds only
|
|
69
|
+
what its own write names: evidence carries nothing forward from an earlier
|
|
70
|
+
record, and a review stays current while the verdict and checks of the
|
|
71
|
+
evidence it saw are unchanged.
|
|
72
|
+
|
|
73
|
+
Where artifact bytes live is the repo's choice: `canon.yaml`'s `artifact_store:`
|
|
74
|
+
block defaults to the local, git-tracked `.canon/artifacts/sha256/`, or names
|
|
75
|
+
an object-store tier, in which case `evidence add`, `review add` and
|
|
76
|
+
`evidence vault` upload by digest and keep nothing locally. The gate and
|
|
77
|
+
`canon artifacts verify [--full]` ask the store and refuse, never pass, when
|
|
78
|
+
it cannot be reached. Moving an existing store with `canon artifacts migrate`
|
|
79
|
+
is the repo owner's decision, not a step of the loop.
|
|
80
|
+
|
|
61
81
|
## Know your role
|
|
62
82
|
|
|
63
83
|
canon cannot detect a role; you declare it. Pass `--as <role>` or set
|
|
@@ -93,6 +113,10 @@ A role is guidance, not enforcement: `--role` on `evidence add` and
|
|
|
93
113
|
on `error.code`, fix the option `field` names using the `expected` values,
|
|
94
114
|
and follow `error.hint`. Every refusal carries a concrete `hint`: follow
|
|
95
115
|
it. The codes are stable; `canon schema --errors --json` lists them.
|
|
116
|
+
- Flags that name an id are spelled `--<noun>-id` everywhere (`--change-id`,
|
|
117
|
+
`--project-id`, `--subject-id`, `--scenario-id`, `--actor-id`,
|
|
118
|
+
`--session-id`), and `--kind` values carry no `_record` suffix
|
|
119
|
+
(`--kind evidence`).
|
|
96
120
|
- `canon status` sends the planner to write the brief while `brief check`
|
|
97
121
|
has blockers; the human stop appears only when the brief is clean and
|
|
98
122
|
unapproved.
|
|
@@ -84,7 +84,7 @@ enum, like `lane`.
|
|
|
84
84
|
## Subject pinning
|
|
85
85
|
|
|
86
86
|
Every scenario belongs to a Subject (the durable product unit, see
|
|
87
|
-
`canon-subject`). Write the Subject first, then pin with `--subject`;
|
|
87
|
+
`canon-subject`). Write the Subject first, then pin with `--subject-id`;
|
|
88
88
|
the scenario index carries the tag onto the index record and
|
|
89
89
|
`spec_coverage.scope` filters on it. An unpinned scenario is invisible to
|
|
90
90
|
a scoped gate.
|
|
@@ -94,9 +94,9 @@ a scoped gate.
|
|
|
94
94
|
```bash
|
|
95
95
|
canon feature new checkout.cart --title "Cart" # once per surface
|
|
96
96
|
canon scenario new checkout.cart.01 --title "Adding an item raises the count" \
|
|
97
|
-
--subject checkout-core --lane behavior --case happy # once per behavior
|
|
97
|
+
--subject-id checkout-core --lane behavior --case happy # once per behavior
|
|
98
98
|
canon scenario new checkout.cart.04 --title "Adding an out-of-stock item is refused" \
|
|
99
|
-
--subject checkout-core --lane behavior --case failure # and its failure path
|
|
99
|
+
--subject-id checkout-core --lane behavior --case failure # and its failure path
|
|
100
100
|
```
|
|
101
101
|
|
|
102
102
|
There is no format or sync step after these: the scaffold writes a
|
|
@@ -108,7 +108,7 @@ one of them prints `NOT refreshed`, the corpus has a format violation
|
|
|
108
108
|
duplicate id; `canon inventory sync` (or `canon format <root>`) names it.
|
|
109
109
|
Fix it and the next command indexes the files (see `canon-inventory`).
|
|
110
110
|
|
|
111
|
-
- `--actor <id>` (or `CANON_ACTOR` in the environment) puts YOUR id on the
|
|
111
|
+
- `--actor-id <id>` (or `CANON_ACTOR` in the environment) puts YOUR id on the
|
|
112
112
|
provenance line. Default is `canon-scaffold`.
|
|
113
113
|
- **Keep the `# canon:` lines when you fill in the scenario.** Dropping one
|
|
114
114
|
makes `canon inventory sync` abort on `missing-provenance`; restore it
|
|
@@ -146,7 +146,7 @@ Fix it and the next command indexes the files (see `canon-inventory`).
|
|
|
146
146
|
- **A Feature title that is a theme.** "Fidelity of the original
|
|
147
147
|
experience" names no surface. Name the screen.
|
|
148
148
|
- **Hand-stamped provenance.** Eighty lines with one timestamp and the
|
|
149
|
-
same `agent_id`. Use `canon scenario new --actor`.
|
|
149
|
+
same `agent_id`. Use `canon scenario new --actor-id`.
|
|
150
150
|
- **Coverage by splitting.** One behavior as two scenarios so two rows go
|
|
151
151
|
green. One behavior, one id.
|
|
152
152
|
- **Runner tags as classification.** `@design`, `@p2`, `@wip` are
|
|
@@ -226,9 +226,13 @@ from shipping until every one of its evidenced scenarios has evidence
|
|
|
226
226
|
recorded after the close or the approval. The refusal names the scenarios
|
|
227
227
|
and the batch command: `canon evidence add --project-id <root-id>
|
|
228
228
|
--all-scenarios --report <format>:<path> --kind test-run --role
|
|
229
|
-
implementer --ref "<test command>" …`.
|
|
230
|
-
|
|
231
|
-
|
|
229
|
+
implementer --ref "<test command>" …`. Reviews pin what the evidence says
|
|
230
|
+
(its verdict and the identity of its checks), not which record it is, so
|
|
231
|
+
recording the suite again with the same verdicts from the same cases leaves
|
|
232
|
+
every scenario review current. A chore instead needs one change-level
|
|
233
|
+
review of the chore itself (`canon review add --change-id <slug> …`) before
|
|
234
|
+
`--to shipped`; a changed verdict or case set stales that scenario's
|
|
235
|
+
review as before (and a new `divergence accept`, where the gap remains).
|
|
232
236
|
|
|
233
237
|
### Audit records (`--kind audit`)
|
|
234
238
|
|
|
@@ -263,33 +267,16 @@ canon evidence add --kind audit --project-id root --scenario-id cart.add.01 \
|
|
|
263
267
|
- `canon status` shows an `audits` section (sampled / confirmed / owed /
|
|
264
268
|
refuted) and suggests `canon evidence add --kind audit …` in `next:`.
|
|
265
269
|
|
|
266
|
-
###
|
|
267
|
-
|
|
268
|
-
`canon evidence add`
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
`
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
screenshots. A carried file whose blob the artifact store does not
|
|
277
|
-
hold intact is refused (exit `2`, `EVIDENCE_CARRY_BLOB_MISSING`, nothing
|
|
278
|
-
written): re-bind it, run `canon evidence vault`, or drop it. The audit
|
|
279
|
-
fold (`--kind audit`) does not carry: an audit that leaves out a file
|
|
280
|
-
the latest audit bound is refused (exit `2`) unless it names the file
|
|
281
|
-
again or drops it with `--drop-attachment`, so a counterexample never
|
|
282
|
-
rides along into a confirming audit.
|
|
283
|
-
|
|
284
|
-
The only way to lose a binding is `--drop-attachment <path>`
|
|
285
|
-
(repeatable) with one single-line `--drop-reason <text>`, recorded as
|
|
286
|
-
`attachment_drop: {paths, reason}`. With `--all-scenarios` the drop
|
|
287
|
-
applies to each scenario that binds the path. Refused (`2`): a drop
|
|
288
|
-
without a reason, a reason without a drop, a drop path that no
|
|
289
|
-
scenario of the write leaves out, a multi-line reason, or a drop
|
|
290
|
-
without `--scenario-id`/`--all-scenarios` (task-only evidence has no
|
|
291
|
-
carry or drop). `canon status` lists recorded drops (`dropped bindings
|
|
292
|
-
(N):`; `drops[]` in `--json`).
|
|
270
|
+
### A record binds what its own write names
|
|
271
|
+
|
|
272
|
+
`canon evidence add` binds exactly the files the write names
|
|
273
|
+
(`--artifact`, `--report`, `--report-case`). Nothing is carried forward
|
|
274
|
+
from an earlier record, in a single add or in a suite batch
|
|
275
|
+
(`--all-scenarios`), and no drop is recorded: history keeps the earlier
|
|
276
|
+
records, and a new record is reviewed on its own files. An audit
|
|
277
|
+
(`--kind audit`) replaces a refutation the same way, so a counterexample
|
|
278
|
+
never rides along into a confirming audit. `--drop-attachment` and
|
|
279
|
+
`--drop-reason` no longer exist.
|
|
293
280
|
|
|
294
281
|
### Nothing is attested before the brief is approved (`--override-reason`)
|
|
295
282
|
|
|
@@ -308,7 +295,7 @@ check `canon status` and the ship guard use; see
|
|
|
308
295
|
`--task`:
|
|
309
296
|
|
|
310
297
|
```
|
|
311
|
-
canon evidence add: unreviewed-promotion demo-subject — evidence for world.demo.01: brief not approved — .canon/changes/c-demo/brief.md has no `Approved:` line, so the human has not answered it; once they do, record their words with `canon brief approve --change c-demo --by <the human> --note "<their words>"`
|
|
298
|
+
canon evidence add: unreviewed-promotion demo-subject — evidence for world.demo.01: brief not approved — .canon/changes/c-demo/brief.md has no `Approved:` line, so the human has not answered it; once they do, record their words with `canon brief approve --change-id c-demo --by <the human> --note "<their words>"`
|
|
312
299
|
canon evidence add: refused — stop, report the brief to the human and wait for their answer: nothing is built or attested for a subject before its brief is approved. Record their answer with `canon brief approve --by <the human> --note "<their words>"` only from that answer; to attest anyway, pass --override-reason <text>, which is recorded on the evidence record
|
|
313
300
|
```
|
|
314
301
|
|
|
@@ -319,7 +306,7 @@ openspec/changes/c-ext/proposal.md, which canon reads and never edits, so
|
|
|
319
306
|
its approval lives in that canon file, so the human has not answered it;
|
|
320
307
|
once they do, create .canon/changes/c-ext/brief.md holding a `Source:
|
|
321
308
|
openspec/changes/c-ext/proposal.md` line, then record their words with
|
|
322
|
-
`canon brief approve --change c-ext --by <the human> --note "<their words>"` ``. An `Approved:` line in the
|
|
309
|
+
`canon brief approve --change-id c-ext --by <the human> --note "<their words>"` ``. An `Approved:` line in the
|
|
323
310
|
external `proposal.md` itself is never read.
|
|
324
311
|
|
|
325
312
|
A subject with no brief to answer (no adopted change, or a newest change
|
|
@@ -376,51 +363,43 @@ the stop is at the brief. `canon gate check` does not grade briefs, so a
|
|
|
376
363
|
waived ship brief prints no advisory here; `canon status` keeps showing
|
|
377
364
|
the brief as not approved.
|
|
378
365
|
|
|
379
|
-
### Reviews that predate pinning (
|
|
380
|
-
|
|
381
|
-
With `require_review` on, the reviews in effect (latest per reviewer and
|
|
382
|
-
scenario) that carry no pins were written before 0.15. Each still names
|
|
383
|
-
`pin`, the commit it reviewed, and the gate checks it there (0.16):
|
|
384
|
-
|
|
385
|
-
- **current** — the scenario's digest at that commit (read with `git`,
|
|
386
|
-
normalized exactly as `canon inventory sync` computes
|
|
387
|
-
`scenario_digest`) equals its current digest, and no declared evidence
|
|
388
|
-
record of the scenario is newer than the review. It counts. When the
|
|
389
|
-
scenario lived in another `.feature` file at that commit, the spec
|
|
390
|
-
root's files at that commit are searched for its id, then the files
|
|
391
|
-
under each of the root's `former_roots` (where `canon migrate` moved
|
|
392
|
-
it from, e.g. `specs` for a root now at `.canon/specs`). Only the
|
|
393
|
-
root's own directories are searched, never another root's.
|
|
394
|
-
- **stale** — the scenario changed since the commit, or it has newer
|
|
395
|
-
evidence. It no longer counts, and an in-scope scenario left with no
|
|
396
|
-
other qualifying review is `unreviewed-promotion`:
|
|
397
|
-
|
|
398
|
-
```
|
|
399
|
-
unreviewed-promotion gate.check.15 — stale review by `canon-reviewer-g1` (written before review pinning; checked against commit b556ab7529a332aa0b28d0a0f98bdd62fcbb9a63): it has declared evidence newer than the review; a review counts only while what it saw is current — review it again (`canon review add`)
|
|
400
|
-
```
|
|
401
|
-
|
|
402
|
-
A spec change reads `its spec changed since <pin> (the scenario's
|
|
403
|
-
digest at that commit is not its current one)`.
|
|
404
|
-
- **undecidable** — the pin is not a commit in this clone (a shallow
|
|
405
|
-
clone, rewritten history), or no feature file of the scenario's root
|
|
406
|
-
holds its id at that commit. It keeps counting, as in 0.15: a missing
|
|
407
|
-
commit never becomes a violation.
|
|
366
|
+
### Reviews that predate 0.23 pinning (`canon migrate`)
|
|
408
367
|
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
the
|
|
413
|
-
|
|
368
|
+
A review pins what its scenario's evidence says: the scenario's spec
|
|
369
|
+
digest and, per role, the verdict and the identity of the checks the latest
|
|
370
|
+
evidence bound (`ReviewPins.checks`). Reviews written before 0.23 pin
|
|
371
|
+
something the gate no longer compares: 0.15–0.22 reviews pin evidence
|
|
372
|
+
record files, and reviews written before 0.15 pin only `pin`, the commit
|
|
373
|
+
they reviewed. Such a review does not count until `canon migrate` rewrites
|
|
374
|
+
it; the gate says so:
|
|
414
375
|
|
|
415
376
|
```
|
|
416
|
-
|
|
377
|
+
unreviewed-promotion gate.check.15 — review by `canon-reviewer-g1` predates 0.23 review pinning (it pins evidence records, or nothing): `canon migrate` rewrites it to what the evidence says, and a review that is not migrated does not count
|
|
417
378
|
```
|
|
418
379
|
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
380
|
+
One advisory line counts the scenarios whose review is not migrated.
|
|
381
|
+
`canon migrate` converts each review (and accepted divergence) that was
|
|
382
|
+
current under the rule it was written under, in place, keeping its
|
|
383
|
+
reviewer, actor, session and artifacts; one that was already stale stays
|
|
384
|
+
stale and its scenario needs a new review.
|
|
385
|
+
|
|
386
|
+
- **0.15–0.22 reviews** are current when the spec pin matches and the
|
|
387
|
+
latest evidence record per role is the one the review pinned.
|
|
388
|
+
- **pre-0.15 reviews** are current when the scenario's digest at the pinned
|
|
389
|
+
commit (read with `git`, normalized as `canon inventory sync` computes
|
|
390
|
+
`scenario_digest`) equals its current digest and no declared evidence is
|
|
391
|
+
newer than the review. When the scenario lived in another `.feature`
|
|
392
|
+
file at that commit, the spec root's files at that commit are searched
|
|
393
|
+
for its id, then the files under each of the root's `former_roots`
|
|
394
|
+
(where `canon migrate` moved it from). Only the root's own directories
|
|
395
|
+
are searched, never another root's. A pin that is not a commit in this
|
|
396
|
+
clone, or a scenario no file holds at that commit, is undecidable: the
|
|
397
|
+
review is pinned as the scenario stands (it counted unchecked before
|
|
398
|
+
0.23), and the migration says how many. In CI, check out full history
|
|
399
|
+
(`actions/checkout` with `fetch-depth: 0`) for the job that runs
|
|
400
|
+
`canon migrate` to check them.
|
|
401
|
+
|
|
402
|
+
A review of a scenario no feature file declares any more is left alone.
|
|
424
403
|
|
|
425
404
|
## Experimental: evidence binding (`experimental.evidence_binding`, off by default)
|
|
426
405
|
|
|
@@ -503,15 +482,20 @@ them: `faithful` when every case passed, `divergent` when one failed,
|
|
|
503
482
|
claims the same verdict for every record instead. A case whose id is not
|
|
504
483
|
indexed is named and skipped (`skipped <id> — not an indexed scenario`);
|
|
505
484
|
a report no indexed scenario owns is refused (exit 2). The records share
|
|
506
|
-
the stored report and
|
|
507
|
-
|
|
485
|
+
the stored report, and the output lists each scenario with its verdict on
|
|
486
|
+
its own line. Each record binds the attachments its own cases name in the
|
|
487
|
+
report (a JUnit test case's `[[ATTACHMENT|path]]` lines, which Playwright's
|
|
488
|
+
JUnit reporter writes; a Cucumber JSON embedding), so a test's screenshot
|
|
489
|
+
stays with its scenario. A file the report does not name goes with
|
|
490
|
+
`--artifact <scenario-id>=<path>`; a bare `--artifact <path>` in a batch is
|
|
491
|
+
refused (`EVIDENCE_INVALID_ARGUMENT`). A report attachment that is missing,
|
|
492
|
+
outside the repo or oversized is refused.
|
|
508
493
|
|
|
509
494
|
It is all or nothing: a claimed `faithful` verdict over a failed case,
|
|
510
495
|
a fabrication marker in `--summary`, an unapproved brief for any
|
|
511
|
-
scenario's subject
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
`--drop-attachment` applies per scenario. `--scenario-id`, `--task`,
|
|
496
|
+
scenario's subject refuses the whole batch, and nothing is written. Each
|
|
497
|
+
record binds only what the batch names (nothing is carried forward).
|
|
498
|
+
`--scenario-id`, `--task`,
|
|
515
499
|
`--report-case`, `--kind audit` and the approval flags name one subject
|
|
516
500
|
and are refused with `--all-scenarios`.
|
|
517
501
|
|
|
@@ -522,10 +506,10 @@ in the report, so a `faithful` verdict needs all of them to pass.
|
|
|
522
506
|
|
|
523
507
|
When new evidence (one record or a batch) supersedes the evidence a
|
|
524
508
|
current pinned review saw, `canon evidence add` prints one line per
|
|
525
|
-
review it makes stale, with the command that re-pins it:
|
|
509
|
+
review it makes stale (its verdict or checks differ from what the review saw), with the command that re-pins it:
|
|
526
510
|
|
|
527
511
|
```
|
|
528
|
-
canon evidence add: review by `reviewer-2` of cart.add.01 is now stale (
|
|
512
|
+
canon evidence add: review by `reviewer-2` of cart.add.01 is now stale (this record's verdict or checks differ from what it saw); re-pin it: canon review add --project-id root --scenario-id cart.add.01 --reviewer reviewer-2 --actor-id reviewer-2 --role reviewer --pin <HEAD sha> --original-spec-ref specs/kind=feature/area=cart/add.feature
|
|
529
513
|
```
|
|
530
514
|
|
|
531
515
|
The decision is the gate's own (the same stale-review rule `canon gate
|
|
@@ -545,6 +529,16 @@ again. The directory is tracked in git (`canon init` never ignores it):
|
|
|
545
529
|
commit it with the ledger. A file larger than 25 MiB is refused (exit 2);
|
|
546
530
|
raise the limit with `--max-artifact-mib <N>`.
|
|
547
531
|
|
|
532
|
+
Where those bytes live is `canon.yaml`'s `artifact_store: { backend: local |
|
|
533
|
+
<s3 tier>, prefix: artifacts/ }` (default `local`: the directory above,
|
|
534
|
+
git-tracked, no credentials). With an object-store tier, `evidence add`,
|
|
535
|
+
`review add` and `evidence vault` upload each blob under
|
|
536
|
+
`<prefix>sha256/<digest>` and keep nothing locally. `canon gate check` and
|
|
537
|
+
`canon artifacts verify [--full]` ask the store (a HEAD per digest; `--full`
|
|
538
|
+
downloads and re-hashes) and refuse, never pass, when it cannot be reached.
|
|
539
|
+
`canon artifacts migrate --to <tier> [--dry-run]` moves an existing local
|
|
540
|
+
store; it is the repo owner's decision. See `canon-storage`.
|
|
541
|
+
|
|
548
542
|
`canon gate check` re-checks every file bound to a LATEST evidence record
|
|
549
543
|
(the latest per task and per `(project_id, scenario_id)`):
|
|
550
544
|
|
|
@@ -575,8 +569,7 @@ re-attesting is the fix.
|
|
|
575
569
|
Files bound to a review (`canon review add --artifact`, see
|
|
576
570
|
`canon-review`) are checked by the same rule for every CURRENT in-effect
|
|
577
571
|
review: the latest per `(project_id, scenario_id, reviewer)`, less any
|
|
578
|
-
that went stale against its scenario (its spec or evidence pin moved
|
|
579
|
-
a review from before pinning is stale at its commit). A stale review is
|
|
572
|
+
that went stale against its scenario (its spec or evidence pin moved). A stale review is
|
|
580
573
|
superseded — the gate reads the newer review, or reports the missing
|
|
581
574
|
one as `unreviewed-promotion` — so its files are not re-checked, as a
|
|
582
575
|
superseded evidence record's are not. A missing blob whose working-tree
|
|
@@ -715,13 +708,12 @@ staged. A record another run staged is not touched: only `gate promote`
|
|
|
715
708
|
commits everything staged. It stays safe to run anyway: with nothing
|
|
716
709
|
staged it prints
|
|
717
710
|
`nothing staged` and exits `0`. `divergence add` stages for `canon
|
|
718
|
-
|
|
719
|
-
other write — `review add`, `
|
|
720
|
-
resolve|defer`, `subject new|adopt|status`, `change new`, `scenario new`, `feature new`,
|
|
711
|
+
gate promote` and names it, under the same `write:` rule. Every
|
|
712
|
+
other write — `review add`, `subject new|adopt|status`, `change new`, `scenario new`, `feature new`,
|
|
721
713
|
`gate task` (it edits the plan document and re-ingests it)
|
|
722
714
|
— is final when it returns, and its success line ends `— written
|
|
723
715
|
directly; nothing to promote`. Full promote-on-write contract:
|
|
724
|
-
`canon-storage` (Staged writes and promotion).
|
|
716
|
+
`canon-storage` (Staged writes and promotion). `divergence resolve|defer|accept` stage and promote their own record in the same run and end `— staged and promoted in this run; nothing to promote`.
|
|
725
717
|
|
|
726
718
|
## `canon gate install-hooks [--repo] [--event] [--matcher] [--command] [--timeout]`
|
|
727
719
|
|
|
@@ -179,7 +179,7 @@ record.
|
|
|
179
179
|
An operator-requested rollback is a distinct authenticated action:
|
|
180
180
|
|
|
181
181
|
```bash
|
|
182
|
-
canon learn rollback <strategy_id> --reason "<reason>" --actor alice \
|
|
182
|
+
canon learn rollback <strategy_id> --reason "<reason>" --actor-id alice \
|
|
183
183
|
--signature-file rollback.sig --approved-at 2026-10-02T12:00:00Z \
|
|
184
184
|
[--contradicting-trajectory-id <trajectory_id>] --repo .
|
|
185
185
|
```
|
|
@@ -142,6 +142,8 @@ canon review add --project-id <root-id> --scenario-id <id> [--scenario-id <id>].
|
|
|
142
142
|
[--outcome signed|indeterminate|unavailable] \
|
|
143
143
|
[--reviewer-provider <p>] [--reviewer-model <m>] [--reviewer-harness <h>] \
|
|
144
144
|
[--artifact <path>]... [--max-artifact-mib <MiB>]
|
|
145
|
+
canon review add --project-id <root> --change-id <slug> --reviewer <who> --pin <sha-or-ref> \
|
|
146
|
+
--role reviewer (--upstream-ref <ref> | --original-spec-ref <ref>) … # a chore, below
|
|
145
147
|
```
|
|
146
148
|
|
|
147
149
|
One attributed Review record per scenario, joined on
|
|
@@ -167,14 +169,30 @@ that is not a scenario id, refuses the call (exit 2) and writes no review.
|
|
|
167
169
|
This is also the batch re-pin after `canon evidence add` reports reviews
|
|
168
170
|
it made stale (see `canon-gate`).
|
|
169
171
|
|
|
172
|
+
### A chore is reviewed as a change (`--change-id`, 0.23)
|
|
173
|
+
|
|
174
|
+
A chore adds no scenarios, and recording the suite again after it leaves
|
|
175
|
+
every scenario review current (a review pins the evidence's verdict and
|
|
176
|
+
checks, so the same verdicts from the same cases change nothing). What the
|
|
177
|
+
chore needs instead is one change-level review: the reviewer signs its
|
|
178
|
+
invariants and checks against the diff with `canon review add --change-id
|
|
179
|
+
<slug> …`, in place of `--scenario-id`. The review pins the chore's brief
|
|
180
|
+
(its digest, less the `Approved:` line), so an edit to the brief stales it,
|
|
181
|
+
and `--to shipped` refuses a chore with no signed, current change-level
|
|
182
|
+
review (`spec_coverage.require_review.distinct_actor` asks it of a reviewer
|
|
183
|
+
and session that did not attest the subject's evidence). `--change-id` takes a
|
|
184
|
+
chore's slug and refuses a feature change.
|
|
185
|
+
|
|
170
186
|
### What the reviewer looked at (artifacts)
|
|
171
187
|
|
|
172
188
|
`--artifact <path>` (repeatable) binds a file the reviewer looked at — a
|
|
173
189
|
screenshot, a recording, a trace — exactly as `canon evidence add
|
|
174
190
|
--artifact` binds one: the path must resolve to a file inside the
|
|
175
191
|
repository, a file over 25 MiB is refused unless `--max-artifact-mib <N>`
|
|
176
|
-
raises the limit, and the bytes are
|
|
177
|
-
`.canon/artifacts/sha256/<hex>`
|
|
192
|
+
raises the limit, and the bytes are stored by digest (in
|
|
193
|
+
`.canon/artifacts/sha256/<hex>` unless `canon.yaml`'s `artifact_store:` names an
|
|
194
|
+
object store, where they are uploaded and nothing is kept locally; commit
|
|
195
|
+
the local store with the ledger) before the
|
|
178
196
|
review is written. Any refusal exits 2 and writes and stores nothing. The
|
|
179
197
|
review records each distinct file once, in the order given:
|
|
180
198
|
|
|
@@ -225,12 +243,16 @@ reviewer never types them:
|
|
|
225
243
|
`scenario_digest`; then the review pins its `source_digest` (the
|
|
226
244
|
sha256 of the whole `.feature` file) and records
|
|
227
245
|
`scenario_digest_scope: file`;
|
|
228
|
-
- `
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
246
|
+
- `checks` — the scenario's latest evidence per role, each as `{role,
|
|
247
|
+
verdict, checks}`: its verdict and the identity of what it checked (the
|
|
248
|
+
report format and the matched case names, as `junit:<case>`, or the
|
|
249
|
+
artifact digests). It names no evidence record, so recording the same
|
|
250
|
+
verdict from the same checks again leaves the review current. A scenario
|
|
251
|
+
with no evidence yet is pinned with an empty list.
|
|
252
|
+
|
|
253
|
+
It prints `canon review add: pinned <id> at scenario digest <12 hex> and the
|
|
254
|
+
verdict and checks of its <n> role(s) of evidence; …` (`at source digest` for
|
|
255
|
+
a file pin). A
|
|
234
256
|
scenario with no record (never synced) is refused with exit 2 and nothing
|
|
235
257
|
is written: there is nothing to pin.
|
|
236
258
|
|
|
@@ -308,44 +330,25 @@ The scenario is reviewed when at least one of those latest reviews:
|
|
|
308
330
|
|
|
309
331
|
1. is `signed`;
|
|
310
332
|
2. still matches its pins — the scenario record's current
|
|
311
|
-
`source_digest
|
|
312
|
-
|
|
313
|
-
|
|
333
|
+
`scenario_digest` (or `source_digest` for a file pin), and the verdict and
|
|
334
|
+
checks of its latest evidence per role — when it has pins; reviews
|
|
335
|
+
written before 0.23 count only after `canon migrate` (below);
|
|
314
336
|
3. passes `distinct_actor`, when that is on.
|
|
315
337
|
|
|
316
|
-
### Reviews written before pinning
|
|
317
|
-
|
|
318
|
-
A review written before 0.
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
stale pinned review;
|
|
331
|
-
- **undecidable** when the pin is not a commit in this clone (shallow
|
|
332
|
-
clone, rewritten history) or the scenario id is absent at it: it keeps
|
|
333
|
-
counting, as in 0.15. A missing commit never becomes a violation.
|
|
334
|
-
|
|
335
|
-
`canon gate check` prints one advisory line with the split (exit code
|
|
336
|
-
unchanged by the line itself). A stale one whose scenario already has
|
|
337
|
-
another qualifying review is counted as superseded and not named:
|
|
338
|
-
|
|
339
|
-
```
|
|
340
|
-
advisory: 338 review(s) predate review pinning (0.15) and were checked against the commit each one pinned: 318 current, 12 stale, 8 undecidable; 7 stale one(s) superseded by a current review of the same scenario, so nothing is owed for them; a stale one with no current review in its place no longer counts — review its scenario again (`canon review add`): platformer.movement.01, platformer.movement.02, …; an undecidable one still counts unchecked, because its pinned commit or the scenario at it is not in this clone — check out full history (`fetch-depth: 0` in CI) to check it
|
|
341
|
-
```
|
|
342
|
-
|
|
343
|
-
`canon status` prints the same line as a warning, and `canon subject
|
|
344
|
-
status` prints it on stderr for the subject's own scenarios; all three
|
|
345
|
-
make the same decision. Re-review exactly the scenarios the line names:
|
|
346
|
-
the new review pins what it saw and replaces the reviewer's legacy one.
|
|
347
|
-
In CI, check out full history (`fetch-depth: 0`) for the job that runs
|
|
348
|
-
the gate, or every legacy review is undecidable.
|
|
338
|
+
### Reviews written before 0.23 pinning
|
|
339
|
+
|
|
340
|
+
A review written before 0.23 pins something the gate no longer compares: a
|
|
341
|
+
0.15–0.22 review pins evidence record files, and a review written before
|
|
342
|
+
0.15 has no pins, only `pin`, the commit it reviewed. Such a review does not
|
|
343
|
+
count until `canon migrate` rewrites it to what the evidence says (see
|
|
344
|
+
`canon-gate`). A review that was current under the rule it was written under
|
|
345
|
+
becomes a current review; one that was stale stays stale, and its scenario
|
|
346
|
+
needs a new `canon review add`, which pins what it saw and replaces the
|
|
347
|
+
reviewer's earlier one. The gate, `canon status` and `canon subject status`
|
|
348
|
+
print one advisory naming how many scenarios are waiting for `canon
|
|
349
|
+
migrate`. In CI, check out full history (`fetch-depth: 0`) for the job that
|
|
350
|
+
runs `canon migrate`, or a pre-0.15 review whose commit is missing is pinned
|
|
351
|
+
as the scenario stands instead of being checked.
|
|
349
352
|
|
|
350
353
|
### `unreviewed-promotion`
|
|
351
354
|
|
|
@@ -105,6 +105,43 @@ canon query --kind trajectory --json --limit 50 --fields at,kind
|
|
|
105
105
|
canon query --kind trajectory --json --limit 50 --cursor <nextCursor>
|
|
106
106
|
```
|
|
107
107
|
|
|
108
|
+
## The ledger is latest-only; the archive holds history
|
|
109
|
+
|
|
110
|
+
The `local` rung's directory (`.canon/ledger`) holds the current record of
|
|
111
|
+
each key. Appending a newer version (evidence for a scenario and role, a
|
|
112
|
+
closed finding, a rewritten scenario, a subject's next status) moves the
|
|
113
|
+
versions it supersedes to `.canon/archive`: same `kind=<kind>/…` layout and
|
|
114
|
+
file names, tracked in git. `status`, the gate, the guards and `canon query`
|
|
115
|
+
read the ledger only.
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
canon query --kind evidence --history # every version, archive included
|
|
119
|
+
canon ledger compact --dry-run # report; writes nothing
|
|
120
|
+
canon ledger compact # pre-0.23 ledger or interrupted move; idempotent; `canon migrate` runs it
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
A crash between the write and the move leaves a readable ledger that the next
|
|
124
|
+
append or `canon ledger compact` finishes. The archive is local, not routed
|
|
125
|
+
through `tiers:`; a `tiers.local.root` whose directory is not named `ledger`
|
|
126
|
+
has no archive and keeps every version.
|
|
127
|
+
|
|
128
|
+
## Where artifact bytes live (`artifact_store:`)
|
|
129
|
+
|
|
130
|
+
```yaml
|
|
131
|
+
artifact_store: { backend: local } # default
|
|
132
|
+
# artifact_store: { backend: cold, prefix: artifacts/ } # an `s3` tier of `tiers:`
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
`local` is `.canon/artifacts/sha256/`, git-tracked and credential-free. An
|
|
136
|
+
object-store tier gets `evidence add`, `review add` and `evidence vault`
|
|
137
|
+
uploads under `<prefix>sha256/<digest>` and keeps nothing locally. The gate
|
|
138
|
+
and `canon artifacts verify [--full]` ask the store and refuse, never pass,
|
|
139
|
+
when it cannot be reached. `canon artifacts migrate --to <tier> [--dry-run]`
|
|
140
|
+
uploads, reads back and verifies every local blob, then writes the
|
|
141
|
+
`artifact_store:` block, removes the local copies and ignores
|
|
142
|
+
`.canon/artifacts/`; commit the removals, `.gitignore` and `canon.yaml`. It is
|
|
143
|
+
resumable and a second run finds nothing to do.
|
|
144
|
+
|
|
108
145
|
## What `canon report` does and doesn't read
|
|
109
146
|
|
|
110
147
|
canon exposes layered `stg_`/`int_`/`mart_` query views; `canon report`
|
|
@@ -173,9 +210,11 @@ record bodies.
|
|
|
173
210
|
## Staged writes and promotion
|
|
174
211
|
|
|
175
212
|
Four writes stage their record instead of committing it: `canon evidence
|
|
176
|
-
add`, `canon finding add` and `canon
|
|
177
|
-
`.canon/ledger/_staging/`,
|
|
178
|
-
|
|
213
|
+
add`, `canon finding add`, `canon finding close` and `canon divergence add`
|
|
214
|
+
(under `.canon/ledger/_staging/`, one staging shape for every kind,
|
|
215
|
+
committed by `canon gate promote`; `canon divergence promote` commits only
|
|
216
|
+
the staged divergences). `canon divergence resolve|defer|accept` stage and
|
|
217
|
+
promote their own record in the same run. `canon.yaml`'s
|
|
179
218
|
`write:` section decides whether they promote in the same run:
|
|
180
219
|
|
|
181
220
|
```yaml
|
|
@@ -184,9 +223,8 @@ write:
|
|
|
184
223
|
```
|
|
185
224
|
|
|
186
225
|
- `auto` — the command stages, then promotes that one record through the
|
|
187
|
-
same validation and assignment `canon gate promote`
|
|
188
|
-
|
|
189
|
-
only `canon gate promote` / `canon divergence promote` commit
|
|
226
|
+
same validation and assignment `canon gate promote` applies. Records
|
|
227
|
+
other runs staged stay staged; only `canon gate promote` commits
|
|
190
228
|
everything staged. The one success line names the committed record
|
|
191
229
|
(`wrote kind=…/….json`; promotion renames it, so the staged file is not
|
|
192
230
|
named) and ends `— staged and promoted in this run; nothing to
|
|
@@ -69,12 +69,12 @@ canon subject new subject-domain-loop \
|
|
|
69
69
|
### 2. Retrieve subject-scoped strategy memory BEFORE working
|
|
70
70
|
|
|
71
71
|
```bash
|
|
72
|
-
canon retrieve --role dev --domain dev --subject subject-domain-loop --k 5
|
|
72
|
+
canon retrieve --role dev --domain dev --subject-id subject-domain-loop --k 5
|
|
73
73
|
```
|
|
74
74
|
|
|
75
|
-
- `--regime <key>` XOR the derived pair `--domain <d> [--subject <id>]`:
|
|
75
|
+
- `--regime <key>` XOR the derived pair `--domain <d> [--subject-id <id>]`:
|
|
76
76
|
give the full regime key directly, OR let retrieval derive it. Never both.
|
|
77
|
-
- With `--domain`/`--subject`, retrieval tries `<domain>-<subject_id>`
|
|
77
|
+
- With `--domain`/`--subject-id`, retrieval tries `<domain>-<subject_id>`
|
|
78
78
|
first, then `<domain>` — a subject's own lessons win, falling back to
|
|
79
79
|
the domain's shared memory when the subject is new.
|
|
80
80
|
- Fail-soft: an empty/missing store degrades to an empty guidance list,
|
|
@@ -82,7 +82,7 @@ canon retrieve --role dev --domain dev --subject subject-domain-loop --k 5
|
|
|
82
82
|
|
|
83
83
|
### 3. Work → pin every artifact to `subject_id`
|
|
84
84
|
|
|
85
|
-
- **Scenarios**: `canon scenario new <tag> --subject <subject-id>` emits
|
|
85
|
+
- **Scenarios**: `canon scenario new <tag> --subject-id <subject-id>` emits
|
|
86
86
|
the `@subject:<subject-id>` tag; the scenario index maps the tag onto
|
|
87
87
|
the scenario's `subject_id` the next time a command reads it (no sync
|
|
88
88
|
step; see `canon-inventory`). A malformed/absent tag leaves it unset
|
|
@@ -172,7 +172,7 @@ a policy. The refusal is an `unreviewed-promotion` naming the subject, and
|
|
|
172
172
|
says what to do:
|
|
173
173
|
|
|
174
174
|
```
|
|
175
|
-
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>"`
|
|
175
|
+
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-id c-demo --by <the human> --note "<their words>"`
|
|
176
176
|
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
|
|
177
177
|
```
|
|
178
178
|
|
|
@@ -181,7 +181,7 @@ For an external change with no canon brief yet, the reason reads
|
|
|
181
181
|
the external plan <proposal>, which canon reads and never edits, so its
|
|
182
182
|
approval lives in that canon file`` and the agent will ``create
|
|
183
183
|
.canon/changes/<c>/brief.md holding a `Source: <proposal>` line, then
|
|
184
|
-
record their words with `canon brief approve --change <c> --by <the human> --note "<their words>"` ``.
|
|
184
|
+
record their words with `canon brief approve --change-id <c> --by <the human> --note "<their words>"` ``.
|
|
185
185
|
|
|
186
186
|
Never record the approval without the human's answer. When there is no brief to
|
|
187
187
|
answer (no adopted change, or a newest change with neither a canon
|
|
@@ -306,7 +306,7 @@ Planning docs written outside canon become change/task records via
|
|
|
306
306
|
```bash
|
|
307
307
|
canon ingest plans --repo . # import → change/task rows
|
|
308
308
|
canon subject adopt subject-domain-loop-plan \
|
|
309
|
-
--subject subject-domain-loop # link change → subject
|
|
309
|
+
--subject-id subject-domain-loop # link change → subject
|
|
310
310
|
```
|
|
311
311
|
|
|
312
312
|
A change made with `canon change new` (below) is recorded and adopted
|
|
@@ -343,7 +343,7 @@ The two records cannot be written atomically, so the subject is written
|
|
|
343
343
|
first: a half-done adoption still has that change's open blockers
|
|
344
344
|
counted. If the change write then fails, `adopt` exits `2`, names the
|
|
345
345
|
subject that now lists the change, and prints `canon subject adopt
|
|
346
|
-
<change_id> --subject <id>` to complete it.
|
|
346
|
+
<change_id> --subject-id <id>` to complete it.
|
|
347
347
|
|
|
348
348
|
### Starting a new change: `canon change new`
|
|
349
349
|
|
|
@@ -351,7 +351,7 @@ For a change that does not exist yet, one command writes it and records
|
|
|
351
351
|
it adopted:
|
|
352
352
|
|
|
353
353
|
```bash
|
|
354
|
-
canon change new add-login --subject auth --title "Add login"
|
|
354
|
+
canon change new add-login --subject-id auth --title "Add login"
|
|
355
355
|
```
|
|
356
356
|
|
|
357
357
|
```
|
|
@@ -387,7 +387,7 @@ canon change new: recorded change `add-login` adopted into subject `auth` — wr
|
|
|
387
387
|
cannot be written atomically. If the change record fails after the
|
|
388
388
|
subject record was written, the command exits `2`, keeps the change
|
|
389
389
|
dir and that one subject record, and prints the repair, `canon ingest
|
|
390
|
-
plans && canon subject adopt <slug> --subject <id>`, which completes
|
|
390
|
+
plans && canon subject adopt <slug> --subject-id <id>`, which completes
|
|
391
391
|
the link and is safe to rerun.
|
|
392
392
|
- **Refuses** with exit `2`, writing nothing, when the subject does not
|
|
393
393
|
exist, or when the slug already has a `.canon/changes/<slug>` dir
|
|
@@ -411,12 +411,12 @@ it, so the external source keeps the change, its summary and its tasks.
|
|
|
411
411
|
### The brief: `canon brief`, `canon brief check`, `canon brief approve`
|
|
412
412
|
|
|
413
413
|
```bash
|
|
414
|
-
canon brief [--change <slug>] # state and the next one to three steps; never edits
|
|
415
|
-
canon brief check [--change <slug>] [--json] # findings; exit 0 clean, 2 blockers, 1 error
|
|
416
|
-
canon brief approve --change <slug> --by <the human> --note "<their words>"
|
|
414
|
+
canon brief [--change-id <slug>] # state and the next one to three steps; never edits
|
|
415
|
+
canon brief check [--change-id <slug>] [--json] # findings; exit 0 clean, 2 blockers, 1 error
|
|
416
|
+
canon brief approve --change-id <slug> --by <the human> --note "<their words>"
|
|
417
417
|
```
|
|
418
418
|
|
|
419
|
-
Without `--change`, the repo's only live change is read. The brief is the
|
|
419
|
+
Without `--change-id`, the repo's only live change is read. The brief is the
|
|
420
420
|
PRD `canon change new` writes; sections are found by their
|
|
421
421
|
`<!-- canon:section <key> role=… -->` marker, never by heading text. A
|
|
422
422
|
brief without markers (written before 0.19) checks as `brief.legacy`, an
|
|
@@ -453,9 +453,10 @@ built or kept, never what it does. Its brief is intent, category,
|
|
|
453
453
|
invariants, checks, out of scope; it adds no scenarios, and its evidence
|
|
454
454
|
is the subject's suite run again at the chore commit with
|
|
455
455
|
`canon evidence add --all-scenarios --report <kind>:<path> …`. That
|
|
456
|
-
supersedes the old evidence
|
|
457
|
-
|
|
458
|
-
change
|
|
456
|
+
supersedes the old evidence; the pinned reviews stay current while the
|
|
457
|
+
verdicts and checks are the same, so the chore itself is reviewed once with
|
|
458
|
+
`canon review add --change-id <slug> …` before it ships. Work that changes
|
|
459
|
+
behavior is a feature change, never a chore.
|
|
459
460
|
|
|
460
461
|
## Where are we: `canon status`
|
|
461
462
|
|
|
@@ -488,8 +489,7 @@ uses, so status and the gate cannot disagree about a gap:
|
|
|
488
489
|
stale or whose latest reviews are all `indeterminate`/`unavailable`
|
|
489
490
|
(see `canon-review`), and the `canon review add` step names both. With
|
|
490
491
|
`require_review` on, a `warning:` line counts the in-effect reviews that
|
|
491
|
-
predate review pinning
|
|
492
|
-
current, stale (named) and undecidable.
|
|
492
|
+
predate 0.23 review pinning and wait for `canon migrate`.
|
|
493
493
|
- **Brief** (0.16, 0.17, 0.19) — for each `building`, `verifying` or `shipped`
|
|
494
494
|
subject, and each `proposed` subject whose newest adopted change has a
|
|
495
495
|
brief (a canon `brief.md`, or an external `proposal.md`), a `brief:`
|
|
@@ -545,8 +545,6 @@ uses, so status and the gate cannot disagree about a gap:
|
|
|
545
545
|
refuted audit adds a `next:` step suggesting `canon evidence add
|
|
546
546
|
--kind audit …` (recorded from an external evaluator; canon never runs
|
|
547
547
|
one).
|
|
548
|
-
- **Dropped bindings** — `dropped bindings (N):` lists every recorded
|
|
549
|
-
`--drop-attachment` (scenario, paths, reason); `drops[]` in `--json`.
|
|
550
548
|
- **Brief overrides** — a `warning:` line per evidence record attested
|
|
551
549
|
with `canon evidence add --override-reason` while its subject's brief
|
|
552
550
|
was not approved: what it attests, who, when, the subject and the
|
|
@@ -562,7 +560,7 @@ uses, so status and the gate cannot disagree about a gap:
|
|
|
562
560
|
first, then building, specced, proposed, shipped: an unapproved brief
|
|
563
561
|
(building and later, and proposed once its change has a brief)
|
|
564
562
|
→ the human step below, which is then the subject's
|
|
565
|
-
only step; no scenario → `canon scenario new … --subject <id>` (and, when no
|
|
563
|
+
only step; no scenario → `canon scenario new … --subject-id <id>` (and, when no
|
|
566
564
|
`.feature` file holds a scenario yet, `canon feature new` first, unless
|
|
567
565
|
every subject waits on its brief); a missing case → `canon scenario new
|
|
568
566
|
<surface>.<next nn> … --case <case>`; (building and later) an
|
|
@@ -574,12 +572,12 @@ uses, so status and the gate cannot disagree about a gap:
|
|
|
574
572
|
Last, unowned scenarios → `canon status`, after tagging them in their
|
|
575
573
|
`.feature` files (status re-indexes the edit; no sync step). With more
|
|
576
574
|
than one `specs.roots[]` entry, every suggested `canon scenario new`
|
|
577
|
-
carries `--project`: the owning root for a missing case, `<root-id>`
|
|
575
|
+
carries `--project-id`: the owning root for a missing case, `<root-id>`
|
|
578
576
|
(the reason lists the roots) for a subject that owns no scenario yet.
|
|
579
577
|
- **The one human step.** Every step is the agent's to run except one,
|
|
580
578
|
and it comes only once the brief is clean: while `canon brief check
|
|
581
|
-
--change <slug>` has blockers, the subject gets the planner step
|
|
582
|
-
`write the brief: canon brief check --change <slug> lists N blockers`
|
|
579
|
+
--change-id <slug>` has blockers, the subject gets the planner step
|
|
580
|
+
`write the brief: canon brief check --change-id <slug> lists N blockers`
|
|
583
581
|
(`role: planner`) instead. A subject whose brief is enforced (its
|
|
584
582
|
newest change's canon
|
|
585
583
|
`brief.md` has no `Approved:` line, or it has no canon `brief.md` yet
|
|
@@ -591,10 +589,10 @@ uses, so status and the gate cannot disagree about a gap:
|
|
|
591
589
|
and the approval is never assumed: `canon evidence add` refuses <id>'s
|
|
592
590
|
scenarios and tasks until it is recorded; once the human answers,
|
|
593
591
|
<record> (one call records the brief and the policy approval)``, where
|
|
594
|
-
`<record>` is ``record their words with `canon brief approve --change
|
|
592
|
+
`<record>` is ``record their words with `canon brief approve --change-id
|
|
595
593
|
<c> --by <the human> --note "<their words>"` ``, or, for an external
|
|
596
594
|
change, ``create <brief> holding a `Source: <proposal>` line, then
|
|
597
|
-
record their words with `canon brief approve --change <c> --by <the
|
|
595
|
+
record their words with `canon brief approve --change-id <c> --by <the
|
|
598
596
|
human> --note "<their words>"` ``; for a brief edited after its
|
|
599
597
|
approval, ``re-approve with `canon brief approve …` ``.
|
|
600
598
|
It is the subject's only step, whatever its status: nothing suggests
|
|
@@ -640,7 +638,7 @@ Status fails closed rather than suggest progress the gate would refuse:
|
|
|
640
638
|
],
|
|
641
639
|
"unowned": ["cart.promo.01"],
|
|
642
640
|
"next": [
|
|
643
|
-
{ "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" },
|
|
641
|
+
{ "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-id c-promo --by <the human> --note \"<their words>\"` (one call records the brief and the policy approval)", "actor": "human" },
|
|
644
642
|
{ "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" },
|
|
645
643
|
{ "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" },
|
|
646
644
|
{ "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" }
|
|
@@ -14,13 +14,16 @@ a `specs.roots[]` entry in `canon.yaml` (`root` by default).
|
|
|
14
14
|
```bash
|
|
15
15
|
canon status --as implementer --json # once, at the start
|
|
16
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
|
|
17
|
+
canon scenario new <area>.<surface>.<nn> --subject-id <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-id","flow","case",…} per line
|
|
19
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
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
21
|
canon evidence add --project-id <root-id> --scenario-id <id> --verdict divergent --artifact <path> --kind test-run --role implementer --ref "<the external limit, and where it is tracked>" --actor-id <unit> --session-id <session> --as implementer --json # honest divergent evidence
|
|
22
|
+
canon artifacts verify --full --as implementer --json # bound files are in the artifact store (omit --full to trust the store's size and digest metadata); refuses when an object store is unreachable
|
|
22
23
|
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
|
|
23
24
|
canon inventory sync --as implementer --json # only after a write printed NOT refreshed
|
|
25
|
+
canon query --kind <kind> --history --as implementer --json # earlier versions too; without --history only the current record of each key
|
|
26
|
+
canon ledger compact --as implementer --json # only for a pre-0.23 ledger or an interrupted move (add --dry-run to preview)
|
|
24
27
|
canon gate promote --as implementer --json # only with write.promote: manual
|
|
25
28
|
```
|
|
26
29
|
|
|
@@ -52,10 +55,23 @@ After the first `status`, follow `meta.next` of each write; do not call
|
|
|
52
55
|
`--all-scenarios`: one record per indexed scenario owning a case, each with
|
|
53
56
|
the verdict its own cases show; any refusal writes none. For one scenario
|
|
54
57
|
use `--scenario-id <id>` (repeatable) with `--report` or `--artifact`.
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
58
|
+
A record binds exactly what its own write names (`--report`,
|
|
59
|
+
`--artifact`): nothing is carried forward from an earlier record, so name
|
|
60
|
+
every file the record should prove again. canon stores the bytes by digest
|
|
61
|
+
in the artifact store (by default `.canon/artifacts/sha256/`, committed
|
|
62
|
+
with the ledger; an object store when `canon.yaml`'s `artifact_store:` block
|
|
63
|
+
names one, in which case nothing is kept locally) and `canon gate check`
|
|
64
|
+
re-checks them, so the working-tree report may be gitignored. Run
|
|
65
|
+
`canon artifacts verify` to ask the store yourself; it refuses, never
|
|
66
|
+
passes, when an object store cannot be reached. Moving an existing store
|
|
67
|
+
(`canon artifacts migrate`) is the repo owner's call: do not run it.
|
|
68
|
+
For a scenario about what users see, let the test report carry its
|
|
69
|
+
screenshots: Playwright's JUnit reporter attaches, per test case, what a
|
|
70
|
+
test attaches (`testInfo.attach(name, {path})`, or `screenshot: 'on'`), and
|
|
71
|
+
one `evidence add --all-scenarios --report junit:<path>` binds to each
|
|
72
|
+
record only its own tests' files. A file the report does not name goes
|
|
73
|
+
with `--artifact <scenario-id>=<path>`; a bare `--artifact` in a batch is
|
|
74
|
+
refused. An external evaluator's audit is
|
|
59
75
|
`--kind audit`; canon never generates one. Evidence for a task of an
|
|
60
76
|
imported plan (`--task <id>`) must bind the artifact or report that proves
|
|
61
77
|
the task; a canon command is never that proof. When the behavior cannot
|
|
@@ -67,16 +83,21 @@ After the first `status`, follow `meta.next` of each write; do not call
|
|
|
67
83
|
the record is final when `evidence add` returns. Only with
|
|
68
84
|
`write.promote: manual` (or no `write:` key) follow it with
|
|
69
85
|
`gate promote`; `meta.next` names it.
|
|
70
|
-
6. **A
|
|
71
|
-
and the
|
|
72
|
-
|
|
86
|
+
6. **A changed result makes a signed review stale.** A review pins the
|
|
87
|
+
verdict and the checks (report format and matched case names, or artifact
|
|
88
|
+
digests) of the evidence it saw, not which record it was. Recording the
|
|
89
|
+
same verdict from the same checks keeps every review current; a different
|
|
90
|
+
verdict, different checks or an edited scenario stales it. The add prints
|
|
91
|
+
the reviewer and the `canon review add … --pin <sha>` that re-pins it; that
|
|
92
|
+
is the reviewer's call. Do not run it yourself.
|
|
73
93
|
7. **After a fix or a chore, record the whole suite again.** A blocker the
|
|
74
94
|
reviewer closed `fixed`, and an approved chore, are not done until every
|
|
75
95
|
evidenced scenario has evidence newer than the close or the approval:
|
|
76
96
|
run the suite and bind it with `canon evidence add --all-scenarios …`
|
|
77
|
-
(the shipping refusal names the scenarios and this command).
|
|
78
|
-
|
|
79
|
-
by the reviewer once new evidence
|
|
97
|
+
(the shipping refusal names the scenarios and this command). Reviews stay
|
|
98
|
+
current while the verdicts and checks are unchanged; a scenario whose gap
|
|
99
|
+
was accepted is accepted again by the reviewer once new evidence says
|
|
100
|
+
something different.
|
|
80
101
|
|
|
81
102
|
## Retry safety
|
|
82
103
|
|
|
@@ -88,6 +109,8 @@ response, run it again.
|
|
|
88
109
|
|
|
89
110
|
When every scenario has evidence, the review is the reviewer's session, in a
|
|
90
111
|
separate process: do not review your own change. A chore adds no scenarios:
|
|
91
|
-
run the suite
|
|
92
|
-
|
|
93
|
-
|
|
112
|
+
once it is approved, run the suite and bind it with `--all-scenarios`;
|
|
113
|
+
shipping needs that evidence newer than the chore's approval, plus the
|
|
114
|
+
reviewer's one change-level review of the chore (`review add --change-id`).
|
|
115
|
+
Scenario reviews stay current when the verdicts and checks are unchanged.
|
|
116
|
+
Reference: `reference/canon-gate.md`.
|
|
@@ -10,11 +10,14 @@ Every line is complete; fill the `<placeholders>`.
|
|
|
10
10
|
|
|
11
11
|
```bash
|
|
12
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
|
|
13
|
+
canon brief approve --change-id <slug> --by "<the human>" --note "<their words>" --as orchestrator --json
|
|
14
14
|
canon subject status <subject-id> --to <proposed|specced|building|verifying|shipped> --actor-id <unit> --as orchestrator --json
|
|
15
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
16
|
canon policy show --as orchestrator --json # policy digest and its approval: approved, changed, unreadable or unapproved
|
|
17
17
|
canon gate check --as orchestrator --json
|
|
18
|
+
canon migrate --dry-run --as orchestrator --json # on a repo upgraded from an earlier canon: the plan, nothing written
|
|
19
|
+
canon migrate --as orchestrator --json # applies it (see "Upgraded repo")
|
|
20
|
+
canon artifacts verify --as orchestrator --json # the artifact store holds every bound file; refuses when an object store is unreachable
|
|
18
21
|
```
|
|
19
22
|
|
|
20
23
|
## Read the state
|
|
@@ -78,10 +81,27 @@ brief says the human ships. A follow-up on a shipped subject goes back to
|
|
|
78
81
|
change, then `--to shipped` again. A chore on a shipped subject (`change
|
|
79
82
|
new … --kind chore` names the reopen command) is approved first, then
|
|
80
83
|
reopened the same way; shipping again needs the whole suite evidenced after
|
|
81
|
-
the chore's approval (`evidence add --all-scenarios`) and
|
|
82
|
-
|
|
84
|
+
the chore's approval (the implementer's `evidence add --all-scenarios`) and
|
|
85
|
+
one change-level review of the chore from the reviewer (`review add --change-id
|
|
86
|
+
<slug>`): the order is approval, evidence, review. A blocker closed
|
|
87
|
+
`fixed` needs the suite again. The refusal names the scenarios and
|
|
83
88
|
the command; never override them. `canon status` flags a shipped subject
|
|
84
89
|
whose gate went red. A bad `--to` fails with `error.field` `--to` and
|
|
85
90
|
`error.expected` listing the states.
|
|
86
91
|
|
|
92
|
+
## Upgraded repo
|
|
93
|
+
|
|
94
|
+
After a repo moves to a newer canon, run `canon migrate --dry-run`, then
|
|
95
|
+
`canon migrate`. Besides the layout, it compacts the ledger (moves every
|
|
96
|
+
superseded version to `.canon/archive`, as `canon ledger compact` does) and
|
|
97
|
+
re-pins the reviews written before 0.23, which do not count until then (the
|
|
98
|
+
gate, `status` and `subject status` print one advisory naming it). Commit
|
|
99
|
+
what it changes. In CI run it with full history (`fetch-depth: 0`).
|
|
100
|
+
|
|
101
|
+
`canon artifacts migrate --to <tier>` moves the local artifact store to an
|
|
102
|
+
object store and removes the local blobs. It is the repo owner's decision,
|
|
103
|
+
not a step of the loop: never run it yourself. If the repo is large, the
|
|
104
|
+
store is slow or the human has not asked for it, ask the human first, and
|
|
105
|
+
run it only on their answer (`--dry-run` previews it).
|
|
106
|
+
|
|
87
107
|
Reference: `reference/canon-subject.md`.
|
|
@@ -12,17 +12,17 @@ Every line is complete; fill the `<placeholders>`.
|
|
|
12
12
|
```bash
|
|
13
13
|
canon status --as planner --json # once, at the start
|
|
14
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
|
|
15
|
+
canon change new <slug> --subject-id <subject-id> --title "<title>" --as planner --json
|
|
16
|
+
canon change new <slug> --subject-id <subject-id> --kind chore --title "<title>" --as planner --json
|
|
17
|
+
canon subject adopt <slug> --subject-id <other-subject-id> --as planner --json
|
|
18
|
+
canon brief check --change-id <slug> --as planner --json
|
|
19
|
+
canon brief approve --change-id <slug> --by "<the human>" --note "<their words>" --as planner --json
|
|
20
20
|
```
|
|
21
21
|
|
|
22
22
|
After the first `status`, follow `meta.next` of each write. `brief check`
|
|
23
23
|
lists blockers and advisories; bare `canon brief --as planner` names the next
|
|
24
24
|
steps. While the brief has blockers, `status` and `change new`'s `meta.next`
|
|
25
|
-
send you to write it (`write the brief: canon brief check --change <slug>
|
|
25
|
+
send you to write it (`write the brief: canon brief check --change-id <slug>
|
|
26
26
|
lists N blockers`); the human step appears only once the brief is clean and
|
|
27
27
|
unapproved. A policy that no longer matches its approval fails closed
|
|
28
28
|
(`POLICY_CHANGED_SINCE_APPROVAL`): never edit it; the human re-approves.
|
|
@@ -79,7 +79,8 @@ can observe: `change new … --kind chore`. Its brief is short (intent,
|
|
|
79
79
|
category, invariants, checks, out of scope) and approved like any brief, one
|
|
80
80
|
human stop. A chore adds no scenarios: the
|
|
81
81
|
implementer runs the subject's suite again and binds it with
|
|
82
|
-
`canon evidence add --all-scenarios …`, then
|
|
82
|
+
`canon evidence add --all-scenarios …`, then the reviewer records one
|
|
83
|
+
change-level review of the chore (`canon review add --change-id <slug>`). On a
|
|
83
84
|
shipped subject `change new` prints the reopen command for the orchestrator
|
|
84
85
|
(`subject status <id> --to verifying --reason "<why>"`); the subject cannot
|
|
85
86
|
ship again until the suite is evidenced after the chore's approval. Work that
|
|
@@ -10,14 +10,17 @@ Every line is complete; fill the `<placeholders>`. `<root-id>` is the `id` of
|
|
|
10
10
|
a `specs.roots[]` entry in `canon.yaml` (`root` by default).
|
|
11
11
|
|
|
12
12
|
```bash
|
|
13
|
-
canon brief check --change <slug> --as reviewer --json
|
|
13
|
+
canon brief check --change-id <slug> --as reviewer --json
|
|
14
14
|
canon gate check --as reviewer --json # once, before review add
|
|
15
15
|
canon finding add --change-id <slug> --round <n> --seq <n> --severity <blocker|should-fix|note> --reviewer <me> --actor-id <me> --session-id <session> --summary "<defect>" --as reviewer --json
|
|
16
16
|
canon finding list --change-id <slug> --open --as reviewer --json
|
|
17
17
|
canon finding close --change-id <slug> --round <n> --seq <n> --disposition fixed --resolution-sha <sha> --as reviewer --json
|
|
18
18
|
canon review add --project-id <root-id> --scenario-id <id> --scenario-id <id> --reviewer <me> --actor-id <me> --role reviewer --session-id <session> --pin <sha> --upstream-ref <ref> --as reviewer --json
|
|
19
19
|
canon review add --project-id <root-id> --scenario-ids-from - --reviewer <me> --actor-id <me> --role reviewer --session-id <session> --pin <sha> --upstream-ref <ref> --as reviewer --json # ids on stdin, one per line
|
|
20
|
+
canon review add --project-id <root-id> --change-id <chore-slug> --reviewer <me> --actor-id <me> --role reviewer --session-id <session> --pin <sha> --upstream-ref <ref> --as reviewer --json # a chore: one change-level review of its invariants and checks against the diff
|
|
20
21
|
canon divergence accept --project-id <root-id> --scenario-id <id> --reason "<why the gap stays>" --tracking <issue-url-or-ref> [--expiry YYYY-MM-DD] --reviewer <me> --actor-id <me> --session-id <session> --as reviewer --json
|
|
22
|
+
canon artifacts verify --full --as reviewer --json # the files the evidence and reviews bind are in the artifact store; refuses when an object store is unreachable
|
|
23
|
+
canon query --kind evidence --history --as reviewer --json # every version, not only the current record
|
|
21
24
|
```
|
|
22
25
|
|
|
23
26
|
`review add` needs `--upstream-ref <ref>` or `--original-spec-ref <path>`;
|
|
@@ -25,6 +28,14 @@ add `--artifact <path>` (repeatable) for user-visible behavior. A short
|
|
|
25
28
|
`<sha>` is fine. With `write.promote: manual`, follow `finding add` and
|
|
26
29
|
`finding close` with `canon gate promote --as reviewer --json`.
|
|
27
30
|
|
|
31
|
+
A scenario review pins what you saw: the scenario's digest plus the verdict
|
|
32
|
+
and checks (report format and matched case names, or artifact digests) of its
|
|
33
|
+
latest evidence per role, not which record held them. It stays current when
|
|
34
|
+
the implementer records the same verdict from the same checks again, and
|
|
35
|
+
goes stale when the verdict or the checks differ or the scenario changes.
|
|
36
|
+
Reviews written before 0.23 do not count until `canon migrate` re-pins them
|
|
37
|
+
(an advisory names it); that is the orchestrator's step, not yours.
|
|
38
|
+
|
|
28
39
|
Run `gate check` once, before `review add`: its `unreviewed-promotion`
|
|
29
40
|
violations are the subjects and scenarios that need your review. Do not loop
|
|
30
41
|
it. After `review add`, read its `meta.next` instead of calling `gate check`
|
|
@@ -87,9 +98,16 @@ ship guard and stays listed as `accepted-divergence` in `status` and
|
|
|
87
98
|
directly; nothing to promote. canon stores and re-checks attached bytes
|
|
88
99
|
and never compares images.
|
|
89
100
|
- A blocker you close `fixed` stays blocking until the implementer records
|
|
90
|
-
the whole suite again (`evidence add --all-scenarios`);
|
|
91
|
-
|
|
92
|
-
- Fixes you ask for return as a re-review of the same change. When
|
|
93
|
-
evidence
|
|
101
|
+
the whole suite again (`evidence add --all-scenarios`); your pinned review
|
|
102
|
+
stays current when the new evidence says the same, otherwise look again.
|
|
103
|
+
- Fixes you ask for return as a re-review of the same change. When new
|
|
104
|
+
evidence changes a verdict or the checks and so stales your review, look
|
|
105
|
+
again before you re-pin.
|
|
106
|
+
A chore has no scenarios: review it with one change-level review, `canon
|
|
107
|
+
review add --project-id <root-id> --change-id <slug> --reviewer <me> --role
|
|
108
|
+
reviewer --pin <sha> --upstream-ref <ref> …`. It signs the chore's
|
|
109
|
+
invariants and checks against the diff and pins its brief, so editing the
|
|
110
|
+
brief stales it. The chore also needs evidence recorded after its approval
|
|
111
|
+
(the implementer's `--all-scenarios` run) before it ships.
|
|
94
112
|
|
|
95
113
|
Reference: `reference/canon-review.md`.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@journeykit/canon",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.24.0",
|
|
4
4
|
"description": "canon — harness knowledge substrate. JS launcher: platform detection + native binary resolution.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -30,9 +30,9 @@
|
|
|
30
30
|
},
|
|
31
31
|
"dependencies": {},
|
|
32
32
|
"optionalDependencies": {
|
|
33
|
-
"@journeykit/canon-core-darwin-arm64": "0.
|
|
34
|
-
"@journeykit/canon-core-linux-x64": "0.
|
|
35
|
-
"@journeykit/canon-core-win32-x64": "0.
|
|
33
|
+
"@journeykit/canon-core-darwin-arm64": "0.24.0",
|
|
34
|
+
"@journeykit/canon-core-linux-x64": "0.24.0",
|
|
35
|
+
"@journeykit/canon-core-win32-x64": "0.24.0"
|
|
36
36
|
},
|
|
37
37
|
"devDependencies": {
|
|
38
38
|
"@types/node": "^20.0.0",
|