@journeykit/canon 0.17.0 → 0.18.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
CHANGED
|
@@ -36,6 +36,14 @@ a feature, a one-line request included. A human is needed once per scope:
|
|
|
36
36
|
stays in the approved change: add it as tasks in that change's
|
|
37
37
|
`tasks.md`, never with `canon change new`, because a new change is a new
|
|
38
38
|
scope and needs the human's approval.
|
|
39
|
+
- **A follow-up on a shipped subject reopens it.** Reopen it with
|
|
40
|
+
`canon subject status <id> verifying --reason "<why>" --actor-id <unit>`
|
|
41
|
+
(no human inside the approved scope; the reason and your id are
|
|
42
|
+
recorded), add the follow-up as tasks in the approved change's
|
|
43
|
+
`tasks.md`, then its scenario, evidence and an independent review, and
|
|
44
|
+
ship again with `canon subject status <id> shipped`, which runs every
|
|
45
|
+
guard again. `canon status` flags a shipped subject whose gate went red
|
|
46
|
+
and names the reopen.
|
|
39
47
|
|
|
40
48
|
When no human can answer one of the brief's questions, write the question
|
|
41
49
|
into the brief, state the assumption you will build on, and say so in your
|
|
@@ -59,6 +67,9 @@ work inside an approved scope, never to the approval itself.
|
|
|
59
67
|
`canon change new <slug> --subject <id> --title "…"`, and put the brief in
|
|
60
68
|
the `.canon/changes/<slug>/brief.md` it writes. For a new subject or a
|
|
61
69
|
scope change, this is where you stop until the human answers (step 1).
|
|
70
|
+
A change that also serves another subject is adopted there too,
|
|
71
|
+
`canon subject adopt <slug> --subject <other>`: it then belongs to both,
|
|
72
|
+
and its open blockers gate both.
|
|
62
73
|
3. **Scenarios.** `canon feature new <area>.<surface> --title "…"`, then one
|
|
63
74
|
`canon scenario new <area>.<surface>.<nn> --subject <id> --case <case> --title "…"`
|
|
64
75
|
per behavior, with failure paths (`--case failure`) as well as `happy`
|
|
@@ -88,12 +99,20 @@ work inside an approved scope, never to the approval itself.
|
|
|
88
99
|
`canon evidence add --project-id <root-id> --scenario-id <id> --kind test-run --role implementer --ref "<command>" --actor-id <unit> --session-id <session>`,
|
|
89
100
|
with `--report junit:<path>` or `--artifact <path>` when files prove it.
|
|
90
101
|
canon stores those bytes in `.canon/artifacts/sha256/` (commit it with the
|
|
91
|
-
ledger) and `canon gate check` re-checks them
|
|
102
|
+
ledger) and `canon gate check` re-checks them; the bound copy is the one
|
|
103
|
+
kept, so the working-tree report itself may be gitignored. For a scenario about what
|
|
92
104
|
users see, bind the screenshot or recording that shows it
|
|
93
105
|
(`--artifact <path>`). Under `write: { promote: auto }` (what
|
|
94
106
|
`canon init` writes) the record is final when the command returns; only
|
|
95
107
|
in a repo with `write.promote: manual` (or no `write:` key) follow it
|
|
96
108
|
with `canon gate promote`.
|
|
109
|
+
One test run that proves several scenarios is bound once:
|
|
110
|
+
`canon evidence add --project-id <root-id> --all-scenarios --report <junit|cucumber|swift-testing>:<path> --kind test-run --role implementer --ref "<command>" --actor-id <unit> --session-id <session>`
|
|
111
|
+
writes one record per indexed scenario owning a case, each with the
|
|
112
|
+
verdict its own cases show; any refusal writes none. When a new record
|
|
113
|
+
makes a signed review stale, the add prints the reviewer and the
|
|
114
|
+
`canon review add … --pin <sha>` that re-pins it; the reviewer looks
|
|
115
|
+
again before running it.
|
|
97
116
|
An external evaluator's audit is recorded with `canon evidence add --kind
|
|
98
117
|
audit`; canon never generates one.
|
|
99
118
|
Evidence for a plan task (`--task <id>`, before `canon gate task`) must
|
|
@@ -102,16 +121,24 @@ work inside an approved scope, never to the approval itself.
|
|
|
102
121
|
change's `tasks.md` as it is now, so a task row you add needs no
|
|
103
122
|
`canon ingest plans` first.
|
|
104
123
|
7. **Independent review.** A different session from the one that wrote the
|
|
105
|
-
code and evidence reviews the change. The
|
|
124
|
+
code and evidence reviews the change. The review session must be able
|
|
125
|
+
to run the checks it signs off (the build, the tests, the app); a
|
|
126
|
+
reviewer that cannot run the tests says so and does not sign off. The
|
|
127
|
+
reviewer records every defect
|
|
106
128
|
the review finds as a finding:
|
|
107
129
|
`canon finding add --change-id <slug> --round <n> --seq <n> --severity <blocker|should-fix|note> --reviewer <me> --actor-id <me> --session-id <session> --summary "…"`;
|
|
130
|
+
`canon finding list --change-id <slug> [--open] [--json]` shows the
|
|
131
|
+
change's findings (round, seq, severity, disposition, reviewer, summary),
|
|
132
|
+
so the next seq and the open blockers are one command away;
|
|
108
133
|
once fixed,
|
|
109
134
|
`canon finding close --change-id <slug> --round <n> --seq <n> --disposition fixed --resolution-sha <sha>`
|
|
110
135
|
(a short sha is fine; canon resolves it). With `write.promote: manual`,
|
|
111
136
|
follow each with `canon gate promote`. Every bug fix adds or extends a scenario. The
|
|
112
137
|
reviewer signs off each scenario:
|
|
113
138
|
`canon review add --project-id <root-id> --scenario-id <id> --reviewer <who> --actor-id <who> --role reviewer --session-id <review-session> --pin <sha> --upstream-ref <ref>`
|
|
114
|
-
(written directly; nothing to promote).
|
|
139
|
+
(written directly; nothing to promote). Repeat `--scenario-id`, or pass
|
|
140
|
+
`--scenario-ids-from <file>`, to sign off several scenarios in one
|
|
141
|
+
call, all or nothing. A reviewer of user-visible
|
|
115
142
|
behavior attaches what it looked at with `--artifact <path>`
|
|
116
143
|
(repeatable); canon stores and re-checks those bytes and never compares
|
|
117
144
|
images.
|
|
@@ -133,7 +160,10 @@ work inside an approved scope, never to the approval itself.
|
|
|
133
160
|
`shipped` yourself once `canon subject status <id> shipped` passes its
|
|
134
161
|
guard, unless the brief says the human ships. `canon status` shows
|
|
135
162
|
whether each subject's brief is approved, by the same check, and names
|
|
136
|
-
the next step.
|
|
163
|
+
the next step. `shipped → verifying` reopens a shipped subject for a
|
|
164
|
+
follow-up (see the working loop above): it needs `--reason`, and it is
|
|
165
|
+
refused, naming the brief, when the subject's newest change is not
|
|
166
|
+
approved.
|
|
137
167
|
|
|
138
168
|
Every write command says whether the record is final (`written directly`,
|
|
139
169
|
or `staged and promoted in this run`) or staged; only a staged record
|
|
@@ -151,9 +181,17 @@ canon init --providers=claude,codex # harnesses for the canon skill; wi
|
|
|
151
181
|
canon skills install # follows canon.yaml skills.providers (else the install lock's, else the detected ones; none detected installs nothing)
|
|
152
182
|
canon skills install --providers=claude,codex,omp,pi # any subset or none; rewrites skills.providers
|
|
153
183
|
canon skills check # read-only drift check
|
|
154
|
-
canon skills doctor # diagnostics, incl. a stray canon projection for an unselected provider; never deletes user files
|
|
184
|
+
canon skills doctor # diagnostics, incl. a stray canon projection for an unselected provider and a projection a formatter rewrote; never deletes user files
|
|
155
185
|
```
|
|
156
186
|
|
|
187
|
+
`install` and `doctor` end with `owned-paths:`, the globs of every path
|
|
188
|
+
canon writes. Add them to the repo's formatter and linter ignore lists; a
|
|
189
|
+
formatter that rewrites a projection or a ledger record makes canon read
|
|
190
|
+
drift. Specs, `policy.yaml` and the changes are written by hand and are not
|
|
191
|
+
on the list. `doctor` reports a projection whose bytes changed
|
|
192
|
+
but whose content did not as `formatter-rewritten` and names the ignore
|
|
193
|
+
entry to add; a changed file stays `stale`.
|
|
194
|
+
|
|
157
195
|
The harness list lives in `canon.yaml` as `skills: { providers: [...] }`.
|
|
158
196
|
`.agents` alone never selects Codex; `.codex` does. Change the list with
|
|
159
197
|
`canon skills install --providers=<list>`, never by assuming a default.
|
|
@@ -28,7 +28,7 @@ Every violation carries one of these stable, grep-able strings:
|
|
|
28
28
|
| `malformed-evidence` | A candidate record doesn't parse, is misfiled, or carries an unparseable interim tag. Malformed evidence is no evidence. |
|
|
29
29
|
| `flagged` | The human-only `flagged` overlay is set — never green regardless of passing evidence. |
|
|
30
30
|
| `unevidenced-flip` | `canon gate task <task_id>` was asked to flip a checkbox with no matching, non-`Divergent` evidence record. |
|
|
31
|
-
| `fabricated-evidence` | An evidence note contains a blocklisted marker (`"would pass"`, `"TBD"`, `"n/a"`) or a bare `verified` claim with no attached command result. |
|
|
31
|
+
| `fabricated-evidence` | An evidence note contains a blocklisted marker (`"would pass"`, `"TBD"`, `"n/a"`) standing as a whole word — never inside a path, a file name, a URL or a longer word, so `pack.json/atlas` is not `n/a` — or a bare `verified` claim with no attached command result. `canon evidence add` refuses such a note before writing (`canon evidence add: refused — fabricated-evidence …`, exit 1). |
|
|
32
32
|
| `open-blocker` | With `spec_coverage.require_review` set (and `block_on_findings`, the default), a Finding whose latest version is severity `blocker`, disposition `open`, on a change an in-scope subject adopted. Subject string `<change_id>#<round>.<seq>`. See `canon-review`. |
|
|
33
33
|
| `false-success` | The latest audit record (`canon evidence add --kind audit`) of a scenario says `refuted`: an external evaluator found a counterexample to the claimed success. Always on, regardless of policy or sampling, and takes precedence over any declared faithful verdict; only a newer audit (`confirmed` or `indeterminate`) clears it. The detail names the evaluator, its version, its sha256 and the counterexample paths. |
|
|
34
34
|
|
|
@@ -391,6 +391,8 @@ canon evidence add --scenario-id cart.add.04 --project-id root \
|
|
|
391
391
|
# a JUnit XML or Cucumber JSON report: also records the matched case + outcome
|
|
392
392
|
canon evidence add ... --report junit:target/nextest/default/junit.xml
|
|
393
393
|
canon evidence add ... --report cucumber:reports/cucumber.json
|
|
394
|
+
# a Swift Testing event stream (swift test --experimental-event-stream-output <path>)
|
|
395
|
+
canon evidence add ... --report swift-testing:.build/events.jsonl
|
|
394
396
|
```
|
|
395
397
|
|
|
396
398
|
A case is bound when the scenario id is its OWN: the first
|
|
@@ -408,6 +410,12 @@ verdict when ANY bound case failed or was skipped refuses (exit 1). Files
|
|
|
408
410
|
must be inside the repository, are recorded by relative path, and are
|
|
409
411
|
stored (see Bound evidence stays provable).
|
|
410
412
|
|
|
413
|
+
A Swift Testing case is named by its display name (`@Test("cart.add.04
|
|
414
|
+
refuses an out-of-stock item")`), else its function name
|
|
415
|
+
(`cart_add_04_refuses()`). A test with a failing issue failed, a skipped
|
|
416
|
+
test was skipped, a test that started and ended without one passed, and
|
|
417
|
+
a test that started and never ended failed.
|
|
418
|
+
|
|
411
419
|
Strength, weakest first: `attested` (no attachment) < `artifact` (any
|
|
412
420
|
bound file) < `report` (a parsed report whose case passed). The policy
|
|
413
421
|
decides whether the gate cares:
|
|
@@ -431,6 +439,55 @@ violation in every mode. Canon still cannot know whether the bound test
|
|
|
431
439
|
actually exercises the scenario — binding narrows the trust gap, it does
|
|
432
440
|
not close it.
|
|
433
441
|
|
|
442
|
+
### One report, many scenarios (0.18)
|
|
443
|
+
|
|
444
|
+
One test run proves many scenarios. Bind it once:
|
|
445
|
+
|
|
446
|
+
```bash
|
|
447
|
+
canon evidence add --project-id root --all-scenarios \
|
|
448
|
+
--report junit:target/nextest/default/junit.xml \
|
|
449
|
+
--kind test-run --ref "cargo nextest run" --role implementer
|
|
450
|
+
```
|
|
451
|
+
|
|
452
|
+
Each indexed scenario of `--project-id` that owns a case (the rule above)
|
|
453
|
+
gets its own record, with only its own cases. Its verdict comes from
|
|
454
|
+
them: `faithful` when every case passed, `divergent` when one failed,
|
|
455
|
+
`not-applicable` when one was skipped and none failed. `--verdict <v>`
|
|
456
|
+
claims the same verdict for every record instead. A case whose id is not
|
|
457
|
+
indexed is named and skipped (`skipped <id> — not an indexed scenario`);
|
|
458
|
+
a report no indexed scenario owns is refused (exit 2). The records share
|
|
459
|
+
the stored report and every `--artifact`, and the output lists each
|
|
460
|
+
scenario with its verdict on its own line.
|
|
461
|
+
|
|
462
|
+
It is all or nothing: a claimed `faithful` verdict over a failed case,
|
|
463
|
+
a fabrication marker in `--summary`, an unapproved brief for any
|
|
464
|
+
scenario's subject, or a scenario whose latest record binds a file the
|
|
465
|
+
batch does not refuses the whole batch, and nothing is written.
|
|
466
|
+
`--scenario-id`, `--task`, `--report-case`, `--kind audit`,
|
|
467
|
+
`--drop-attachment` and the approval flags name one subject and are
|
|
468
|
+
refused with `--all-scenarios`.
|
|
469
|
+
|
|
470
|
+
A task's evidence with `--report` and no `--scenario-id` binds EVERY case
|
|
471
|
+
in the report, so a `faithful` verdict needs all of them to pass.
|
|
472
|
+
|
|
473
|
+
### A write that makes a review stale says so (0.18)
|
|
474
|
+
|
|
475
|
+
When new evidence (one record or a batch) supersedes the evidence a
|
|
476
|
+
current pinned review saw, `canon evidence add` prints one line per
|
|
477
|
+
review it makes stale, with the command that re-pins it:
|
|
478
|
+
|
|
479
|
+
```
|
|
480
|
+
canon evidence add: review by `reviewer-2` of cart.add.01 is now stale (it pinned the evidence this record supersedes); 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
|
|
481
|
+
```
|
|
482
|
+
|
|
483
|
+
The decision is the gate's own (the same stale-review rule `canon gate
|
|
484
|
+
check` reports). The command repeats the review's reviewer, actor, role,
|
|
485
|
+
session, provenance ref, runtime labels and artifacts, pinned at `HEAD` (`<sha>`
|
|
486
|
+
when the repository has no commit). A record that is only staged says
|
|
487
|
+
the review "goes stale once this evidence is promoted": promote first,
|
|
488
|
+
then re-pin. The reviewer re-reviews before re-pinning; several
|
|
489
|
+
scenarios re-pin in one `canon review add` (see `canon-review`).
|
|
490
|
+
|
|
434
491
|
## Bound evidence stays provable (the artifact store)
|
|
435
492
|
|
|
436
493
|
`canon evidence add --artifact/--report` copies each bound file into
|
|
@@ -468,20 +525,29 @@ have its bindings checked. A superseded record is not checked —
|
|
|
468
525
|
re-attesting is the fix.
|
|
469
526
|
|
|
470
527
|
Files bound to a review (`canon review add --artifact`, see
|
|
471
|
-
`canon-review`) are checked the same
|
|
472
|
-
latest per `(project_id, scenario_id, reviewer)
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
528
|
+
`canon-review`) are checked by the same rule for every CURRENT in-effect
|
|
529
|
+
review: the latest per `(project_id, scenario_id, reviewer)`, less any
|
|
530
|
+
that went stale against its scenario (its spec or evidence pin moved, or
|
|
531
|
+
a review from before pinning is stale at its commit). A stale review is
|
|
532
|
+
superseded — the gate reads the newer review, or reports the missing
|
|
533
|
+
one as `unreviewed-promotion` — so its files are not re-checked, as a
|
|
534
|
+
superseded evidence record's are not. A missing blob whose working-tree
|
|
535
|
+
file still matches is an advisory, listed with the reviewer:
|
|
536
|
+
|
|
537
|
+
```
|
|
538
|
+
unstored cart.add.01 reports/cart-badge.png (sha256 9848…, review by `reviewer-2`) is verified against the working tree only
|
|
539
|
+
```
|
|
540
|
+
|
|
541
|
+
Anything else is `stale-evidence` on the review's scenario, and the
|
|
542
|
+
detail names the review: ``review by `<reviewer>` (actor `<id>`,
|
|
543
|
+
<time>)``.
|
|
478
544
|
|
|
479
545
|
`canon evidence vault [--max-artifact-mib <N>]` stores the bytes for
|
|
480
|
-
existing records: every attachment whose
|
|
481
|
-
its digest is copied in, and everything
|
|
482
|
-
with both digests. It exits `0` whenever it
|
|
483
|
-
an unstored file matters), `2` when the
|
|
484
|
-
fails.
|
|
546
|
+
existing evidence records and current reviews (a superseded review's files are not vaulted): every attachment whose
|
|
547
|
+
working-tree file still matches its digest is copied in, and everything
|
|
548
|
+
else is listed as not stored, with both digests. It exits `0` whenever it
|
|
549
|
+
ran (the gate decides whether an unstored file matters), `2` when the
|
|
550
|
+
ledger cannot be read or a write fails.
|
|
485
551
|
|
|
486
552
|
## Effect-aware risk approvals (`risk_tiers`)
|
|
487
553
|
|
|
@@ -135,7 +135,8 @@ fixed finding reopened with `--disposition open` counts as open again.
|
|
|
135
135
|
## `canon review add`
|
|
136
136
|
|
|
137
137
|
```bash
|
|
138
|
-
canon review add --project-id <root-id> --scenario-id <id> \
|
|
138
|
+
canon review add --project-id <root-id> --scenario-id <id> [--scenario-id <id>]... \
|
|
139
|
+
[--scenario-ids-from <file|->] \
|
|
139
140
|
--reviewer <who> --pin <sha-or-ref> --role <role> \
|
|
140
141
|
(--upstream-ref <ref> | --original-spec-ref <ref>) [--actor-id <id>] [--session-id <id>] \
|
|
141
142
|
[--outcome signed|indeterminate|unavailable] \
|
|
@@ -143,7 +144,7 @@ canon review add --project-id <root-id> --scenario-id <id> \
|
|
|
143
144
|
[--artifact <path>]... [--max-artifact-mib <MiB>]
|
|
144
145
|
```
|
|
145
146
|
|
|
146
|
-
One attributed Review record
|
|
147
|
+
One attributed Review record per scenario, joined on
|
|
147
148
|
`(project_id, scenario_id)` — both are required because two spec roots
|
|
148
149
|
may carry the same scenario id. Exactly one provenance ref is required.
|
|
149
150
|
`--actor-id` defaults to `canon`; `--session-id` fills `actor.session_id`.
|
|
@@ -153,6 +154,18 @@ own. It is written directly to the ledger, with no staging step, and says
|
|
|
153
154
|
so: `canon review add: wrote <path> (new) — written directly; nothing to
|
|
154
155
|
promote`.
|
|
155
156
|
|
|
157
|
+
### Several scenarios in one call (0.18)
|
|
158
|
+
|
|
159
|
+
`--scenario-id` is repeatable, and `--scenario-ids-from <file>` (`-` reads
|
|
160
|
+
stdin) adds one id per line; `#` starts a comment and blank lines are
|
|
161
|
+
skipped. One reviewer, pin, provenance ref and set of artifacts cover
|
|
162
|
+
every review; each review pins its own scenario and evidence, and each
|
|
163
|
+
gets its own `pinned` and `wrote` line. The artifacts are stored once.
|
|
164
|
+
It is all or nothing: a scenario with no record, or a line in the file
|
|
165
|
+
that is not a scenario id, refuses the call (exit 2) and writes no review.
|
|
166
|
+
This is also the batch re-pin after `canon evidence add` reports reviews
|
|
167
|
+
it made stale (see `canon-gate`).
|
|
168
|
+
|
|
156
169
|
### What the reviewer looked at (artifacts)
|
|
157
170
|
|
|
158
171
|
`--artifact <path>` (repeatable) binds a file the reviewer looked at — a
|
|
@@ -173,19 +186,25 @@ The field is absent when no `--artifact` is given. `review add` prints
|
|
|
173
186
|
.canon/artifacts/sha256/ (<k> newly stored) — commit them with the
|
|
174
187
|
ledger`.
|
|
175
188
|
|
|
176
|
-
`canon gate check` re-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
`canon
|
|
189
|
+
`canon gate check` re-checks every file bound to a current in-effect
|
|
190
|
+
review (the latest per `(project_id, scenario_id, reviewer)`, unless it
|
|
191
|
+
went stale against its scenario) by the evidence rule (see `canon-gate`,
|
|
192
|
+
"Bound evidence stays provable"). A missing blob whose working-tree file
|
|
193
|
+
still matches is an advisory naming `canon evidence vault`, which stores
|
|
194
|
+
it back. A missing blob with a changed or missing working-tree file, or
|
|
195
|
+
an altered blob, is `stale-evidence` on the review's scenario, naming
|
|
196
|
+
the review:
|
|
181
197
|
|
|
182
198
|
```
|
|
183
|
-
stale-evidence world.demo.01 — review by `reviewer-2` (actor `reviewer-2`, 2026-10-11T09:00:00Z) bound file `reports/title.png` recorded sha256 <hex>; stored blob .canon/artifacts/sha256/<hex>: missing; working tree: <hex> — the bound bytes are gone, look again and re-record the review with `canon review add --artifact <path>`
|
|
199
|
+
stale-evidence world.demo.01 — review by `reviewer-2` (actor `reviewer-2`, 2026-10-11T09:00:00Z) bound file `reports/title.png` recorded sha256 <hex>; stored blob .canon/artifacts/sha256/<hex>: missing; working tree: <other hex> — the bound bytes are gone, look again and re-record the review with `canon review add --artifact <path>`
|
|
184
200
|
```
|
|
185
201
|
|
|
186
202
|
The reviewer's later review replaces the earlier one, so its files are
|
|
187
|
-
the ones checked.
|
|
188
|
-
|
|
203
|
+
the ones checked. A review that went stale (its scenario's spec or
|
|
204
|
+
evidence moved since) is not checked either: it no longer counts, and the
|
|
205
|
+
scenario's newer review is the one whose files are checked. Canon binds
|
|
206
|
+
and re-checks bytes only; it never compares images or judges what a
|
|
207
|
+
screenshot shows.
|
|
189
208
|
|
|
190
209
|
### What the review saw (pins)
|
|
191
210
|
|
|
@@ -104,11 +104,12 @@ canon query --kind trajectory --json
|
|
|
104
104
|
|
|
105
105
|
canon exposes layered `stg_`/`int_`/`mart_` query views; `canon report`
|
|
106
106
|
builds its numbers from the `mart_` views. Those read ONLY local roots
|
|
107
|
-
— the git ledger
|
|
107
|
+
— the git ledger, the sqlite `hot` db file (local, no credential; read
|
|
108
|
+
since 0.18) and a LOCAL `.canon/r2` parquet mirror. `canon report`
|
|
108
109
|
never opens a live DB connection or the live bucket, so:
|
|
109
110
|
|
|
110
|
-
- Kinds routed to `hot`
|
|
111
|
-
report
|
|
111
|
+
- Kinds routed to a postgres `hot` rung do NOT appear in `canon
|
|
112
|
+
report`; kinds routed to `canon init`'s default sqlite `hot` rung do.
|
|
112
113
|
- Kinds routed to `cold` (s3) appear only if a local `.canon/r2` mirror
|
|
113
114
|
has been separately materialized — there is no automatic sync.
|
|
114
115
|
|
|
@@ -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>] [--actor-id <id>]`
|
|
106
|
+
`canon subject status <id> <state> [--override-reason <text>] [--reason <text>] [--actor-id <id>]`
|
|
107
107
|
performs a policy-gated transition.
|
|
108
108
|
The legal chain:
|
|
109
109
|
|
|
@@ -111,9 +111,10 @@ The legal chain:
|
|
|
111
111
|
proposed → specced → building → verifying → shipped
|
|
112
112
|
```
|
|
113
113
|
|
|
114
|
-
plus **
|
|
115
|
-
|
|
116
|
-
|
|
114
|
+
plus **shipped → verifying** to reopen a shipped subject (with
|
|
115
|
+
`--reason`, below) and **any state → retired** (a subject can be retired
|
|
116
|
+
from anywhere). Any other transition is rejected by failure class, exits
|
|
117
|
+
1, and leaves the record UNCHANGED (fail closed).
|
|
117
118
|
|
|
118
119
|
```bash
|
|
119
120
|
canon subject status subject-domain-loop specced
|
|
@@ -242,6 +243,39 @@ raised later, a scenario tagged later) still fails the gate. Without
|
|
|
242
243
|
`require_review`, nothing is printed and nothing changes, except that
|
|
243
244
|
`shipped` still runs the brief check above.
|
|
244
245
|
|
|
246
|
+
### Reopening a shipped subject (0.18)
|
|
247
|
+
|
|
248
|
+
A follow-up on a shipped subject (a fix, a review round, a scenario its
|
|
249
|
+
gate now misses) reopens it: reopen, add tasks to the approved change,
|
|
250
|
+
scenario, evidence, review, ship.
|
|
251
|
+
|
|
252
|
+
```bash
|
|
253
|
+
canon subject status demo-subject verifying \
|
|
254
|
+
--reason "follow-up: the retry path leaks a file handle" --actor-id impl
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
```
|
|
258
|
+
canon subject status: reopened `demo-subject` from shipped, recorded by `impl`: follow-up: the retry path leaks a file handle; add the follow-up as tasks in its approved change `c-demo`, then scenario, evidence and review; shipping again runs every guard
|
|
259
|
+
canon subject status: demo-subject → verifying — written directly; nothing to promote
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
- `--reason "<one line>"` is required for `shipped → verifying` and
|
|
263
|
+
refused on any other transition (exit 2, as is a blank one). The new
|
|
264
|
+
record carries it with the actor and the change the work goes into:
|
|
265
|
+
`reopen: { reason: …, change: c-demo, actor: { agent_id: impl } }`. The
|
|
266
|
+
next status write clears it, as it clears a waiver.
|
|
267
|
+
- Inside the approved scope it needs no human: the subject's newest
|
|
268
|
+
adopted change's brief is approved, or there is no brief to answer.
|
|
269
|
+
Otherwise it refuses, exit 1, naming the brief to approve, and
|
|
270
|
+
`--override-reason` does not waive it:
|
|
271
|
+
``unreviewed-promotion demo-subject — shipped → verifying: reopening needs the approved scope, and the brief is not approved — .canon/changes/c-next/brief.md has no `Approved:` line; …``.
|
|
272
|
+
- The review guard does not run on a reopen (it prints `skipped … — a
|
|
273
|
+
reopen moves the subject back to fix its gaps; shipping again runs
|
|
274
|
+
it`). `verifying → shipped` afterwards runs every guard above again.
|
|
275
|
+
- `canon status` flags a shipped subject whose gate went red (whatever
|
|
276
|
+
`verifying → shipped` would now refuse it on) and its only step is
|
|
277
|
+
this reopen.
|
|
278
|
+
|
|
245
279
|
## Adopt flow
|
|
246
280
|
|
|
247
281
|
Planning docs written outside canon become change/task records via
|
|
@@ -261,19 +295,33 @@ and a flip re-imports that change itself. Run `canon ingest plans` for a
|
|
|
261
295
|
plan written outside canon, or when `canon query --kind task` must list
|
|
262
296
|
rows nobody has flipped yet.
|
|
263
297
|
|
|
264
|
-
`adopt`
|
|
265
|
-
`change_ids`, so `canon query --kind change --change-id …`
|
|
266
|
-
query --kind subject` agree on the link from both ends. It
|
|
267
|
-
the side that lacks the link, so rerunning it on a linked
|
|
268
|
-
nothing and exits `0`.
|
|
298
|
+
`adopt` adds the subject to the change's `subject_ids` and the change to
|
|
299
|
+
the Subject's `change_ids`, so `canon query --kind change --change-id …`
|
|
300
|
+
and `canon query --kind subject` agree on the link from both ends. It
|
|
301
|
+
writes only the side that lacks the link, so rerunning it on a linked
|
|
302
|
+
pair writes nothing and exits `0`.
|
|
303
|
+
|
|
304
|
+
A change may belong to several subjects (0.18). Adopting it under a
|
|
305
|
+
second subject adds that subject, never re-points the change, and says
|
|
306
|
+
so:
|
|
307
|
+
|
|
308
|
+
```
|
|
309
|
+
canon subject adopt: linked change `refactor-core` to subject `editing` as well; the change now belongs to `perception` and `editing` — written directly; nothing to promote
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
Every reader joins a subject to its changes from both sides (the
|
|
313
|
+
subject's `change_ids`, and every change whose `subject_ids` names it):
|
|
314
|
+
the open-blocker rule, the status guard, `canon status`, the brief and
|
|
315
|
+
the evidence refusal. An open blocker on a shared change gates each
|
|
316
|
+
subject it belongs to. A change written before 0.18 names one subject as
|
|
317
|
+
`subject_id`; it reads, and `canon query --kind change --json` shows it,
|
|
318
|
+
as a one-element `subject_ids`.
|
|
269
319
|
|
|
270
320
|
The two records cannot be written atomically, so the subject is written
|
|
271
|
-
first:
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
and prints `canon subject adopt <change_id> --subject <id>` to complete
|
|
276
|
-
it.
|
|
321
|
+
first: a half-done adoption still has that change's open blockers
|
|
322
|
+
counted. If the change write then fails, `adopt` exits `2`, names the
|
|
323
|
+
subject that now lists the change, and prints `canon subject adopt
|
|
324
|
+
<change_id> --subject <id>` to complete it.
|
|
277
325
|
|
|
278
326
|
### Starting a new change: `canon change new`
|
|
279
327
|
|
|
@@ -393,6 +441,14 @@ uses, so status and the gate cannot disagree about a gap:
|
|
|
393
441
|
canon brief and no line it reads
|
|
394
442
|
``brief: not approved (<brief> has no `Approved:` line)``.
|
|
395
443
|
There is no record kind for it; the file is the record.
|
|
444
|
+
- **Red shipped subject** (0.18) — a `shipped` subject that the
|
|
445
|
+
`verifying → shipped` guard would now refuse, by the guard's own checks
|
|
446
|
+
(owned scenarios, verdicts, required cases, audits, and, where
|
|
447
|
+
`require_review` covers `shipped`, reviews and open blockers under
|
|
448
|
+
`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>` ``,
|
|
450
|
+
carries `red: [...]` in `--json`, and that reopen is its only `next:`
|
|
451
|
+
step. Open blockers count against every subject a change belongs to.
|
|
396
452
|
- **Unowned** — scenarios with no `@subject:` tag.
|
|
397
453
|
- **Audits** — how many scenarios are sampled for
|
|
398
454
|
`spec_coverage.require_audit`, confirmed, owed (no audit or
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@journeykit/canon",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.18.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.18.0",
|
|
34
|
+
"@journeykit/canon-core-linux-x64": "0.18.0",
|
|
35
|
+
"@journeykit/canon-core-win32-x64": "0.18.0"
|
|
36
36
|
},
|
|
37
37
|
"devDependencies": {
|
|
38
38
|
"@types/node": "^20.0.0",
|