@journeykit/canon 0.18.0 → 0.21.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -142,7 +142,7 @@ A present section, even `require_evidence: false` or a malformed one
142
142
  case). This is the gap evidence presence cannot see: every scenario
143
143
  attested, none of them specifying a refusal. Untagged scenarios satisfy
144
144
  no case, so an unclassified corpus is reported, not passed. Works with
145
- `require_evidence: false` too. `canon subject status <id> shipped`
145
+ `require_evidence: false` too. `canon subject status <id> --to shipped`
146
146
  applies the same rule to the scenarios the subject owns.
147
147
  - **Independent review** (`require_review`) — scenarios whose subject is
148
148
  in `require_review.scope` (default `[verifying, shipped]`; `[]` = every
@@ -228,25 +228,38 @@ canon evidence add --kind audit --project-id root --scenario-id cart.add.01 \
228
228
  - Audits fold apart: per `(project_id, scenario_id)` there is a latest
229
229
  declared record and a latest audit. An audit never becomes the
230
230
  scenario's latest declared verdict.
231
- - `canon subject status <id> shipped` refuses `false-success`, and an
231
+ - `canon subject status <id> --to shipped` refuses `false-success`, and an
232
232
  owed sampled audit when `require_audit.scope` covers `shipped`.
233
233
  - `canon status` shows an `audits` section (sampled / confirmed / owed /
234
234
  refuted) and suggests `canon evidence add --kind audit …` in `next:`.
235
235
 
236
- ### Dropping a binding (`--drop-attachment`)
237
-
238
- `canon evidence add` for a scenario refuses (exit `2`) when the
239
- scenario's current latest record of the same fold (declared records, or
240
- audit records for an audit) — committed ledger plus `_staging` — binds
241
- files the new record does not. The refusal names the paths and two ways
242
- out: re-bind them (`--artifact`/`--report`; new bytes at the same path
243
- are fine), or drop them on purpose with `--drop-attachment <path>`
244
- (repeatable) and one single-line `--drop-reason <text>`. The drop is
245
- recorded as `attachment_drop: {paths, reason}`. Refused (`2`): a drop
246
- without a reason, a reason without a drop, a drop path that is not
247
- actually dropped, a multi-line reason, or a drop without
248
- `--scenario-id` (task-only evidence has no drop guard). `canon status`
249
- lists recorded drops (`dropped bindings (N):`; `drops[]` in `--json`).
236
+ ### Carrying and dropping bindings (`--drop-attachment`)
237
+
238
+ `canon evidence add` for a scenario carries forward what the scenario's
239
+ current latest record of the same fold (declared records, or audit
240
+ records for an audit; committed ledger plus `_staging`) bound: every
241
+ file the new write does not name again is bound into the new record
242
+ unchanged (same path, digest and stored blob), and `--json` lists it as
243
+ `carried: [paths]`. A file named again (`--artifact`/`--report`, new
244
+ bytes at the same path included) replaces its earlier binding. A suite
245
+ batch (`--all-scenarios`) therefore never costs a scenario its
246
+ screenshots. A carried file whose blob the artifact store does not
247
+ hold intact is refused (exit `2`, `EVIDENCE_CARRY_BLOB_MISSING`, nothing
248
+ written): re-bind it, run `canon evidence vault`, or drop it. The audit
249
+ fold (`--kind audit`) does not carry: an audit that leaves out a file
250
+ the latest audit bound is refused (exit `2`) unless it names the file
251
+ again or drops it with `--drop-attachment`, so a counterexample never
252
+ rides along into a confirming audit.
253
+
254
+ The only way to lose a binding is `--drop-attachment <path>`
255
+ (repeatable) with one single-line `--drop-reason <text>`, recorded as
256
+ `attachment_drop: {paths, reason}`. With `--all-scenarios` the drop
257
+ applies to each scenario that binds the path. Refused (`2`): a drop
258
+ without a reason, a reason without a drop, a drop path that no
259
+ scenario of the write leaves out, a multi-line reason, or a drop
260
+ without `--scenario-id`/`--all-scenarios` (task-only evidence has no
261
+ carry or drop). `canon status` lists recorded drops (`dropped bindings
262
+ (N):`; `drops[]` in `--json`).
250
263
 
251
264
  ### Nothing is attested before the brief is approved (`--override-reason`)
252
265
 
@@ -256,7 +269,8 @@ reports it, and builds nothing for that scope until the human answers.
256
269
  refuses (exit `1`, nothing staged or stored) while a subject the record
257
270
  attests for has an enforced, unapproved brief: the newest adopted
258
271
  change's canon `.canon/changes/<change>/brief.md` has no `Approved:`
259
- line, or there is no canon `brief.md` yet but the change comes from an
272
+ line, was edited after its approval (`BRIEF_CHANGED_SINCE_APPROVAL`), or
273
+ there is no canon `brief.md` yet but the change comes from an
260
274
  external openspec plan with a `proposal.md` under the plan sources (the
261
275
  check `canon status` and the ship guard use; see
262
276
  `canon-subject`). The subjects are the scenario's `@subject` owner for
@@ -264,8 +278,8 @@ check `canon status` and the ship guard use; see
264
278
  `--task`:
265
279
 
266
280
  ```
267
- 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 as `Approved: <who> <when> — "<their words>"` in .canon/changes/c-demo/brief.md
268
- 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 words as `Approved: <who> <when> — "<their words>"` in that brief.md only from that answer; to attest anyway, pass --override-reason <text>, which is recorded on the evidence record
281
+ 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>"`
282
+ 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
269
283
  ```
270
284
 
271
285
  For a change from an external plan with no canon brief yet, the first
@@ -274,8 +288,8 @@ change `c-ext` comes from the external plan
274
288
  openspec/changes/c-ext/proposal.md, which canon reads and never edits, so
275
289
  its approval lives in that canon file, so the human has not answered it;
276
290
  once they do, create .canon/changes/c-ext/brief.md holding a `Source:
277
- openspec/changes/c-ext/proposal.md` line and record their words there as
278
- `Approved: <who> <when> — "<their words>"` ``. An `Approved:` line in the
291
+ openspec/changes/c-ext/proposal.md` line, then record their words with
292
+ `canon brief approve --change c-ext --by <the human> --note "<their words>"` ``. An `Approved:` line in the
279
293
  external `proposal.md` itself is never read.
280
294
 
281
295
  A subject with no brief to answer (no adopted change, or a newest change
@@ -305,7 +319,7 @@ brief overrides: 1 evidence record(s) attested before their brief was approved
305
319
 
306
320
  ### Review-waiver advisories
307
321
 
308
- A subject moved into a review-scoped status with `canon subject status
322
+ A subject moved into a review-scoped status with `canon subject status <id> --to <state>
309
323
  --override-reason` (see `canon-subject`) keeps the `unreviewed-promotion`
310
324
  and `open-blocker` gaps its waiver recorded visible without failing the
311
325
  gate. Only those exact `(class, subject)` pairs are waived; a gap that
@@ -319,15 +333,18 @@ review waivers: 1 advisory(ies) — not failing the gate:
319
333
 
320
334
  The waiver lasts only until the subject's next status write.
321
335
 
322
- `canon subject status <id> shipped` also refuses where a brief exists and
323
- has no `Approved:` line (the subject's newest adopted change has a canon
324
- `brief.md`, or an external `proposal.md` and no canon `brief.md` yet;
336
+ `canon subject status <id> --to shipped` also refuses where a brief exists and
337
+ is not approved (the subject's newest adopted change has a canon
338
+ `brief.md` with no `Approved:` line or one the brief's text no longer
339
+ matches, or an external `proposal.md` and no canon `brief.md` yet;
325
340
  see `canon-subject`), as an `unreviewed-promotion` naming
326
- the subject. With no brief to answer it prints an advisory and does not
327
- refuse. Like the evidence refusal above, it is a backstop: the stop is at
328
- the brief. `canon gate check` does not grade briefs, so a waived ship
329
- brief prints no advisory here; `canon status` keeps showing the brief as
330
- not approved.
341
+ the subject, and while `canon brief check` has structure blockers or a
342
+ flow step has no scenario or no failure scenario (the coverage rules gate
343
+ shipping, never approval). With no brief to answer it prints an advisory
344
+ and does not refuse. Like the evidence refusal above, it is a backstop:
345
+ the stop is at the brief. `canon gate check` does not grade briefs, so a
346
+ waived ship brief prints no advisory here; `canon status` keeps showing
347
+ the brief as not approved.
331
348
 
332
349
  ### Reviews that predate pinning (checked against their pin)
333
350
 
@@ -461,11 +478,12 @@ scenario with its verdict on its own line.
461
478
 
462
479
  It is all or nothing: a claimed `faithful` verdict over a failed case,
463
480
  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`.
481
+ scenario's subject, or a carried attachment whose blob is missing from the
482
+ store refuses the whole batch, and nothing is written. Each scenario's
483
+ earlier attachments are carried into its new record (see above), and
484
+ `--drop-attachment` applies per scenario. `--scenario-id`, `--task`,
485
+ `--report-case`, `--kind audit` and the approval flags name one subject
486
+ and are refused with `--all-scenarios`.
469
487
 
470
488
  A task's evidence with `--report` and no `--scenario-id` binds EVERY case
471
489
  in the report, so a `faithful` verdict needs all of them to pass.
@@ -580,6 +598,11 @@ empty clean map.
580
598
 
581
599
  ## `canon gate task <task_id> [--repo <dir>]`
582
600
 
601
+ For imported external plans, and for a `tasks.md` that already exists in
602
+ a canon change. `canon change new` writes no `tasks.md` (0.19), and
603
+ nothing gates on one: a canon-owned change is covered by its flow steps'
604
+ scenarios (`canon status`).
605
+
583
606
  The evidence-gated task checkbox flip. Resolves `<task_id>`
584
607
  (`<change_id>#<n>`) through `.canon/changes/` (canon's own changes, read
585
608
  first whenever the directory exists, with no `plans:` entry) and then
@@ -661,7 +684,7 @@ promote`, so no separate
661
684
  staged. A record another run staged is not touched: only `gate promote`
662
685
  commits everything staged. It stays safe to run anyway: with nothing
663
686
  staged it prints
664
- `nothing staged` and exits `0`. `divergence stage` stages for `canon
687
+ `nothing staged` and exits `0`. `divergence add` stages for `canon
665
688
  divergence promote` and names it, under the same `write:` rule. Every
666
689
  other write — `review add`, `divergence
667
690
  resolve|defer`, `subject new|adopt|status`, `change new`, `scenario new`, `feature new`,
@@ -72,7 +72,7 @@ scenario index # refreshed by every reader; `canon inventory sy
72
72
  │
73
73
  ▼
74
74
  canon review add # attests a review — see canon-inventory downstream / canon-gate
75
- canon divergence {stage,promote,resolve,defer,status} # tracks/resolves a divergence
75
+ canon divergence {add,promote,resolve,defer,status} # tracks/resolves a divergence
76
76
  canon gate check [--release] # trust-ladder / staleness / coverage — see canon-gate
77
77
  │
78
78
  ▼
@@ -158,7 +158,7 @@ specs:
158
158
 
159
159
  - **Review** — `canon review add` attests a verdict against a synced
160
160
  scenario. (See `canon-inventory` downstream commands and each command's `--help`.)
161
- - **Divergence** — `canon divergence {stage,promote,resolve,defer,status}`
161
+ - **Divergence** — `canon divergence {add,promote,resolve,defer,status}`
162
162
  tracks/resolves divergences against synced scenarios.
163
163
  - **Gate** — `canon gate check` enforces trust-ladder / staleness /
164
164
  coverage over the corpus (see `canon-gate`).
@@ -46,15 +46,13 @@ The production activation path is a signed, paired-evaluation workflow:
46
46
  ```bash
47
47
  canon learn approve <strategy_id> --evaluation evaluation.json \
48
48
  --principal alice --repo .
49
- # `request` is an exact alias:
50
- canon learn request <strategy_id> --evaluation evaluation.json \
51
- --principal alice --repo .
52
49
  ```
53
50
 
54
- The JSON output contains the evaluation digest, RFC3339 `approved_at`,
51
+ With `--json` the payload is `.data` of the `{ "data", "meta" }` envelope, and
52
+ `meta.next` names the human signing step. The `data` contains the evaluation digest, RFC3339 `approved_at`,
55
53
  subject `strategy:<strategy_id>`, namespace
56
- `canon-learning-approval-v1`, and `payload_hex`. `approve`/`request` never
57
- read a private key and never set `verified`.
54
+ `canon-learning-approval-v1`, and `payload_hex`. `approve` never
55
+ reads a private key and never sets `verified`.
58
56
  6. Decode `payload_hex` to the exact canonical JSON bytes. The signed object
59
57
  is:
60
58
 
@@ -10,8 +10,9 @@ one, and neither can `canon gate`.
10
10
  ## 1. Canon's own changes and external `plans:` sources
11
11
 
12
12
  `.canon/changes/` is canon's native plan source (dialect id `canon`):
13
- one `<slug>/` directory per change holding `brief.md` and `tasks.md`,
14
- written by `canon change new` (see `canon-subject`). Every plan reader —
13
+ one `<slug>/` directory per change holding `brief.md` (and a `tasks.md`
14
+ only when someone adds one: an optional work note), written by
15
+ `canon change new` (see `canon-subject`). Every plan reader —
15
16
  `canon ingest plans`, `canon gate task`, `canon evidence add --task`,
16
17
  `canon dispatch --task`, `canon status` and the brief check — reads it
17
18
  first whenever the directory exists, with no `plans:` entry. Being first,
@@ -21,7 +22,8 @@ Its records carry the actor `canon-plan-import-canon`; an external
21
22
  openspec change's carry `canon-plan-import-openspec`. A `brief.md` with a
22
23
  `Source: <path>` line is not a change of its own: it is the canon brief
23
24
  of a change imported from an external plan (it points at the external
24
- document and holds the human's `Approved:` line), so the native reader
25
+ document and holds the human's `Approved:` line, written by
26
+ `canon brief approve`), so the native reader
25
27
  skips that directory and the external source keeps the change id.
26
28
 
27
29
  `plans.sources` lists only EXTERNAL plans — documents you prepare outside
@@ -89,12 +91,13 @@ No dialect/plugin filter exists or is needed — an imported `change`/
89
91
  ## The `canon` dialect (`.canon/changes/`)
90
92
 
91
93
  Uses the `openspec` change-dir grammar below, with three differences:
92
- the brief is `brief.md` (its `## Why` first paragraph is the summary; a
94
+ the brief is `brief.md` (the first paragraph of its intent section, or of
95
+ `## Why` in a pre-0.19 brief, is the summary; a
93
96
  missing one is a `missing-brief-md` malformed dir), the source root is
94
97
  always `.canon/changes/` itself (archived changes under
95
98
  `.canon/changes/archive/`), and a `brief.md` carrying a `Source:` line is
96
- skipped (a pointer brief, above). `tasks.md` uses the same checkbox row
97
- grammar.
99
+ skipped (a pointer brief, above). An existing `tasks.md` uses the same
100
+ checkbox row grammar; a change with none imports with zero tasks.
98
101
 
99
102
  ## The `openspec` dialect
100
103
 
@@ -175,7 +178,7 @@ downstream reader's job.
175
178
  ## What this skill does NOT cover
176
179
 
177
180
  - **Authoring a change** — `canon change new` writes
178
- `.canon/changes/<slug>/brief.md` and `tasks.md` and adopts the change
181
+ `.canon/changes/<slug>/brief.md` (no `tasks.md`) and adopts the change
179
182
  into a subject (see `canon-subject`); it never scaffolds an openspec
180
183
  change dir. This skill covers IMPORTING plans.
181
184
  - **`canon gate check`/`canon gate task`** (the hand-authored one-task-at-
@@ -74,8 +74,10 @@ canon policy approve --by <who> [--note <text>] [--repo <dir>]
74
74
 
75
75
  A human approves the policy after reviewing `.canon/policy.yaml`, in the
76
76
  same answer that approves a subject's brief (0.16; see the skill's working
77
- loop). The human runs this, or the agent runs it right after that answer
78
- as `canon policy approve --by <the human> --note "<their words>"`. It
77
+ loop). `canon brief approve` records both in one call, so the agent does
78
+ not run this command next to it. Run `canon policy approve` alone for a
79
+ policy-only change, as the human, or as the agent right after that answer
80
+ (`canon policy approve --by <the human> --note "<their words>"`). It
79
81
  writes `.canon/policy.approval.json` directly (nothing to promote):
80
82
 
81
83
  ```json
@@ -227,7 +229,7 @@ read as a big-endian u64, are less than `rate · 2^64` — computed exactly,
227
229
  as a comparison against `ceil(rate · 2^64)`, so `rate: 0` samples nothing
228
230
  and `rate: 1` samples everything. A sampled in-scope scenario whose
229
231
  latest audit is missing or `indeterminate` is `uncovered-cell`; with
230
- `shipped` in `scope`, `canon subject status <id> shipped` also refuses
232
+ `shipped` in `scope`, `canon subject status <id> --to shipped` also refuses
231
233
  it. `canon context` and the `canon status` policy line show
232
234
  `require_audit=(rate=… seed=`…` scope=…)`.
233
235
 
@@ -55,7 +55,7 @@ The order an agent follows to claim a subject done:
55
55
  the screenshots or recordings it looked at with `--artifact <path>`
56
56
  (below).
57
57
 
58
- 4. **Move the subject**: `canon subject status <id> verifying`, later
58
+ 4. **Move the subject**: `canon subject status <id> --to verifying`, later
59
59
  `shipped`. See `canon-subject`.
60
60
 
61
61
  ## `canon finding add` / `canon finding close`
@@ -413,7 +413,7 @@ review waivers: 1 advisory(ies) — not failing the gate:
413
413
 
414
414
  ## The status guard
415
415
 
416
- With `require_review` present, `canon subject status <id> <state>`
416
+ With `require_review` present, `canon subject status <id> --to <state>`
417
417
  prints on stderr which review checks it ran or skipped:
418
418
 
419
419
  ```
@@ -98,6 +98,11 @@ canon query --kind handoff --since 2026-06-01T00:00:00Z
98
98
 
99
99
  # Machine-readable output instead of the human table.
100
100
  canon query --kind trajectory --json
101
+
102
+ # Page a long result and keep only the fields you need; `meta` carries
103
+ # returned, total, hasMore and nextCursor (`--all` returns everything).
104
+ canon query --kind trajectory --json --limit 50 --fields at,kind
105
+ canon query --kind trajectory --json --limit 50 --cursor <nextCursor>
101
106
  ```
102
107
 
103
108
  ## What `canon report` does and doesn't read
@@ -120,8 +125,11 @@ section listing them and emits a matching stderr `WARN`.
120
125
 
121
126
  ## Retention, export, and access audit
122
127
 
123
- `canon purge --kind <kind> --before <RFC3339> [--repo <dir>] [--dry-run] [--json]`
124
- is the canonical data-lifecycle boundary. The only purgeable kinds are
128
+ `canon purge --kind <kind> --before <RFC3339> (--plan | --confirm <planDigest>) [--repo <dir>] [--json]`
129
+ is the canonical data-lifecycle boundary: `--plan` prints the removal set and
130
+ its `planDigest` and deletes nothing; `--confirm <planDigest>` deletes only if
131
+ the plan recomputed now has the same digest (`PLAN_CHANGED`, exit 2, when the
132
+ data moved), and a bare `purge` is refused (`PURGE_PLAN_REQUIRED`). The only purgeable kinds are
125
133
  `session`, `run`, `event`, and `trajectory`; the cutoff applies to every
126
134
  routed rung and every configured aging destination, including retained
127
135
  historical versions. For `trajectory`, the command also purges the
@@ -134,8 +142,14 @@ an explicit `partial` result with matched/deleted counts and a nonzero
134
142
  status. Purge never rewrites Git history or deletes source transcript
135
143
  files.
136
144
 
145
+ With `--json`, `purge` and `export` answer `{ "data", "meta" }` like every other
146
+ command: `data` is the plan or manifest, `meta.next` the step after (a plan's is the
147
+ human's review and `--confirm`), and an export's page state (`total`, `returned`,
148
+ `nextCursor`) sits in `meta`. `export --json --out <file>` writes the manifest to the
149
+ file and answers with `data: { out, kind, recordCount }`.
150
+
137
151
  `canon export --kind <kind> [--before <RFC3339>] [--after <RFC3339>]
138
- [--out <file>] [--json]` emits a versioned manifest containing source scope,
152
+ [--out <file>] [--json] [--limit <n> | --all] [--cursor <c>]` emits a versioned manifest containing source scope,
139
153
  counts, digests, and redacted metadata. It does not authorize backend access;
140
154
  credentials remain an operator prerequisite. New output files are mode
141
155
  `0600`. Raw event detail text is excluded unless `--include-sensitive` is
@@ -161,7 +175,7 @@ record bodies.
161
175
  Four writes stage their record instead of committing it: `canon evidence
162
176
  add`, `canon finding add` and `canon finding close` (under
163
177
  `.canon/ledger/_staging/`, committed by `canon gate promote`) and `canon
164
- divergence stage` (committed by `canon divergence promote`). `canon.yaml`'s
178
+ divergence add` (committed by `canon divergence promote`). `canon.yaml`'s
165
179
  `write:` section decides whether they promote in the same run:
166
180
 
167
181
  ```yaml