@journeykit/canon 0.34.0 → 0.36.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 +47 -30
- package/dist/skills/reference/canon-artifact-ingest.md +6 -3
- package/dist/skills/reference/canon-authoring.md +9 -5
- package/dist/skills/reference/canon-context.md +6 -3
- package/dist/skills/reference/canon-fmt.md +2 -1
- package/dist/skills/reference/canon-gate.md +29 -21
- package/dist/skills/reference/canon-inventory.md +16 -11
- package/dist/skills/reference/canon-plan-import.md +6 -3
- package/dist/skills/reference/canon-plugins.md +12 -6
- package/dist/skills/reference/canon-policy.md +3 -3
- package/dist/skills/reference/canon-report-dashboard.md +11 -8
- package/dist/skills/reference/canon-review.md +8 -7
- package/dist/skills/reference/canon-session-ingest.md +12 -6
- package/dist/skills/reference/canon-storage.md +47 -17
- package/dist/skills/reference/canon-subject.md +45 -35
- package/dist/skills/reference/role-implementer.md +33 -33
- package/dist/skills/reference/role-orchestrator.md +83 -77
- package/dist/skills/reference/role-planner.md +28 -28
- package/dist/skills/reference/role-reviewer.md +29 -29
- package/man/canon-adapter-authorize.1 +2 -2
- package/man/canon-adapter-validate.1 +2 -2
- package/man/canon-adapter.1 +2 -2
- package/man/canon-artifacts-move.1 +6 -6
- package/man/canon-artifacts-store.1 +2 -2
- package/man/canon-artifacts-verify.1 +2 -2
- package/man/canon-artifacts.1 +2 -2
- package/man/canon-brief-approve.1 +5 -4
- package/man/canon-brief-check.1 +2 -2
- package/man/canon-brief-show.1 +2 -2
- package/man/canon-brief.1 +2 -2
- package/man/canon-change-new.1 +2 -2
- package/man/canon-change.1 +2 -2
- package/man/canon-completion.1 +2 -2
- package/man/canon-context-pack-add.1 +2 -2
- package/man/canon-context-pack-show.1 +2 -2
- package/man/canon-context-pack-verify.1 +2 -2
- package/man/canon-context-pack.1 +2 -2
- package/man/canon-context.1 +2 -2
- package/man/canon-dashboard.1 +2 -2
- package/man/canon-demo-init.1 +2 -2
- package/man/canon-demo-review.1 +2 -2
- package/man/canon-demo.1 +2 -2
- package/man/canon-dispatch-begin.1 +2 -2
- package/man/canon-dispatch-diff.1 +2 -2
- package/man/canon-dispatch-end.1 +2 -2
- package/man/canon-dispatch.1 +2 -2
- package/man/canon-divergence-accept.1 +2 -2
- package/man/canon-divergence-add.1 +3 -3
- package/man/canon-divergence-defer.1 +3 -3
- package/man/canon-divergence-list.1 +2 -2
- package/man/canon-divergence-resolve.1 +3 -3
- package/man/canon-divergence.1 +2 -2
- package/man/canon-evidence-add.1 +5 -5
- package/man/canon-evidence-approval-payload.1 +3 -3
- package/man/canon-evidence-audit.1 +4 -4
- package/man/canon-evidence.1 +2 -2
- package/man/canon-export.1 +2 -2
- package/man/canon-feature-new.1 +2 -2
- package/man/canon-feature.1 +2 -2
- package/man/canon-finding-add.1 +3 -3
- package/man/canon-finding-close.1 +2 -2
- package/man/canon-finding-list.1 +2 -2
- package/man/canon-finding.1 +2 -2
- package/man/canon-format.1 +2 -2
- package/man/canon-gate-check.1 +2 -2
- package/man/canon-gate-install-hooks.1 +2 -2
- package/man/canon-gate-promote.1 +2 -2
- package/man/canon-gate.1 +2 -2
- package/man/canon-ingest-plans.1 +2 -2
- package/man/canon-ingest-records.1 +2 -2
- package/man/canon-ingest-sessions.1 +2 -2
- package/man/canon-ingest.1 +2 -2
- package/man/canon-init.1 +7 -7
- package/man/canon-inventory-sync.1 +2 -2
- package/man/canon-inventory.1 +2 -2
- package/man/canon-ledger-compact.1 +5 -5
- package/man/canon-ledger.1 +3 -3
- package/man/canon-migrate.1 +8 -8
- package/man/canon-plugin-sync.1 +2 -2
- package/man/canon-plugin.1 +2 -2
- package/man/canon-policy-approve.1 +2 -2
- package/man/canon-policy-show.1 +2 -2
- package/man/canon-policy.1 +2 -2
- package/man/canon-prompt-add.1 +2 -2
- package/man/canon-prompt-show.1 +2 -2
- package/man/canon-prompt.1 +2 -2
- package/man/canon-purge.1 +7 -7
- package/man/canon-query.1 +2 -2
- package/man/canon-regime-show.1 +2 -2
- package/man/canon-regime.1 +2 -2
- package/man/canon-report-build.1 +2 -2
- package/man/canon-report-check.1 +2 -2
- package/man/canon-report-snapshot.1 +2 -2
- package/man/canon-report.1 +2 -2
- package/man/canon-retrieve.1 +2 -2
- package/man/canon-review-change.1 +3 -3
- package/man/canon-review-scenario.1 +3 -3
- package/man/canon-review.1 +2 -2
- package/man/canon-scenario-new.1 +2 -2
- package/man/canon-scenario.1 +2 -2
- package/man/canon-schema.1 +10 -4
- package/man/canon-search.1 +2 -2
- package/man/canon-selftest.1 +2 -2
- package/man/canon-skills-check.1 +2 -2
- package/man/canon-skills-doctor.1 +2 -2
- package/man/canon-skills-install.1 +2 -2
- package/man/canon-skills-show.1 +5 -5
- package/man/canon-skills.1 +2 -2
- package/man/canon-status.1 +2 -2
- package/man/canon-strategy-approval-payload.1 +2 -2
- package/man/canon-strategy-promote.1 +2 -2
- package/man/canon-strategy-rollback.1 +2 -2
- package/man/canon-strategy.1 +2 -2
- package/man/canon-subject-adopt.1 +2 -2
- package/man/canon-subject-move.1 +2 -2
- package/man/canon-subject-new.1 +2 -2
- package/man/canon-subject-show.1 +2 -2
- package/man/canon-subject-unadopt.1 +3 -3
- package/man/canon-subject.1 +2 -2
- package/man/canon-task-done.1 +2 -2
- package/man/canon-task.1 +2 -2
- package/man/canon-tier-age.1 +5 -5
- package/man/canon-tier.1 +2 -2
- package/man/canon-trace.1 +2 -2
- package/man/canon.1 +3 -3
- package/package.json +4 -4
|
@@ -18,11 +18,16 @@ concurrency outgrows sqlite's single-writer mode.
|
|
|
18
18
|
## `canon ingest sessions [--watch] [--interval-secs N] [--home <dir>] [--full] [--all-workspaces]`
|
|
19
19
|
|
|
20
20
|
```bash
|
|
21
|
-
|
|
22
|
-
canon ingest sessions
|
|
23
|
-
|
|
24
|
-
canon ingest sessions --
|
|
25
|
-
|
|
21
|
+
# one pass over this project's sources
|
|
22
|
+
canon ingest sessions
|
|
23
|
+
# poll every 30s (see --interval-secs)
|
|
24
|
+
canon ingest sessions --watch
|
|
25
|
+
# ignore the watermark; re-parse every in-scope file
|
|
26
|
+
canon ingest sessions --full
|
|
27
|
+
# machine-wide scan
|
|
28
|
+
canon ingest sessions --all-workspaces
|
|
29
|
+
# override the scan-root home ($HOME)
|
|
30
|
+
canon ingest sessions --home /custom
|
|
26
31
|
```
|
|
27
32
|
|
|
28
33
|
**Done when:** the run summary (below) ends with `malformed records … : 0`
|
|
@@ -175,7 +180,8 @@ These events interleave with `token_usage` events in one deterministic
|
|
|
175
180
|
order (directive-before-token on a timestamp tie). Query them with:
|
|
176
181
|
|
|
177
182
|
```bash
|
|
178
|
-
|
|
183
|
+
# filter detail.label == "user_directive"
|
|
184
|
+
canon query --kind event
|
|
179
185
|
```
|
|
180
186
|
|
|
181
187
|
## Reading the run summary
|
|
@@ -105,9 +105,9 @@ canon query --kind trajectory --json
|
|
|
105
105
|
|
|
106
106
|
# Page a long result and keep only the fields you need; `meta` carries
|
|
107
107
|
# `page` = {returned, total, nextCursor} and `fields` = the paths kept; a present `nextCursor` means more, and
|
|
108
|
-
# `meta.next` repeats the call with `--cursor
|
|
109
|
-
# call that issued it: change only `--limit`, `--all` or `--
|
|
110
|
-
# (`error.reason` `ARGUMENTS_CHANGED`). A field path is the record's own key, written as it is.
|
|
108
|
+
# `meta.next` repeats the call's explicit arguments with `--cursor=<nextCursor>` (`--all` returns everything). A cursor is bound to the
|
|
109
|
+
# call that issued it, `CANON_ROLE` included: change only `--limit`, `--all`, `--fields` or `--json`; any other change is `CURSOR_INVALID`
|
|
110
|
+
# (`error.reason` `ARGUMENTS_CHANGED`; `FOREIGN` for a cursor an older canon issued: start from the first page). A field path is the record's own key, written as it is.
|
|
111
111
|
canon query --kind trajectory --json --limit 50 --fields at,kind
|
|
112
112
|
canon query --kind trajectory --json --limit 50 --fields at,kind --cursor <nextCursor>
|
|
113
113
|
```
|
|
@@ -122,9 +122,12 @@ file names, tracked in git. `status`, the gate, the guards and `canon query`
|
|
|
122
122
|
read the ledger only.
|
|
123
123
|
|
|
124
124
|
```bash
|
|
125
|
-
|
|
126
|
-
canon
|
|
127
|
-
|
|
125
|
+
# every version, archive included
|
|
126
|
+
canon query --kind evidence --history
|
|
127
|
+
# preview; writes nothing
|
|
128
|
+
canon ledger compact
|
|
129
|
+
# pre-0.23 ledger or interrupted move; idempotent; `canon migrate` runs it
|
|
130
|
+
canon ledger compact --commit --plan-digest "$PLAN_DIGEST"
|
|
128
131
|
```
|
|
129
132
|
|
|
130
133
|
A crash between the write and the move leaves a readable ledger that the next
|
|
@@ -287,12 +290,12 @@ skills:
|
|
|
287
290
|
providers: [claude, codex] # any of claude, codex, omp, pi; [] for none
|
|
288
291
|
```
|
|
289
292
|
|
|
290
|
-
`canon init` writes it
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
293
|
+
`canon init` writes it and never asks: `--providers a,b` (or `none`) is the
|
|
294
|
+
answer; without it init takes the harnesses whose
|
|
295
|
+
`.claude`, `.codex`, `.omp` or `.pi` directory exists, and with none detected
|
|
296
|
+
it writes only a commented example
|
|
294
297
|
and installs nothing. `.agents` alone never selects Codex. `canon skills
|
|
295
|
-
install|check|doctor` follow the section; `install` prunes projected files the bundle no longer ships when their bytes match the lock (a modified one stays, reported as `kept:`), `check` exits 1 (`
|
|
298
|
+
install|check|doctor` follow the section; `install` prunes projected files the bundle no longer ships when their bytes match the lock (a modified one stays, reported as `kept:`), `check` exits 1 (`SKILLS_CHECK_FAILED`) naming each orphan and `doctor` prints `orphan:` lines; `canon skills install
|
|
296
299
|
--providers=<list>` rewrites it in place. Without the section they use the
|
|
297
300
|
install lock's providers, then the detected ones; nothing undetected is ever
|
|
298
301
|
added. A present section that is not this shape, or names another harness,
|
|
@@ -311,17 +314,22 @@ or when the index holds a scenario of a spec root that no `.feature` file
|
|
|
311
314
|
of it declares any more.
|
|
312
315
|
|
|
313
316
|
The commands that write records (`canon subject move`, `canon review
|
|
314
|
-
scenario`, `canon evidence add` and `canon artifacts store`) rebuild a stale index on disk
|
|
317
|
+
scenario`, `canon evidence add` and `canon artifacts store`) rebuild a stale index on disk, once nothing
|
|
318
|
+
refuses the call (a refused call writes nothing, whatever the index; until then they read the index as the
|
|
319
|
+
refresh would leave it, in memory): they
|
|
315
320
|
run `canon inventory sync`'s code on the changed files (validation still
|
|
316
|
-
covers the whole root) and print one stderr line
|
|
321
|
+
covers the whole root) and print one stderr line (in JSON mode the line is a `meta.warnings` entry,
|
|
322
|
+
never a line on stderr):
|
|
317
323
|
|
|
318
324
|
```stderr
|
|
319
|
-
canon artifacts store: refreshed the scenario index from 1 changed feature file(s) (.canon/specs/kind=feature/area=cart/add.feature): 1 record(s) written
|
|
325
|
+
stderr: canon artifacts store: refreshed the scenario index from 1 changed feature file(s) (.canon/specs/kind=feature/area=cart/add.feature): 1 record(s) written
|
|
320
326
|
```
|
|
321
327
|
|
|
322
328
|
The commands that only read (`canon gate check`, `canon status`) write
|
|
323
329
|
nothing: they grade a stale index as the refresh would leave it, built in
|
|
324
|
-
memory, and print no refresh line. `canon
|
|
330
|
+
memory, and print no refresh line. `canon subject show` is the one read
|
|
331
|
+
command that declares the refresh, as a cache effect (its `effectNote`), after the subject is found.
|
|
332
|
+
`canon query --kind scenario` and the
|
|
325
333
|
report panels read the index on disk as it is.
|
|
326
334
|
|
|
327
335
|
A deleted scenario is not erased: `canon inventory sync` — and a writer's
|
|
@@ -332,7 +340,7 @@ a live generation that supersedes the tombstone. A root that fails to
|
|
|
332
340
|
sync writes no tombstone either.
|
|
333
341
|
|
|
334
342
|
```stderr
|
|
335
|
-
canon artifacts store: refreshed the scenario index from 1 scenario(s) no longer in any feature file (cart.pay.01): 1 record(s) written
|
|
343
|
+
stderr: canon artifacts store: refreshed the scenario index from 1 scenario(s) no longer in any feature file (cart.pay.01): 1 record(s) written
|
|
336
344
|
```
|
|
337
345
|
|
|
338
346
|
`canon inventory sync` and every refresh hold an OS file lock on
|
|
@@ -350,7 +358,7 @@ the `verifying → shipped` guard refuses, and `canon status` lists only
|
|
|
350
358
|
`canon inventory sync`:
|
|
351
359
|
|
|
352
360
|
```stderr
|
|
353
|
-
canon artifacts store: found the scenario index stale for 1 changed feature file(s) (…): 0 record(s) written; NOT refreshed: root `root` (2 format violation(s), 0 sync error(s)) — `canon inventory sync` lists them
|
|
361
|
+
stderr: canon artifacts store: found the scenario index stale for 1 changed feature file(s) (…): 0 record(s) written; NOT refreshed: root `root` (2 format violation(s), 0 sync error(s)) — `canon inventory sync` lists them
|
|
354
362
|
```
|
|
355
363
|
|
|
356
364
|
Nothing is written in a directory without `canon.yaml`. `canon inventory
|
|
@@ -370,6 +378,26 @@ released when its holder exits, however it exits, so it never goes stale;
|
|
|
370
378
|
a writer gives up after 30 s with a message naming the file and saying to
|
|
371
379
|
retry (the command exits `2` and writes no record).
|
|
372
380
|
|
|
381
|
+
## The repository write lock
|
|
382
|
+
|
|
383
|
+
A committed destructive command (`canon purge`, `canon tier age`, `canon ledger
|
|
384
|
+
compact`, `canon artifacts move`, `canon migrate`) recomputes its plan, checks
|
|
385
|
+
its digest and then makes the effects, and nothing may change the plan in
|
|
386
|
+
between. It holds an OS file lock on `.canon/repo-write.lock` for the whole
|
|
387
|
+
span, from before it recomputes the plan to after its last effect; a preview
|
|
388
|
+
holds nothing. Every other command that writes what a plan reads (ledger
|
|
389
|
+
appends, the artifact store, briefs, specs, hooks, skill files, `canon.yaml`)
|
|
390
|
+
holds the same lock while it writes, so it waits for a commit and a commit
|
|
391
|
+
waits for it; reads, `--dry-run` runs and previews hold nothing. The lock is
|
|
392
|
+
the outermost one: it is taken before the scenario-index lock and the
|
|
393
|
+
artifact-store lock. It is released when its holder exits, however it exits,
|
|
394
|
+
so a killed commit never leaves it held, and the file is removed only by a
|
|
395
|
+
call that made it to take the lock and then ended refused (a refusal leaves
|
|
396
|
+
no file and no `.git/info/exclude` line behind). A
|
|
397
|
+
command that waited 30 s for it starts nothing: it exits `2` with
|
|
398
|
+
`REPO_LOCK_UNAVAILABLE` (retryable; run it again in a moment). The first
|
|
399
|
+
call that needs the lock makes the file, in any repository.
|
|
400
|
+
|
|
373
401
|
## What `canon init` keeps out of git
|
|
374
402
|
|
|
375
403
|
A fresh `canon init` writes these lines to `.gitignore` (each under a
|
|
@@ -384,6 +412,8 @@ comment, only the missing ones):
|
|
|
384
412
|
- `.canon/scenario-index.lock` — the lock serializing index rebuilds.
|
|
385
413
|
- `.canon/artifact-store.lock` — the lock serializing artifact store
|
|
386
414
|
writes.
|
|
415
|
+
- `.canon/repo-write.lock` — the lock a committed destructive command and
|
|
416
|
+
every writer of what its plan reads hold.
|
|
387
417
|
- `.canon/dashboard-snapshot/` — the snapshot `canon dashboard`
|
|
388
418
|
regenerates on every run.
|
|
389
419
|
- `.canon/learn/` and `.canon/r2/` — local learn scratch and the local
|
|
@@ -40,7 +40,8 @@ Write (or update) the Subject before touching anything else, so every
|
|
|
40
40
|
downstream artifact has a `subject_id` to pin to.
|
|
41
41
|
|
|
42
42
|
```bash
|
|
43
|
-
|
|
43
|
+
# record kinds, enum domains, policy requirements
|
|
44
|
+
canon context --repo .
|
|
44
45
|
canon subject new subject-domain-loop \
|
|
45
46
|
--domain dev --title "Subject domain loop" \
|
|
46
47
|
--summary "The durable product unit + its per-domain loop" \
|
|
@@ -70,7 +71,8 @@ canon subject new subject-domain-loop \
|
|
|
70
71
|
subjects at once: `--input` takes inline JSON, a file or `-`; an array (or
|
|
71
72
|
NDJSON) is a batch validated before any write, with `operands` holding the
|
|
72
73
|
id and the other keys the option names in camelCase (`canon schema subject
|
|
73
|
-
new --json` shows `input
|
|
74
|
+
new --json` shows `input`, its limits and a complete `example`; `canon schema subject new --schemas --json`
|
|
75
|
+
gives `Input`, the schema of one document). Writing an identical subject
|
|
74
76
|
again is a no-op reported `unchanged` (`data.unchanged`).
|
|
75
77
|
|
|
76
78
|
### 2. Retrieve subject-scoped strategy memory BEFORE working
|
|
@@ -130,12 +132,13 @@ from anywhere). Any other transition is rejected by failure class, exits
|
|
|
130
132
|
canon subject move subject-domain-loop --to specced
|
|
131
133
|
canon subject move subject-domain-loop --to building
|
|
132
134
|
canon subject move subject-domain-loop --to verifying
|
|
133
|
-
|
|
135
|
+
# gated — see below
|
|
136
|
+
canon subject move subject-domain-loop --to shipped
|
|
134
137
|
```
|
|
135
138
|
|
|
136
139
|
On success the status updates in place; `--json` prints the updated
|
|
137
140
|
record. On a gate block it prints violations by failure class to stderr,
|
|
138
|
-
exits
|
|
141
|
+
exits 2 (the code names the condition: `SUBJECT_APPROVAL_REQUIRED`, `EVIDENCE_STALE`, `LAYOUT_MIGRATION_REQUIRED` or `SUBJECT_INVALID`; several causes are `error.details[]`), and the record is unchanged.
|
|
139
142
|
|
|
140
143
|
**Corpus-wide, the same join:** `canon gate check` with
|
|
141
144
|
`.canon/policy.yaml`'s opt-in `spec_coverage` section generalizes this
|
|
@@ -164,7 +167,7 @@ from shipping, until every evidenced scenario has evidence recorded after
|
|
|
164
167
|
the close or the approval; the refusal names the scenarios and the
|
|
165
168
|
`canon evidence add --all-scenarios` command. Each refusal prints by
|
|
166
169
|
failure class (`uncovered-cell`), carries a `hint`,
|
|
167
|
-
exits
|
|
170
|
+
exits 2, and the status stays `verifying`. `retired` is not gated.
|
|
168
171
|
|
|
169
172
|
**The brief (0.16, 0.17, 0.19):** `verifying → shipped` also refuses where a brief
|
|
170
173
|
exists and is not approved, read exactly as `canon status` reads it (one
|
|
@@ -172,7 +175,7 @@ shared check, so status says `ship` only when this passes): the subject's
|
|
|
172
175
|
scope (every change the subject answers for, see below) has a canon `.canon/changes/<change>/brief.md`
|
|
173
176
|
with no `Approved:` line outside HTML comments and code fences, whose
|
|
174
177
|
`Approved:` line carries a digest the brief's text no longer matches
|
|
175
|
-
(`
|
|
178
|
+
(`BRIEF_APPROVAL_STALE`; the brief was edited after the human
|
|
176
179
|
answered), or has no canon `brief.md` yet but comes from an external
|
|
177
180
|
openspec plan with a `proposal.md` under the plan sources. It also
|
|
178
181
|
refuses while `canon brief check` has blocker findings for that change,
|
|
@@ -182,8 +185,8 @@ a policy. The refusal is an `unreviewed-promotion` naming the subject, and
|
|
|
182
185
|
says what to do:
|
|
183
186
|
|
|
184
187
|
```output
|
|
185
|
-
canon subject move:
|
|
186
|
-
canon subject move: 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
|
|
188
|
+
output: canon subject move: SUBJECT_INVALID — 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 c-demo --approver <the human> --note "<their words>"`
|
|
189
|
+
output: canon subject move: 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
|
|
187
190
|
```
|
|
188
191
|
|
|
189
192
|
For an external change with no canon brief yet, the reason reads
|
|
@@ -209,7 +212,7 @@ This refusal is a backstop, and so is the evidence one: shipping is not
|
|
|
209
212
|
the first thing an unapproved brief stops. The agent writes the brief,
|
|
210
213
|
runs `canon brief check` until it has no blockers, then shows it to the
|
|
211
214
|
human and asks, and nothing is built or attested for the subject until the human
|
|
212
|
-
answers. `canon evidence add` refuses (exit
|
|
215
|
+
answers. `canon evidence add` refuses (exit 2, nothing staged) a
|
|
213
216
|
scenario whose `@subject` owner, or a task whose change
|
|
214
217
|
an adopting subject, has an enforced, unapproved brief, through the same
|
|
215
218
|
check; see `canon-gate`.
|
|
@@ -221,7 +224,7 @@ check; see `canon-gate`.
|
|
|
221
224
|
edited since, and even if the tags were removed; or (3) a *chore*, which has
|
|
222
225
|
no scenario to tag and so governs from the moment it is adopted. They pass
|
|
223
226
|
only when every one of them reads as approved now; an unapproved one and one
|
|
224
|
-
edited after its approval (`
|
|
227
|
+
edited after its approval (`BRIEF_APPROVAL_STALE`) are named for
|
|
225
228
|
different reasons, and the answer does not depend on the order the changes
|
|
226
229
|
were adopted or approved. The ship guard's brief-structure check runs over
|
|
227
230
|
all of them. When several block, the hint is one
|
|
@@ -243,7 +246,7 @@ answers only to the changes its tags name (counting only changes the subject
|
|
|
243
246
|
adopted); another change's edit after its approval never blocks it. An untagged
|
|
244
247
|
scenario (one whose tags were removed reads the same) answers to every adopted
|
|
245
248
|
change with an `Approved:` line, a chore included, so an approved change edited
|
|
246
|
-
since refuses it (`
|
|
249
|
+
since refuses it (`BRIEF_APPROVAL_STALE`); an unapproved chore blocks
|
|
247
250
|
reopen and ship, never evidence. When no adopted change has an approved line,
|
|
248
251
|
a scenario with evidence recorded before the subject first adopted a change
|
|
249
252
|
(and without a brief override) needs no brief, and one without answers to the
|
|
@@ -259,7 +262,7 @@ When `.canon/policy.yaml` sets `spec_coverage.require_review` (see
|
|
|
259
262
|
prints on stderr which it ran or skipped:
|
|
260
263
|
|
|
261
264
|
```output
|
|
262
|
-
canon subject move: review guard for building → verifying (spec_coverage.require_review, scope: verifying, shipped)
|
|
265
|
+
output: canon subject move: review guard for building → verifying (spec_coverage.require_review, scope: verifying, shipped)
|
|
263
266
|
ran unreviewed-promotion — every owned scenario needs a review by an actor, and from a session, other than its evidence's
|
|
264
267
|
ran open-blocker — 1 adopted change(s) checked for open blocker findings
|
|
265
268
|
```
|
|
@@ -268,7 +271,7 @@ A target outside `require_review.scope` prints `skipped
|
|
|
268
271
|
unreviewed-promotion — `specced` is not in require_review.scope`. A
|
|
269
272
|
transition INTO an in-scope status with gaps — an owned scenario with no
|
|
270
273
|
qualifying review (`unreviewed-promotion`), or an open blocker finding
|
|
271
|
-
on an adopted change (`open-blocker`) — prints the violations, exits
|
|
274
|
+
on an adopted change (`open-blocker`) — prints the violations, exits 2,
|
|
272
275
|
leaves the record unchanged, and names `--override-reason`. `shipped`
|
|
273
276
|
also refuses a `false-success` (a scenario whose latest audit is
|
|
274
277
|
`refuted`) and, when `require_audit.scope` covers `shipped`, an owed
|
|
@@ -316,8 +319,8 @@ canon subject move demo-subject --to verifying \
|
|
|
316
319
|
```
|
|
317
320
|
|
|
318
321
|
```output
|
|
319
|
-
canon subject move: reopened `demo-subject` from shipped, recorded by `impl`: follow-up: the retry path leaks a file handle; keep the follow-up in its approved change `c-demo`, then scenario, evidence and review; shipping again runs every guard
|
|
320
|
-
canon subject move: demo-subject → verifying — written directly; nothing to promote
|
|
322
|
+
output: canon subject move: reopened `demo-subject` from shipped, recorded by `impl`: follow-up: the retry path leaks a file handle; keep the follow-up in its approved change `c-demo`, then scenario, evidence and review; shipping again runs every guard
|
|
323
|
+
output: canon subject move: demo-subject → verifying — written directly; nothing to promote
|
|
321
324
|
```
|
|
322
325
|
|
|
323
326
|
- `--reason "<one line>"` is required for `shipped → verifying` (exit 2
|
|
@@ -330,14 +333,14 @@ canon subject move: demo-subject → verifying — written directly; nothing to
|
|
|
330
333
|
- Inside the approved scope it needs no human: every change the subject
|
|
331
334
|
answers for (tagged, approved or chore) has an approved brief, or there
|
|
332
335
|
is no brief to answer.
|
|
333
|
-
Otherwise it refuses, exit
|
|
336
|
+
Otherwise it refuses, exit 2, naming the brief to approve, and
|
|
334
337
|
`--override-reason` does not waive it:
|
|
335
338
|
``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; …``.
|
|
336
339
|
- The review guard does not run on a reopen (it prints `skipped … — a
|
|
337
340
|
reopen moves the subject back to fix its gaps; shipping again runs
|
|
338
341
|
it`). `verifying → shipped` afterwards runs every guard above again.
|
|
339
|
-
- A policy that fails closed (`
|
|
340
|
-
`
|
|
342
|
+
- A policy that fails closed (`POLICY_APPROVAL_STALE`,
|
|
343
|
+
`POLICY_APPROVAL_CORRUPT`, `POLICY_APPROVAL_DENIED`: see `canon-policy`) refuses the reopen and
|
|
341
344
|
the ship, and `--override-reason` does not waive it.
|
|
342
345
|
- `canon change new` on a shipped subject prints the reopen command
|
|
343
346
|
(`canon subject move <id> --to verifying --reason "<why it reopens>"`)
|
|
@@ -353,9 +356,11 @@ Planning docs written outside canon become change/task records via
|
|
|
353
356
|
`canon subject adopt` lifts an imported change into a managed Subject:
|
|
354
357
|
|
|
355
358
|
```bash
|
|
356
|
-
|
|
359
|
+
# import → change/task rows
|
|
360
|
+
canon ingest plans --repo .
|
|
361
|
+
# link change → subject
|
|
357
362
|
canon subject adopt subject-domain-loop \
|
|
358
|
-
--change subject-domain-loop-plan
|
|
363
|
+
--change subject-domain-loop-plan
|
|
359
364
|
```
|
|
360
365
|
|
|
361
366
|
A change made with `canon change new` (below) is recorded and adopted
|
|
@@ -377,7 +382,7 @@ second subject adds that subject, never re-points the change, and says
|
|
|
377
382
|
so:
|
|
378
383
|
|
|
379
384
|
```output
|
|
380
|
-
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
|
|
385
|
+
output: 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
|
|
381
386
|
```
|
|
382
387
|
|
|
383
388
|
Every reader joins a subject to its changes from both sides (the
|
|
@@ -418,7 +423,7 @@ canon subject unadopt <S> --change <change> --json
|
|
|
418
423
|
|
|
419
424
|
Removes the link on both records and keeps the change and its brief, so the
|
|
420
425
|
draft can be adopted elsewhere (a `proposed` holding subject, say) while the
|
|
421
|
-
live subject keeps shipping. It is refused with `
|
|
426
|
+
live subject keeps shipping. It is refused with `SUBJECT_CONFLICT` (reason `APPROVED_BRIEF` or `FLOW_TAGGED`)
|
|
422
427
|
for a change whose brief carries an `Approved:` line, and while a scenario
|
|
423
428
|
the subject owns carries a `@flow:<change>.*` tag (it would fall back to an
|
|
424
429
|
older approval; remove or retag those scenarios first, the hint names them);
|
|
@@ -434,8 +439,8 @@ canon change new add-login --subject auth --title "Add login"
|
|
|
434
439
|
```
|
|
435
440
|
|
|
436
441
|
```output
|
|
437
|
-
canon change new: wrote .canon/changes/add-login/brief.md
|
|
438
|
-
canon change new: recorded change `add-login` adopted into subject `auth` — written directly; nothing to promote
|
|
442
|
+
output: canon change new: wrote .canon/changes/add-login/brief.md
|
|
443
|
+
output: canon change new: recorded change `add-login` adopted into subject `auth` — written directly; nothing to promote
|
|
439
444
|
```
|
|
440
445
|
|
|
441
446
|
- **Writes** canon's own change dir, `.canon/changes/<slug>/`, holding
|
|
@@ -493,8 +498,10 @@ brief (`data.next`, `meta.next`).
|
|
|
493
498
|
### The brief: `canon brief show`, `canon brief check`, `canon brief approve`
|
|
494
499
|
|
|
495
500
|
```text
|
|
496
|
-
|
|
497
|
-
canon brief
|
|
501
|
+
# state and the next one to three steps; never edits
|
|
502
|
+
canon brief show [<slug>]
|
|
503
|
+
# findings; exit 0 clean, 2 blockers, 1 error
|
|
504
|
+
canon brief check [<slug>] [--json]
|
|
498
505
|
canon brief approve <slug> <slug> --approver <the human> --note "<their words>"
|
|
499
506
|
```
|
|
500
507
|
|
|
@@ -503,12 +510,12 @@ same answer and one timestamp on every brief and on the policy approval
|
|
|
503
510
|
(0.31). Every slug is checked before anything is written (a brief that
|
|
504
511
|
exists and reads, no blocker, a one-line name and answer, a body that does not
|
|
505
512
|
end inside an open code fence); one refused slug refuses the call, exit 2
|
|
506
|
-
|
|
507
|
-
nothing is written. A repeated slug counts once. With one slug the output is as
|
|
513
|
+
with the first refused slug's own code (`BRIEF_INVALID`, `BRIEF_NOT_FOUND`) and an
|
|
514
|
+
`error.details` entry for each refused slug, and nothing is written. A repeated slug counts once. With one slug the output is as
|
|
508
515
|
before; with several, `--json` carries one entry per slug (`written`,
|
|
509
516
|
`unchanged`, `failed`, `skipped`). A disk error while writing exits 1 and
|
|
510
517
|
leaves the slugs already written approved. The call is human-only for every
|
|
511
|
-
slug; the `
|
|
518
|
+
slug; the `ACTOR_DENIED` hint gives the human one line with all the slugs.
|
|
512
519
|
|
|
513
520
|
Without the change operand, the repo's only live change is read. The brief is the
|
|
514
521
|
PRD `canon change new` writes; sections are found by their English `##`
|
|
@@ -518,7 +525,7 @@ before. A brief with `<!-- canon:… -->` comments or Korean headings is in the
|
|
|
518
525
|
0.30 format: `brief check` gives it one blocker (`brief.old-format`) and
|
|
519
526
|
`canon migrate` rewrites it; `status`, `gate check`, `context` and `brief show`
|
|
520
527
|
warn, and a command that writes and judges it is refused
|
|
521
|
-
(`
|
|
528
|
+
(`BRIEF_MIGRATION_REQUIRED`).
|
|
522
529
|
|
|
523
530
|
- **Blockers (structure, `brief.*`)**: a missing, duplicated or
|
|
524
531
|
out-of-order section; a flow with no steps or a duplicate step id;
|
|
@@ -543,7 +550,7 @@ warn, and a command that writes and judges it is refused
|
|
|
543
550
|
`Approved: <who> <when> — "<words>" (brief sha256:<digest>)` into the
|
|
544
551
|
brief and records the policy approval in the same call. It refuses
|
|
545
552
|
(exit 2) while the check has structure blockers. Edit the brief
|
|
546
|
-
afterwards and every reader says `
|
|
553
|
+
afterwards and every reader says `BRIEF_APPROVAL_STALE` until a
|
|
547
554
|
human answers again and `approve` runs again. Run it only from the
|
|
548
555
|
human's answer, with their words as `--note`.
|
|
549
556
|
|
|
@@ -567,8 +574,10 @@ step that writes it.
|
|
|
567
574
|
## Where are we: `canon status`
|
|
568
575
|
|
|
569
576
|
```bash
|
|
570
|
-
|
|
571
|
-
canon status
|
|
577
|
+
# subjects by status, their gaps, next commands
|
|
578
|
+
canon status
|
|
579
|
+
# the same report, stable shape
|
|
580
|
+
canon status --json
|
|
572
581
|
canon status --repo ../other
|
|
573
582
|
```
|
|
574
583
|
|
|
@@ -611,7 +620,7 @@ uses, so status and the gate cannot disagree about a gap:
|
|
|
611
620
|
`Approved: <who> <when> — "<the human's words>" (brief sha256:<digest>)`,
|
|
612
621
|
the digest covering the brief without its `Approved:` lines. A brief
|
|
613
622
|
edited after that reads `brief: not approved (<brief> changed after its
|
|
614
|
-
approval (
|
|
623
|
+
approval (BRIEF_APPROVAL_STALE))` and names the re-approval
|
|
615
624
|
command: the changed brief needs a new human answer. A line with no
|
|
616
625
|
digest (written before 0.19) stays approved, with an `advisory:` to
|
|
617
626
|
re-approve so it binds to the text. Fixes and follow-ups
|
|
@@ -818,7 +827,8 @@ and `canon gate check` exits `0`; read `data.subjects[]` for the counts and
|
|
|
818
827
|
|
|
819
828
|
```bash
|
|
820
829
|
canon query --kind subject --domain dev --status building
|
|
821
|
-
|
|
830
|
+
# renders the "Subjects" panel
|
|
831
|
+
canon report build --repo .
|
|
822
832
|
```
|
|
823
833
|
|
|
824
834
|
- `canon query --kind subject [--domain <d>] [--status <s>]` — the
|