@journeykit/canon 0.35.0 → 0.37.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 +41 -20
- package/dist/skills/reference/canon-authoring.md +3 -2
- package/dist/skills/reference/canon-gate.md +29 -14
- package/dist/skills/reference/canon-inventory.md +3 -1
- package/dist/skills/reference/canon-learn.md +7 -2
- package/dist/skills/reference/canon-policy.md +3 -3
- package/dist/skills/reference/canon-report-dashboard.md +5 -5
- package/dist/skills/reference/canon-review.md +12 -9
- package/dist/skills/reference/canon-storage.md +4 -4
- package/dist/skills/reference/canon-subject.md +25 -23
- package/dist/skills/reference/role-implementer.md +8 -6
- package/dist/skills/reference/role-orchestrator.md +22 -21
- package/dist/skills/reference/role-planner.md +7 -7
- package/dist/skills/reference/role-reviewer.md +8 -8
- 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 +9 -6
- 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 +3 -3
- package/man/canon-divergence-add.1 +4 -4
- package/man/canon-divergence-defer.1 +4 -4
- package/man/canon-divergence-list.1 +3 -3
- package/man/canon-divergence-resolve.1 +3 -3
- package/man/canon-divergence.1 +2 -2
- package/man/canon-evidence-add.1 +9 -9
- package/man/canon-evidence-approval-payload.1 +4 -4
- package/man/canon-evidence-audit.1 +7 -7
- package/man/canon-evidence.1 +2 -2
- package/man/canon-export.1 +4 -4
- package/man/canon-feature-new.1 +2 -2
- package/man/canon-feature.1 +2 -2
- package/man/canon-finding-add.1 +5 -5
- package/man/canon-finding-close.1 +4 -6
- 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 +5 -4
- 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 +8 -8
- package/man/canon-query.1 +3 -3
- 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 +4 -4
- package/man/canon-review-scenario.1 +4 -4
- 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 +3 -3
- package/man/canon-skills-doctor.1 +3 -3
- package/man/canon-skills-install.1 +3 -3
- package/man/canon-skills-show.1 +2 -2
- package/man/canon-skills.1 +2 -2
- package/man/canon-status.1 +2 -2
- package/man/canon-strategy-approval-payload.1 +3 -3
- package/man/canon-strategy-promote.1 +2 -2
- package/man/canon-strategy-rollback.1 +3 -3
- 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
package/dist/skills/SKILL.src.md
CHANGED
|
@@ -49,15 +49,15 @@ a feature, a one-line request included. A human is needed once per scope:
|
|
|
49
49
|
`canon brief approve <slug> <slug> --approver <their name> --note "<their words>" --relayed-via <ask|AskUserQuestion|chat> --actor <your actor id> --digest <slug>=<digest shown>`
|
|
50
50
|
(one or more slugs, each with its own `--digest`, one answer for all, nothing written if any is refused).
|
|
51
51
|
The `Approved:` line then carries `(relayed by <role> <actor> via <tool>)`.
|
|
52
|
-
A digest that no longer matches the brief is `
|
|
53
|
-
it again. Without `--relayed-via` an agent role gets `
|
|
52
|
+
A digest that no longer matches the brief is `RELAY_DIGEST_STALE`: show
|
|
53
|
+
it again. Without `--relayed-via` an agent role gets `ACTOR_DENIED`, and so
|
|
54
54
|
does a repo whose `canon.yaml` says `approval: { relay: deny }`: then the
|
|
55
55
|
human runs the call themselves. Never remove or unset your role
|
|
56
56
|
(`env -u CANON_ROLE`, dropping `--as`) to run it yourself.
|
|
57
57
|
Never edit the policy to make the gate pass: a policy that differs from
|
|
58
|
-
the approved one fails closed (`
|
|
58
|
+
the approved one fails closed (`POLICY_APPROVAL_STALE`, not
|
|
59
59
|
waivable); stop and ask the human. A brief edited after its
|
|
60
|
-
approval is not approved (`
|
|
60
|
+
approval is not approved (`BRIEF_APPROVAL_STALE`): it needs a new
|
|
61
61
|
answer. The ship guard and `canon evidence add` refuse an unapproved or
|
|
62
62
|
edited brief, but those are backstops: the stop itself is at step 1.
|
|
63
63
|
- **Approval is scoped.** Evidence for a scenario tagged `@flow:<change>.<F-id>`
|
|
@@ -120,14 +120,18 @@ the record carries, so a review of a working tree survives the commit made
|
|
|
120
120
|
from it; a review is judged by what it pins (the scenario and what its
|
|
121
121
|
evidence says), never by the commit or the code tree. Outside a git work
|
|
122
122
|
tree, or in a repo with no commits, a command that needs a commit refuses
|
|
123
|
-
`
|
|
123
|
+
`COMMIT_REQUIRED`.
|
|
124
124
|
|
|
125
125
|
### Evidence follows the code
|
|
126
126
|
|
|
127
127
|
After a chore approval or a reopen, a scenario whose latest evidence was
|
|
128
128
|
recorded on the current `codeTree` needs nothing new: a docs-only chore then
|
|
129
129
|
needs its change-level review alone. What counts as code is `code.exclude` in
|
|
130
|
-
`canon.yaml` (globs, default `docs/**` and `*.md`).
|
|
130
|
+
`canon.yaml` (globs, default `docs/**` and `*.md`). A chore approved over the
|
|
131
|
+
code as it still is (the `(code tree <id>)` of its `Approved:` line is the
|
|
132
|
+
current tree) asks for no evidence at all, whatever tree the evidence is on, and
|
|
133
|
+
`status` says so; an approval with no recorded tree follows the rule above.
|
|
134
|
+
When code did change,
|
|
131
135
|
`canon status`, `meta.next` and the refusal hint lead with one batch over
|
|
132
136
|
every report, for example
|
|
133
137
|
`canon evidence add --all-scenarios --report junit:a.xml --report junit:b.xml --role implementer`
|
|
@@ -181,7 +185,7 @@ names the stores not read.
|
|
|
181
185
|
|
|
182
186
|
canon cannot detect a role; you declare it. Pass `--as <role>` or set
|
|
183
187
|
`CANON_ROLE=planner|implementer|reviewer|orchestrator`; a flag wins over the
|
|
184
|
-
variable, and an unknown value is refused (`
|
|
188
|
+
variable, and an unknown value is refused (`ROLE_INVALID`).
|
|
185
189
|
|
|
186
190
|
- **A role is set, or you were spawned as one:** load only
|
|
187
191
|
`canon skills show role-<role>` and do that role's steps. `canon status`
|
|
@@ -211,17 +215,34 @@ A role is guidance, not enforcement: `--role` on `evidence add` and
|
|
|
211
215
|
your complete command set, every option of every leaf included. Run `canon schema <command> --json` only for a
|
|
212
216
|
command it does not list, or after a `USAGE` refusal (read `error.field`
|
|
213
217
|
and `error.expected` first). `canon schema --json` is the tree of every group and command
|
|
214
|
-
with its `effect` and `x-canon.roles` (`--as <role>` lists one role's, and `x-canon.as` names it);
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
218
|
+
with its `effect` and `x-canon.roles` (`--as <role>` lists one role's, and `x-canon.as` names it);
|
|
219
|
+
its `options` define once the options that two or more commands share (`--repo`, `--as`, `--actor`, …)
|
|
220
|
+
and a command names them. `canon schema <group> --json` lists a group's
|
|
221
|
+
commands; `canon schema <command> --json` is the call surface of one command and nothing
|
|
222
|
+
else: `options` (each name of a tree option, or an object with `valueType`, a typed
|
|
223
|
+
`default`, `possibleValues`, `env`, `flagOnly`), `operands`, `constraints`, `validators`, `errors`,
|
|
224
|
+
`outputs`, `input` (what one `--input` document is and a complete `example`) and `examples`.
|
|
225
|
+
A member at its default is absent. `--json` is always accepted, `--input` when the command
|
|
226
|
+
has `input`, `--fields` when an output has `fields`, and the codes `USAGE` (with canon's
|
|
227
|
+
finer `*_INVALID`/`*_REQUIRED` argument codes), `INTERNAL`, `INPUT_*`, `BATCH_INCOMPLETE`,
|
|
228
|
+
`PLAN_STALE` (with `--commit`) and `CURSOR_INVALID` (with `--cursor`) can come without
|
|
229
|
+
being listed. `--commit` and `--plan-digest` are listed on exactly the commands that offer
|
|
230
|
+
them: a command that lists `commit` previews on a bare run. `canon schema <command> --schemas --json`
|
|
231
|
+
gives the JSON Schema of each report in `schemas`, `Input` (the schema of one `--input`
|
|
232
|
+
document) and `rejects` (what each validator refuses, in order); `#/schemas/<Name>` in
|
|
233
|
+
the command document resolves there. The first line of every `--help` points at it.
|
|
234
|
+
- An option of the same name takes the same value in every command that has it, and `canon schema` declares it once. An id
|
|
235
|
+
(`--project`, `--role`, `--domain`) is a kebab slug: lowercase letters and digits, single hyphens between them. A name
|
|
236
|
+
(`--actor`, `--approver`, `--relayed-via`) and a sentence (`--reason`, `--summary`, `--override-reason`) are one line of text:
|
|
237
|
+
the white space around it is trimmed (what is recorded, and what a signature is made of, is the trimmed text), and a value
|
|
238
|
+
that is empty once trimmed, or has a control character (a tab, an escape) or a line break inside it, is refused with the
|
|
239
|
+
command's own argument code. `--round` and `--limit` start at 1, and `--regime` is the four-part key `retrieve` and
|
|
240
|
+
`dispatch begin` both take. `finding close` takes `fixed`, `rejected` or `deferred`, never `open`.
|
|
220
241
|
- Pass `--json` and read stdout as one document, nothing on stderr. Success is
|
|
221
242
|
`{"ok":true,"data":…,"meta":…}`; failure is
|
|
222
243
|
`{"ok":false,"error":{…},"meta":…}` (a check that fails keeps its report in
|
|
223
244
|
`data`). `meta` carries `tool`, `toolVersion`, `contract` (`atip/1`),
|
|
224
|
-
`contractVersion` (`1.
|
|
245
|
+
`contractVersion` (`1.5`), `schemaVersion`,
|
|
225
246
|
`command`, and when present `next`, `warnings` (`[{code, message}]`) and
|
|
226
247
|
`page`; a repeated write that changed nothing reports `data.unchanged: true`.
|
|
227
248
|
Absent members are omitted, never `null` or `[]` (a value you sent empty comes back as `received: ""`).
|
|
@@ -243,7 +264,7 @@ A role is guidance, not enforcement: `--role` on `evidence add` and
|
|
|
243
264
|
- **Exit codes:** only 0, 1 and 2 exist: 0 ok; 1 the command ran and its answer
|
|
244
265
|
is negative or incomplete (a failed check, a violation, `unverifiable` when
|
|
245
266
|
nothing was examined, a partly failed batch); 2 refused to run (usage,
|
|
246
|
-
`
|
|
267
|
+
`CONFIG_REQUIRED`, an id or file that does not exist, an invalid argument).
|
|
247
268
|
A refusal leaves the tree unchanged.
|
|
248
269
|
- Repeating a write is safe: a record that already exists with identical
|
|
249
270
|
content is reported `unchanged` (`data.unchanged`), not duplicated.
|
|
@@ -258,13 +279,13 @@ A role is guidance, not enforcement: `--role` on `evidence add` and
|
|
|
258
279
|
has blockers; the approval step appears only when the brief is clean and
|
|
259
280
|
unapproved.
|
|
260
281
|
- Page long lists with `--limit <n>` and `--cursor <nextCursor>` (`--all`
|
|
261
|
-
for everything); a cursor holds only for the call that issued it, so change nothing but `--limit`, `--all`, `--fields` and `--json`, and keep `CANON_ROLE` as it was (`CURSOR_INVALID` with `error.reason` `ARGUMENTS_CHANGED`
|
|
282
|
+
for everything); a cursor holds only for the call that issued it, so change nothing but `--limit`, `--all`, `--fields` and `--json`, and keep `CANON_ROLE` as it was; the next call is `meta.next[0].argv` as it stands (the arguments you gave and the cursor, never a default). An argument you add is a change even when it states the default (`--repo .`): canon binds an explicit default to the cursor (`CURSOR_INVALID` with `error.reason` `ARGUMENTS_CHANGED`, `error.field` `--cursor`; `FOREIGN` for a cursor of another command or another canon version: start again from the first page). `--limit`, `--cursor`, `--all` and `--fields` are flags: an `--input` document key of that name is `INPUT_INVALID` with `reason` `FLAG_ONLY_KEY`. Project fields with `--fields a,b`
|
|
262
283
|
(`status`, `export`, `query`, `finding list`, `divergence list`, `context`): the paths `canon schema <command> --json` lists under `outputs[].fields`, spelled as the answer spells them; `meta.fields` lists what was kept.
|
|
263
284
|
- `--override-reason <text>` is the only way to proceed past a waivable
|
|
264
285
|
refusal; it is recorded, echoed in `data.overrides[]` and listed by `status`
|
|
265
286
|
for the human. There is no `--force`. `brief approve`, `policy approve`,
|
|
266
287
|
`divergence accept` and the `migrate --commit` that re-binds `Approved:`
|
|
267
|
-
lines record a human's decision and refuse every agent role (`
|
|
288
|
+
lines record a human's decision and refuse every agent role (`ACTOR_DENIED`)
|
|
268
289
|
unless they record the explicit answer the human gave you, with
|
|
269
290
|
`--relayed-via <tool>` (show, ask, record, as for the brief; `canon skills
|
|
270
291
|
show role-planner`); never unset your role to run one.
|
|
@@ -281,8 +302,8 @@ before anything is written, and that check reads no state: an invalid element wr
|
|
|
281
302
|
its own code, `error.field` pointing at `/<i>/<key>` and no `data`. An element that fails when it runs (the object
|
|
282
303
|
it names does not exist) stops the batch: exit 1, `BATCH_INCOMPLETE`, `error.field` exactly `/<i>`, the element
|
|
283
304
|
`failed` with its own `error` (no `data`), the later ones `skipped`, and the earlier ones done (not undone). Re-running the same batch reports the finished elements
|
|
284
|
-
`unchanged`. A typo in a key is refused (`INPUT_INVALID`, `field` naming the key), never dropped; a document with several problems reports them all, the first as the error and every one in `error.details` in the order the document wrote its keys, and a value outside a closed set is `INPUT_INVALID` too (`reason` `INVALID_VALUE`, `field` the key), not `USAGE`. Every non-empty line of an NDJSON file is a document (a `#` line is not a comment). Integers compare by number: `1`, `"1"` and `--round +1` are one value,
|
|
285
|
-
`canon schema <command> --json` shows the leaf's `input
|
|
305
|
+
`unchanged`. A typo in a key is refused (`INPUT_INVALID`, `field` naming the key), never dropped; a document with several problems reports them all, the first as the error (`field` is the first `error.details` entry's) and every one in `error.details` in the order the document wrote its keys, and a value outside a closed set is `INPUT_INVALID` too (`reason` `INVALID_VALUE`, `field` the key), not `USAGE`. Every non-empty line of an NDJSON file is a document (a `#` line is not a comment). Integers compare by number: `1`, `"1"` and `--round +1` are one value, `"+1"`, `"0x1"` and `1.0` in a document are `INVALID_VALUE`, and an integer beyond ±9007199254740991 is refused as a number and as a string, in a document (`INVALID_VALUE`, `field` its pointer) and on the command line (`USAGE`). `--input` may give a required option and the command line only the operand; an option given in both with different values is `INPUT_CONFLICT` at the key's pointer. A repeatable option's none is the key left out: `[]` is refused (`INPUT_INVALID`, `reason` `INVALID_VALUE`, `field` the key's pointer), as is `[]` for any other option and for `operands`.
|
|
306
|
+
`canon schema <command> --json` shows the leaf's `input` (what one document is, and a complete `example`); `canon schema <command> --schemas --json` gives `Input`, the JSON Schema of one document.
|
|
286
307
|
|
|
287
308
|
```text
|
|
288
309
|
canon scenario new --input '[{"operands":["auth.login.01"],"title":"Logs in","case":"happy"},{"operands":["auth.login.02"],"title":"Wrong password","case":"failure"}]'
|
|
@@ -338,7 +359,7 @@ canon init --providers=claude,codex
|
|
|
338
359
|
canon skills install
|
|
339
360
|
# any subset or none; rewrites skills.providers
|
|
340
361
|
canon skills install --providers=claude,codex,omp,pi
|
|
341
|
-
# read-only drift check; exit 1 (
|
|
362
|
+
# read-only drift check; exit 1 (SKILLS_CHECK_FAILED) names each orphan, a projected file the bundle no longer ships
|
|
342
363
|
canon skills check
|
|
343
364
|
# diagnostics, incl. a stray canon projection for an unselected provider and a projection a formatter rewrote, and `orphan:` lines for projected files the bundle no longer ships; never deletes user files
|
|
344
365
|
canon skills doctor
|
|
@@ -114,8 +114,9 @@ Fix it and the next command indexes the files (see `canon-inventory`).
|
|
|
114
114
|
Many scenarios at once: `--input` takes inline JSON, a file or `-`. An
|
|
115
115
|
array (or NDJSON) is a batch, validated whole before anything is written;
|
|
116
116
|
each element holds `operands` (the scenario tag) and the other options as
|
|
117
|
-
camelCase keys. `canon schema scenario new --json` shows `input
|
|
118
|
-
|
|
117
|
+
camelCase keys. `canon schema scenario new --json` shows `input` (its limits and a complete
|
|
118
|
+
`example`); `canon schema scenario new --schemas --json` gives `Input`, the schema of one
|
|
119
|
+
document. There is no `--from`.
|
|
119
120
|
|
|
120
121
|
```text
|
|
121
122
|
canon scenario new --input '[{"operands":["checkout.cart.01"],"title":"Adding an item raises the count","subject":"checkout-core","lane":"behavior","case":"happy"},{"operands":["checkout.cart.04"],"title":"Adding an out-of-stock item is refused","subject":"checkout-core","lane":"behavior","case":"failure"}]'
|
|
@@ -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 task done <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"`) 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 (refused with `fabricated-evidence`, exit
|
|
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 (refused with `fabricated-evidence`, `EVIDENCE_NOTE_INVALID`, exit 2). |
|
|
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 — or closed `fixed` while an evidenced scenario of the subject has no evidence recorded after the close (re-verification). Subject string `<change_id>#<round>.<seq>`. See `canon-review`. |
|
|
33
33
|
| `false-success` | The latest audit record (`canon evidence 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
|
|
|
@@ -225,9 +225,9 @@ canon divergence accept cart.add.01 --project root --reason "known gap" --tracki
|
|
|
225
225
|
|
|
226
226
|
Accepting a scenario whose latest evidence is `divergent` as a known gap
|
|
227
227
|
limited by something outside the change is a human-only act: with a role
|
|
228
|
-
declared (`--as`, `CANON_ROLE`) the leaf is refused with `
|
|
228
|
+
declared (`--as`, `CANON_ROLE`) the leaf is refused with `ACTOR_DENIED`. It is
|
|
229
229
|
never a way to skip a fix.
|
|
230
|
-
Refused (`
|
|
230
|
+
Refused (`DIVERGENCE_CONFLICT` when the evidence is not divergent, `DIVERGENCE_DENIED` when the actor or session attested it) unless the latest verdict is
|
|
231
231
|
divergent and the session and actor differ from the evidence's. The record
|
|
232
232
|
pins that evidence: new evidence for the scenario, or `--expiry` passing,
|
|
233
233
|
ends it. While it holds, the ship guard counts the cell covered and
|
|
@@ -255,6 +255,17 @@ review of the chore itself (`canon review change <slug>`) before
|
|
|
255
255
|
`--to shipped`; a changed verdict or case set stales that scenario's
|
|
256
256
|
review as before (and a new `divergence accept`, where the gap remains).
|
|
257
257
|
|
|
258
|
+
A chore's approval records the code tree of the work tree at that moment, as
|
|
259
|
+
`(code tree <id>)` in its `Approved:` line (outside the brief digest; none
|
|
260
|
+
outside git or with no commit). While that tree is the current code tree the
|
|
261
|
+
chore changed no code since it was approved, so it asks for no evidence at
|
|
262
|
+
all, whatever tree each scenario's latest evidence is on, and `status` says so
|
|
263
|
+
on its line; its change-level review is still asked, and it must be a review of
|
|
264
|
+
this code (the review records the code tree it saw): a brief edited after the
|
|
265
|
+
approval needs a new review, and so does a review of other code, as when the
|
|
266
|
+
same brief is approved again over code that came after the review. An approval
|
|
267
|
+
with no recorded tree (written before 0.37) follows the rule above.
|
|
268
|
+
|
|
258
269
|
### Audit records (`canon evidence audit`)
|
|
259
270
|
|
|
260
271
|
canon never generates or runs an audit. An external evaluator does, and
|
|
@@ -304,10 +315,10 @@ never rides along into a confirming audit. `--drop-attachment` and
|
|
|
304
315
|
The approval is never assumed: the agent writes the brief, shows it to the
|
|
305
316
|
human and asks, and builds nothing for that scope until the human answers.
|
|
306
317
|
`canon evidence add` is the backstop for that rule (0.16 M7). It
|
|
307
|
-
refuses (exit `
|
|
318
|
+
refuses (exit `2`, nothing staged or stored) while a subject the record
|
|
308
319
|
attests for has an enforced, unapproved brief: a change the record
|
|
309
320
|
answers to (below) has a canon `.canon/changes/<change>/brief.md` with no `Approved:`
|
|
310
|
-
line, was edited after its approval (`
|
|
321
|
+
line, was edited after its approval (`BRIEF_APPROVAL_STALE`), or
|
|
311
322
|
there is no canon `brief.md` yet but the change comes from an
|
|
312
323
|
external openspec plan with a `proposal.md` under the plan sources (the
|
|
313
324
|
check `canon status` and the ship guard use; see
|
|
@@ -461,7 +472,7 @@ mentions. Each bound case is its own attachment with its name and
|
|
|
461
472
|
outcome. `--report-case <name>` (repeatable; a Rust test by its
|
|
462
473
|
function name) ADDS cases; it never hides the scenario's own. No match,
|
|
463
474
|
or a `--report-case` naming no case, refuses (exit 2); a `faithful`
|
|
464
|
-
verdict when ANY bound case failed or was skipped refuses (exit
|
|
475
|
+
verdict when ANY bound case failed or was skipped refuses (exit 2). Files
|
|
465
476
|
must be inside the repository, are recorded by relative path, and are
|
|
466
477
|
stored (see Bound evidence stays provable).
|
|
467
478
|
|
|
@@ -517,8 +528,9 @@ report (a JUnit test case's `[[ATTACHMENT|path]]` lines, which Playwright's
|
|
|
517
528
|
JUnit reporter writes; a Cucumber JSON embedding), so a test's screenshot
|
|
518
529
|
stays with its scenario. A file the report does not name goes with
|
|
519
530
|
`--artifact <scenario-id>=<path>`; a bare `--artifact <path>` in a batch is
|
|
520
|
-
refused (`
|
|
521
|
-
outside the repo or oversized is refused
|
|
531
|
+
refused (`EVIDENCE_ARGUMENT_CONFLICT`). A report attachment that is missing,
|
|
532
|
+
outside the repo or oversized is refused (`EVIDENCE_BINDING_NOT_FOUND`,
|
|
533
|
+
`EVIDENCE_BINDING_DENIED`, `EVIDENCE_BINDING_INVALID`).
|
|
522
534
|
|
|
523
535
|
It is all or nothing: a claimed `faithful` verdict over a failed case,
|
|
524
536
|
a fabrication marker in `--summary`, an unapproved brief for any
|
|
@@ -536,13 +548,14 @@ or `-`; an array (or NDJSON) is a batch, validated element by element before
|
|
|
536
548
|
the first write (a refusal writes nothing, exit 2). Keys are the option
|
|
537
549
|
names in camelCase plus `operands`: one object per scenario, with
|
|
538
550
|
`scenario`, `class`, `ref` and `role`. `canon schema evidence add --json`
|
|
539
|
-
shows `input
|
|
551
|
+
shows `input` (its limits and a complete `example`) and `canon schema evidence add --schemas --json`
|
|
552
|
+
gives `Input`, the schema of one document. Each element reports `written`,
|
|
540
553
|
`unchanged` or `failed` in `data.items[]`.
|
|
541
554
|
|
|
542
555
|
`--rev <rev>` names the commit the evidence ran against (default `HEAD`).
|
|
543
556
|
Evidence for uncommitted code is recorded with its `codeTree` and
|
|
544
557
|
`worktreeDirty`; outside git, or with no commit, the add is refused with
|
|
545
|
-
`
|
|
558
|
+
`COMMIT_REQUIRED`.
|
|
546
559
|
|
|
547
560
|
**Done when:** `canon gate check` shows no violation for the scenarios you
|
|
548
561
|
recorded evidence for; each record's own output line says whether it was
|
|
@@ -697,7 +710,7 @@ aggregates every record bound to the task, on one line:
|
|
|
697
710
|
(the latest `--summary`, or a default naming the latest passing record).
|
|
698
711
|
Every other path — missing evidence, a `Divergent` verdict, a fabricated
|
|
699
712
|
note on any of the task's records — leaves the row byte-unchanged and
|
|
700
|
-
exits `
|
|
713
|
+
exits `2` with the blocking violation on stderr. After a flip, the plan
|
|
701
714
|
source is re-ingested so the record store's task status agrees with the
|
|
702
715
|
checkbox (`canon query --kind task` reads `done`); a failed re-ingest is
|
|
703
716
|
a stderr warning naming `canon ingest plans`, never a failed flip.
|
|
@@ -734,7 +747,7 @@ Staging → committed: every well-formed record under
|
|
|
734
747
|
applies, assigned a monotonic per-(role, surface) `run_seq` (gap-free
|
|
735
748
|
within one invocation, continuing from the committed max), and moved into
|
|
736
749
|
the committed ledger. A malformed or unpartitionable candidate is refused
|
|
737
|
-
— exit `
|
|
750
|
+
— exit `2` (`PROMOTION_INVALID`; several are `error.details[]`), no `run_seq` consumed, the file left in place. A refusal carries no `data`. `--dry-run`
|
|
738
751
|
prints the plan (target path + assigned `run_seq` per candidate) without
|
|
739
752
|
writing or deleting.
|
|
740
753
|
|
|
@@ -789,8 +802,10 @@ It installs no git hook: copy the script into git's `pre-commit` hook
|
|
|
789
802
|
agent hook files and the script, also under `--dry-run`. In `--json`,
|
|
790
803
|
`installed` is true when an agent hook file changed and
|
|
791
804
|
`preCommitScriptWritten` when the script was written. A `--repo` that is not an
|
|
792
|
-
existing directory exits 2 with `
|
|
793
|
-
and creates nothing.
|
|
805
|
+
existing directory exits 2 with `HOOKS_REPO_INVALID` (also under `--dry-run`)
|
|
806
|
+
and creates nothing. When the install writes the first hook file and then cannot
|
|
807
|
+
write the second, it exits 1 with `HOOKS_INSTALL_INCOMPLETE`: the first file
|
|
808
|
+
stays, `meta.next` leads with `canon status`, then run the install again.
|
|
794
809
|
|
|
795
810
|
Prefer this over hand-editing `settings.json`/`hooks.json`.
|
|
796
811
|
|
|
@@ -164,7 +164,9 @@ specs:
|
|
|
164
164
|
whole root aborts, 0 writes, violations reported.
|
|
165
165
|
- **A root on the pre-0.17 layout** (a `.feature` file under
|
|
166
166
|
`<root>/features/`) → a `layout-grammar` violation at path `features`;
|
|
167
|
-
the root aborts, 0 writes, exit
|
|
167
|
+
the root aborts, 0 writes, exit 2 (`INVENTORY_INVALID`, each violation in
|
|
168
|
+
`error.details`; exit 1 `INVENTORY_INCOMPLETE` when an earlier root was
|
|
169
|
+
indexed first). Run `canon migrate`. An empty
|
|
168
170
|
leftover `features/` directory is not a refusal.
|
|
169
171
|
- **A duplicate `scenario_id`** within one root → that root aborts, 0
|
|
170
172
|
writes, reported as a sync error (a distinct lane from fmt violations).
|
|
@@ -52,7 +52,11 @@ The production activation path is a signed, paired-evaluation workflow:
|
|
|
52
52
|
`meta.next` names the human signing step. The `data` contains the evaluation digest, RFC3339 `approved_at`,
|
|
53
53
|
subject `strategy:<strategy_id>`, namespace
|
|
54
54
|
`canon-learning-approval-v1`, and `payload_hex`. `approval-payload` never
|
|
55
|
-
reads a private key and never sets `verified`.
|
|
55
|
+
reads a private key and never sets `verified`. `--approval-at` is an RFC 3339
|
|
56
|
+
date-time (`2026-10-02T12:00:00Z`; a date alone, white space around it and a
|
|
57
|
+
one-digit month are refused): canon never stamps it, so fix it once and give
|
|
58
|
+
the same value to the signature and to the record. The bytes hold the instant
|
|
59
|
+
as UTC to the nanosecond, so one instant written `Z` or `+09:00` is one payload.
|
|
56
60
|
6. Decode `payload_hex` to the exact canonical JSON bytes. The signed object
|
|
57
61
|
is:
|
|
58
62
|
|
|
@@ -190,7 +194,8 @@ canon strategy rollback <strategy_id> --reason "<reason>" --actor alice \
|
|
|
190
194
|
|
|
191
195
|
`--signature-file` is an armored SSH detached signature over the
|
|
192
196
|
`canon-learning-approval-v1` payload bound to
|
|
193
|
-
`strategy:<id>:rollback:<sha256(reason)>`, actor, and `--approval-at
|
|
197
|
+
`strategy:<id>:rollback:<sha256(reason)>`, actor, and `--approval-at` (the same
|
|
198
|
+
RFC 3339 date-time that was signed; another instant fails the signature).
|
|
194
199
|
`actor` MUST be listed in policy-pinned `approval.allowed_signers`; verification
|
|
195
200
|
uses that file and `ssh-keygen -Y verify`. Successful rollback writes durable
|
|
196
201
|
provenance, sets lifecycle `rolled_back`, and records the optional contradicting
|
|
@@ -76,7 +76,7 @@ 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
77
|
loop). `canon brief approve` records both in one call. `canon policy
|
|
78
78
|
approve` is a human's decision: with a role declared (`--as` or `CANON_ROLE`)
|
|
79
|
-
it is refused with `
|
|
79
|
+
it is refused with `ACTOR_DENIED` (exit 2, not retryable) unless it records the
|
|
80
80
|
answer the human gave in the conversation: show them `.canon/policy.yaml`, ask,
|
|
81
81
|
and on an explicit approval run it (the steps are in `canon skills show role-planner`) with `--relayed-via <tool>`, `--actor`,
|
|
82
82
|
`--approver` and `--note` (their name and words); the file then carries `relay`
|
|
@@ -117,8 +117,8 @@ unreviewed-promotion policy — policy changed since approval: .canon/policy.yam
|
|
|
117
117
|
```
|
|
118
118
|
|
|
119
119
|
`canon gate check` exits `1`; `canon subject move` (the ship guard and
|
|
120
|
-
the reopen) refuses with `
|
|
121
|
-
`
|
|
120
|
+
the reopen) refuses with `POLICY_APPROVAL_STALE`, or
|
|
121
|
+
`POLICY_APPROVAL_CORRUPT` (or `POLICY_APPROVAL_DENIED` when the file may not be read) for a broken approval, and
|
|
122
122
|
`--override-reason` does not waive either. A policy never approved stays
|
|
123
123
|
an advisory. `canon status` prints the same text as a `warning:` line (and
|
|
124
124
|
in `--json` `warnings`). The record names who approved which bytes and
|
|
@@ -143,7 +143,7 @@ canon dashboard --snapshot /tmp/canon-snap --port 8080
|
|
|
143
143
|
- Runs in any repo: the released binary carries the app (in canon's own
|
|
144
144
|
checkout a built `packages/dashboard/dist` is used instead). It prints the
|
|
145
145
|
panels URL and a briefs URL (`/briefs.html`), then serves until Ctrl-C.
|
|
146
|
-
A taken port fails with `
|
|
146
|
+
A taken port fails with `DASHBOARD_PORT_UNAVAILABLE`: pass `--port <n>` or
|
|
147
147
|
`--port 0`.
|
|
148
148
|
- Without the `duckdb` CLI on `PATH` the server still runs; the panels page
|
|
149
149
|
says why there is no snapshot and how to get one (it never shows demo
|
|
@@ -160,11 +160,11 @@ answer once and clicks Approve: one request carries every selected brief with
|
|
|
160
160
|
the sha256 of the file the page read, and records exactly what `canon brief
|
|
161
161
|
approve <id>+ --approver <name> --note <text>` records, the `Approved:` line
|
|
162
162
|
bound to the brief digest and the policy approval, for each. A brief that
|
|
163
|
-
changed after the page loaded (`
|
|
164
|
-
blockers refuses the whole request (409 `
|
|
165
|
-
refused brief): nothing is written and the page says to read it again. A request
|
|
163
|
+
changed after the page loaded (`BRIEF_PAGE_STALE`) or that has
|
|
164
|
+
blockers refuses the whole request (409, the first refused brief's own code such as `BRIEF_INVALID`, with an
|
|
165
|
+
`error.details` entry for each refused brief): nothing is written and the page says to read it again. A request
|
|
166
166
|
naming one brief the old way (`changeId`, `sha256`) is still accepted. The server is localhost only and
|
|
167
|
-
refuses requests from other origins (`
|
|
167
|
+
refuses requests from other origins (`DASHBOARD_REQUEST_DENIED`).
|
|
168
168
|
|
|
169
169
|
For an agent: when clean briefs are waiting, show them (`canon brief show
|
|
170
170
|
<slug> --body`), ask with the harness question tool, and tell the human they
|
|
@@ -89,7 +89,7 @@ in the JSON. Give the reason when you reject or defer: it is the only record
|
|
|
89
89
|
of why the finding was not fixed.
|
|
90
90
|
|
|
91
91
|
`--scenario` (repeatable, each a scenario the index knows; refused with
|
|
92
|
-
`
|
|
92
|
+
`SCENARIO_NOT_FOUND` otherwise) names the scenarios the finding is
|
|
93
93
|
about. After a `fixed` close, fresh evidence for those scenarios alone
|
|
94
94
|
re-verifies the fix; a finding naming none asks every evidenced scenario of
|
|
95
95
|
the subject, as before. `finding close` copies the list.
|
|
@@ -116,14 +116,15 @@ needs it or `--introduced-unknown` (never both). A revision that names no
|
|
|
116
116
|
commit is refused (exit `2`). A finding recorded against uncommitted code
|
|
117
117
|
is allowed: the record carries the `codeTree` it saw and `worktreeDirty`.
|
|
118
118
|
Outside git, or in a repository with no commit, the write is refused with
|
|
119
|
-
`
|
|
119
|
+
`COMMIT_REQUIRED`.
|
|
120
120
|
|
|
121
121
|
Many findings at once: `--input` takes inline JSON, a file or `-`; an array
|
|
122
122
|
(or NDJSON) is a batch validated before any write. Keys are the option
|
|
123
123
|
names in camelCase (`change`, `actor`, `resolutionRev`); `canon schema
|
|
124
|
-
finding add --json` shows `input
|
|
124
|
+
finding add --json` shows `input` (its limits and a complete `example`) and `canon schema finding add
|
|
125
|
+
--schemas --json` gives `Input`, the schema of one document. An identical
|
|
125
126
|
repeat is `unchanged` (`data.unchanged`) and writes nothing; a different
|
|
126
|
-
finding under an occupied key is `
|
|
127
|
+
finding under an occupied key is `FINDING_EXISTS`.
|
|
127
128
|
|
|
128
129
|
### A defect the author found in its own work
|
|
129
130
|
|
|
@@ -154,7 +155,7 @@ text in the summary, not a field.
|
|
|
154
155
|
|
|
155
156
|
| Disposition | Meaning |
|
|
156
157
|
|---|---|
|
|
157
|
-
| `open` | Raised and not yet dealt with. The default on `finding add`; `finding close
|
|
158
|
+
| `open` | Raised and not yet dealt with. The default on `finding add`; `finding close` refuses it (a defect that came back is a new finding). |
|
|
158
159
|
| `fixed` | Fixed by a commit. Carries `--resolution-rev <rev>` (default `HEAD`) naming a commit this repo holds; only `fixed` may carry one. |
|
|
159
160
|
| `rejected` | The reviewer's point was considered and declined. Needs `--reason`. |
|
|
160
161
|
| `deferred` | Accepted, but postponed to later work. Needs `--reason`. |
|
|
@@ -163,8 +164,10 @@ text in the summary, not a field.
|
|
|
163
164
|
|
|
164
165
|
Every reader takes the **latest version per natural key**
|
|
165
166
|
`(change_id, round, seq)` — the same fold the report's finding panel
|
|
166
|
-
uses. A blocker raised `open` and later closed `fixed` counts as fixed
|
|
167
|
-
|
|
167
|
+
uses. A blocker raised `open` and later closed `fixed` counts as fixed.
|
|
168
|
+
A finding is not reopened: `finding close` takes `fixed`, `rejected` or
|
|
169
|
+
`deferred` and refuses `--disposition open`; a defect that came back is a new
|
|
170
|
+
`finding add` (the next `--seq`).
|
|
168
171
|
|
|
169
172
|
## `canon review scenario`
|
|
170
173
|
|
|
@@ -197,7 +200,7 @@ saw (the tree with `.canon/`, the spec roots and `code.exclude` removed), and
|
|
|
197
200
|
allowed, and the commit made from it has the same code tree, so committing it
|
|
198
201
|
afterwards leaves the review as it was: a review is judged by its pins (the
|
|
199
202
|
scenario and what its evidence says), not by the commit. Outside git, or
|
|
200
|
-
with no commit, the write is refused with `
|
|
203
|
+
with no commit, the write is refused with `COMMIT_REQUIRED`. An identical review
|
|
201
204
|
written again is `unchanged` (`data.unchanged` under `--json`) and adds no
|
|
202
205
|
record.
|
|
203
206
|
|
|
@@ -475,7 +478,7 @@ stderr: canon subject move: review guard for building → verifying (spec_covera
|
|
|
475
478
|
|
|
476
479
|
A target status outside the scope prints `skipped unreviewed-promotion —
|
|
477
480
|
`specced` is not in require_review.scope`. A transition into an in-scope
|
|
478
|
-
status with gaps prints the violations, exits
|
|
481
|
+
status with gaps prints the violations, exits 2, leaves the record
|
|
479
482
|
unchanged, and names `--override-reason`.
|
|
480
483
|
|
|
481
484
|
`--override-reason "<one line>"` waives ONLY `unreviewed-promotion` and
|
|
@@ -192,7 +192,7 @@ section listing them and emits a matching stderr `WARN`.
|
|
|
192
192
|
|
|
193
193
|
## Retention, export, and access audit
|
|
194
194
|
|
|
195
|
-
`canon purge --kind <kind> --before <RFC3339> [--commit --plan-digest <planDigest>] [--repo <dir>] [--json]`
|
|
195
|
+
`canon purge --kind <kind> --before <RFC3339 date or date-time> [--commit --plan-digest <planDigest>] [--repo <dir>] [--json]`
|
|
196
196
|
is the canonical data-lifecycle boundary: the bare run previews: it prints the removal set and
|
|
197
197
|
its `planDigest` and deletes nothing; `--commit --plan-digest <planDigest>` deletes only if
|
|
198
198
|
the plan recomputed now has the same digest (`PLAN_STALE`, exit 2, when the
|
|
@@ -220,7 +220,7 @@ human's review and `--commit --plan-digest`), and an export's page state (`total
|
|
|
220
220
|
file and answers with `data: { output, kind, recordCount }`; `--dry-run`
|
|
221
221
|
answers with what it would write and writes nothing.
|
|
222
222
|
|
|
223
|
-
`canon export --kind <kind> [--before <RFC3339>] [--after <RFC3339>]
|
|
223
|
+
`canon export --kind <kind> [--before <RFC3339 date or date-time>] [--after <RFC3339 date or date-time>]
|
|
224
224
|
[--output <file>] [--json] [--limit <n> | --all] [--cursor <c>]` emits a versioned manifest containing source scope,
|
|
225
225
|
counts, digests, and redacted metadata. It does not authorize backend access;
|
|
226
226
|
credentials remain an operator prerequisite. New output files are mode
|
|
@@ -295,7 +295,7 @@ answer; without it init takes the harnesses whose
|
|
|
295
295
|
`.claude`, `.codex`, `.omp` or `.pi` directory exists, and with none detected
|
|
296
296
|
it writes only a commented example
|
|
297
297
|
and installs nothing. `.agents` alone never selects Codex. `canon skills
|
|
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 (`
|
|
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
|
|
299
299
|
--providers=<list>` rewrites it in place. Without the section they use the
|
|
300
300
|
install lock's providers, then the detected ones; nothing undetected is ever
|
|
301
301
|
added. A present section that is not this shape, or names another harness,
|
|
@@ -395,7 +395,7 @@ so a killed commit never leaves it held, and the file is removed only by a
|
|
|
395
395
|
call that made it to take the lock and then ended refused (a refusal leaves
|
|
396
396
|
no file and no `.git/info/exclude` line behind). A
|
|
397
397
|
command that waited 30 s for it starts nothing: it exits `2` with
|
|
398
|
-
`
|
|
398
|
+
`REPO_LOCK_UNAVAILABLE` (retryable; run it again in a moment). The first
|
|
399
399
|
call that needs the lock makes the file, in any repository.
|
|
400
400
|
|
|
401
401
|
## What `canon init` keeps out of git
|