@journeykit/canon 0.15.0 → 0.17.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 +100 -34
- package/dist/skills/reference/canon-authoring.md +11 -4
- package/dist/skills/reference/canon-fmt.md +2 -2
- package/dist/skills/reference/canon-gate.md +181 -16
- package/dist/skills/reference/canon-inventory.md +75 -10
- package/dist/skills/reference/canon-plan-import.md +56 -17
- package/dist/skills/reference/canon-policy.md +38 -11
- package/dist/skills/reference/canon-review.md +82 -15
- package/dist/skills/reference/canon-storage.md +144 -1
- package/dist/skills/reference/canon-subject.md +223 -52
- package/package.json +4 -4
|
@@ -20,11 +20,13 @@ authored in one place:
|
|
|
20
20
|
- **Changes**: `change_ids` on the Subject, appended by `canon subject
|
|
21
21
|
adopt`.
|
|
22
22
|
- **Scenarios**: the `@subject:<id>` tag on each Gherkin scenario,
|
|
23
|
-
indexed onto `Scenario.subject_id` by
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
23
|
+
indexed onto `Scenario.subject_id` by the scenario index (rebuilt by
|
|
24
|
+
`canon inventory sync`, and refreshed by every command that reads it).
|
|
25
|
+
The Subject record carries no scenario list — the spec corpus is the
|
|
26
|
+
only source, and the Subjects report panel, `spec_coverage.scope`, and
|
|
27
|
+
the shipped gate all read it from there. To link a scenario, tag it;
|
|
28
|
+
the next `canon status` or `canon gate check` indexes the tag. There is
|
|
29
|
+
no subject-side command for it.
|
|
28
30
|
|
|
29
31
|
## The 4-step loop
|
|
30
32
|
|
|
@@ -81,9 +83,10 @@ canon retrieve --role dev --domain dev --subject subject-domain-loop --k 5
|
|
|
81
83
|
### 3. Work → pin every artifact to `subject_id`
|
|
82
84
|
|
|
83
85
|
- **Scenarios**: `canon scenario new <tag> --subject <subject-id>` emits
|
|
84
|
-
the `@subject:<subject-id>` tag;
|
|
85
|
-
|
|
86
|
-
|
|
86
|
+
the `@subject:<subject-id>` tag; the scenario index maps the tag onto
|
|
87
|
+
the scenario's `subject_id` the next time a command reads it (no sync
|
|
88
|
+
step; see `canon-inventory`). A malformed/absent tag leaves it unset
|
|
89
|
+
(fail-soft), never an error. See `canon-authoring`.
|
|
87
90
|
- **Changes**: adopt imported plan changes under the Subject (see
|
|
88
91
|
[Adopt flow](#adopt-flow)).
|
|
89
92
|
- **Reviews / evidence / divergences**: authored as usual (`canon review
|
|
@@ -145,6 +148,48 @@ empty set; so is one whose `scenario` records route away from the
|
|
|
145
148
|
gate's rung. Each refusal prints by failure class (`uncovered-cell`),
|
|
146
149
|
exits 1, and the status stays `verifying`. `retired` is not gated.
|
|
147
150
|
|
|
151
|
+
**The brief (0.16, 0.17):** `verifying → shipped` also refuses where a brief
|
|
152
|
+
exists and is not approved, read exactly as `canon status` reads it (one
|
|
153
|
+
shared check, so status says `ship` only when this passes): the subject's
|
|
154
|
+
newest adopted change has a canon `.canon/changes/<change>/brief.md`
|
|
155
|
+
with no `Approved:` line outside HTML comments and code fences, or has no
|
|
156
|
+
canon `brief.md` yet but comes from an external openspec plan with a
|
|
157
|
+
`proposal.md` under the plan sources. It runs with or without a policy.
|
|
158
|
+
The refusal is an `unreviewed-promotion` naming the subject, and says
|
|
159
|
+
what to do:
|
|
160
|
+
|
|
161
|
+
```
|
|
162
|
+
canon subject status: unreviewed-promotion demo-subject — verifying → shipped: brief not approved — .canon/changes/c-demo/brief.md has no `Approved:` line; a human must answer the brief, and only from that answer the agent will record their words as `Approved: <who> <when> — "<their words>"` in .canon/changes/c-demo/brief.md
|
|
163
|
+
canon subject status: refused — get the human's answer to the brief and record it as its `Approved:` line, or pass --override-reason <text> to move anyway; the waiver is recorded on the subject
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
For an external change with no canon brief yet, the reason reads
|
|
167
|
+
``.canon/changes/<c>/brief.md does not exist yet: change `<c>` comes from
|
|
168
|
+
the external plan <proposal>, which canon reads and never edits, so its
|
|
169
|
+
approval lives in that canon file`` and the agent will ``create
|
|
170
|
+
.canon/changes/<c>/brief.md holding a `Source: <proposal>` line and
|
|
171
|
+
record their words there as `Approved: <who> <when> — "<their words>"` ``.
|
|
172
|
+
|
|
173
|
+
Never write the line without the human's answer. When there is no brief to
|
|
174
|
+
answer (no adopted change, or a newest change with neither a canon
|
|
175
|
+
`brief.md` nor an external `proposal.md`), the move is not refused: it
|
|
176
|
+
prints ``canon subject status: advisory: <id>'s brief is not approved —
|
|
177
|
+
its newest adopted change `<c>` has no brief (no
|
|
178
|
+
.canon/changes/<c>/brief.md, no openspec proposal.md), so there is no
|
|
179
|
+
brief to answer and shipping is not refused`` (or `— <id> adopts no
|
|
180
|
+
change, so …`), and status shows the brief as not approved, as an
|
|
181
|
+
advisory. `subjects[].brief.enforced` in `canon status --json` says
|
|
182
|
+
which case applies. A brief that says the human ships changes who runs
|
|
183
|
+
the transition, not the check.
|
|
184
|
+
|
|
185
|
+
This refusal is a backstop, and so is the evidence one: shipping is not
|
|
186
|
+
the first thing an unapproved brief stops. The agent writes the brief,
|
|
187
|
+
then stops and reports it, and nothing is built or attested for the
|
|
188
|
+
subject until the human answers. `canon evidence add` refuses (exit 1,
|
|
189
|
+
nothing staged) a scenario whose `@subject` owner, or a task whose change
|
|
190
|
+
an adopting subject, has an enforced, unapproved brief, through the same
|
|
191
|
+
check; see `canon-gate`.
|
|
192
|
+
|
|
148
193
|
### The review guard (`spec_coverage.require_review`, opt-in)
|
|
149
194
|
|
|
150
195
|
When `.canon/policy.yaml` sets `spec_coverage.require_review` (see
|
|
@@ -172,9 +217,11 @@ canon subject status demo-subject verifying \
|
|
|
172
217
|
--override-reason "reviewer out until Monday" --actor-id lead
|
|
173
218
|
```
|
|
174
219
|
|
|
175
|
-
`--override-reason "<one line>"` waives ONLY `unreviewed-promotion`
|
|
176
|
-
`open-blocker` — never a ship-gate
|
|
177
|
-
|
|
220
|
+
`--override-reason "<one line>"` waives ONLY `unreviewed-promotion` (the
|
|
221
|
+
review gaps and the brief check) and `open-blocker` — never a ship-gate
|
|
222
|
+
verdict or case gap, never unreadable routing. A waived brief is recorded
|
|
223
|
+
as `{ class: unreviewed-promotion, subject: <subject id> }`. It prints
|
|
224
|
+
the `waived ...` lines and `override recorded by
|
|
178
225
|
`lead` for the N violation(s) above: <reason>`, and records on the new
|
|
179
226
|
Subject record exactly the violations it let through:
|
|
180
227
|
|
|
@@ -192,13 +239,14 @@ subject's next status write, so it never outlives the status it was
|
|
|
192
239
|
granted for. While it stands, `canon gate check` lists the recorded gaps
|
|
193
240
|
as advisories instead of failing; a gap it did not record (a blocker
|
|
194
241
|
raised later, a scenario tagged later) still fails the gate. Without
|
|
195
|
-
`require_review`, nothing is printed and nothing changes
|
|
242
|
+
`require_review`, nothing is printed and nothing changes, except that
|
|
243
|
+
`shipped` still runs the brief check above.
|
|
196
244
|
|
|
197
245
|
## Adopt flow
|
|
198
246
|
|
|
199
|
-
Planning docs become change/task records via
|
|
200
|
-
`canon-plan-import`), but that stops at import.
|
|
201
|
-
lifts an imported change into a managed Subject:
|
|
247
|
+
Planning docs written outside canon become change/task records via
|
|
248
|
+
`canon ingest plans` (see `canon-plan-import`), but that stops at import.
|
|
249
|
+
`canon subject adopt` lifts an imported change into a managed Subject:
|
|
202
250
|
|
|
203
251
|
```bash
|
|
204
252
|
canon ingest plans --repo . # import → change/task rows
|
|
@@ -206,6 +254,13 @@ canon subject adopt subject-domain-loop-plan \
|
|
|
206
254
|
--subject subject-domain-loop # link change → subject
|
|
207
255
|
```
|
|
208
256
|
|
|
257
|
+
A change made with `canon change new` (below) is recorded and adopted
|
|
258
|
+
already, and editing its `tasks.md` needs no import either:
|
|
259
|
+
`canon evidence add --task` and `canon gate task` read the file as it is,
|
|
260
|
+
and a flip re-imports that change itself. Run `canon ingest plans` for a
|
|
261
|
+
plan written outside canon, or when `canon query --kind task` must list
|
|
262
|
+
rows nobody has flipped yet.
|
|
263
|
+
|
|
209
264
|
`adopt` stamps the change's `subject_id` and adds it to the Subject's
|
|
210
265
|
`change_ids`, so `canon query --kind change --change-id …` and `canon
|
|
211
266
|
query --kind subject` agree on the link from both ends. It writes only
|
|
@@ -222,21 +277,33 @@ it.
|
|
|
222
277
|
|
|
223
278
|
### Starting a new change: `canon change new`
|
|
224
279
|
|
|
225
|
-
For a change that does not exist yet, one command
|
|
226
|
-
|
|
280
|
+
For a change that does not exist yet, one command writes it and records
|
|
281
|
+
it adopted:
|
|
227
282
|
|
|
228
283
|
```bash
|
|
229
284
|
canon change new add-login --subject auth --title "Add login"
|
|
230
285
|
```
|
|
231
286
|
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
287
|
+
```
|
|
288
|
+
canon change new: wrote .canon/changes/add-login/brief.md
|
|
289
|
+
canon change new: wrote .canon/changes/add-login/tasks.md
|
|
290
|
+
canon change new: recorded change `add-login` adopted into subject `auth` — written directly; nothing to promote
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
- **Writes** canon's own change dir, `.canon/changes/<slug>/`:
|
|
294
|
+
`brief.md` (`# <title>`, `## Why` holding the title, which imports as
|
|
295
|
+
the change summary, `## What Changes` with a comment saying what goes
|
|
296
|
+
there, and a comment naming where the human's `Approved:` line goes,
|
|
297
|
+
so the file starts unapproved) and `tasks.md` (no rows yet, so the
|
|
298
|
+
change imports as `proposed`; the openspec checkbox row grammar).
|
|
299
|
+
`.canon/changes/` is canon's native plan source: every plan reader
|
|
300
|
+
reads it with no `plans:` entry, so `canon.yaml` needs no edit and no
|
|
301
|
+
external plans source is needed (see `canon-plan-import`).
|
|
302
|
+
- **Reads** the files with the native `canon` plan adapter `canon ingest
|
|
238
303
|
plans` uses, then **records** the change already adopted, through the
|
|
239
|
-
same write as `canon subject adopt`.
|
|
304
|
+
same write as `canon subject adopt`. Its records carry the actor
|
|
305
|
+
`canon-plan-import-canon` (canon-written); an external openspec
|
|
306
|
+
change's carry `canon-plan-import-openspec`.
|
|
240
307
|
- **What a failure leaves behind.** The files are staged under `.canon/`
|
|
241
308
|
and the change dir is moved into place only once they parse. A failure
|
|
242
309
|
before any record is written (staging, publishing the dir, or the
|
|
@@ -248,12 +315,22 @@ canon change new add-login --subject auth --title "Add login"
|
|
|
248
315
|
plans && canon subject adopt <slug> --subject <id>`, which completes
|
|
249
316
|
the link and is safe to rerun.
|
|
250
317
|
- **Refuses** with exit `2`, writing nothing, when the subject does not
|
|
251
|
-
exist, when the slug already has a
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
Add task rows to `tasks.md` as `- [ ] <n> <title>`,
|
|
255
|
-
|
|
256
|
-
|
|
318
|
+
exist, or when the slug already has a `.canon/changes/<slug>` dir
|
|
319
|
+
(active or under `.canon/changes/archive/`) or a `change` record.
|
|
320
|
+
|
|
321
|
+
Add task rows to `tasks.md` as `- [ ] <n> <title>`, and flip each with
|
|
322
|
+
`canon gate task <slug>#<n>` once evidence exists. `canon evidence add
|
|
323
|
+
--task` and the flip read `tasks.md` as it is, so a new row needs no
|
|
324
|
+
`canon ingest plans` run.
|
|
325
|
+
|
|
326
|
+
A change from an external plan (an openspec change listed under
|
|
327
|
+
`plans.sources`, imported by `canon ingest plans` and adopted with
|
|
328
|
+
`canon subject adopt`) gets its brief as a canon file too:
|
|
329
|
+
`.canon/changes/<change>/brief.md` holding a `Source: <path>` line that
|
|
330
|
+
points at the external document and, once the human answers, the
|
|
331
|
+
`Approved:` line. Canon never edits the external document. Such a
|
|
332
|
+
pointer `brief.md` is not a change of its own: the native reader skips
|
|
333
|
+
it, so the external source keeps the change, its summary and its tasks.
|
|
257
334
|
|
|
258
335
|
## Where are we: `canon status`
|
|
259
336
|
|
|
@@ -286,7 +363,36 @@ uses, so status and the gate cannot disagree about a gap:
|
|
|
286
363
|
stale or whose latest reviews are all `indeterminate`/`unavailable`
|
|
287
364
|
(see `canon-review`), and the `canon review add` step names both. With
|
|
288
365
|
`require_review` on, a `warning:` line counts the in-effect reviews that
|
|
289
|
-
predate review pinning (0.15)
|
|
366
|
+
predate review pinning (0.15), checked against the commit each pinned:
|
|
367
|
+
current, stale (named) and undecidable.
|
|
368
|
+
- **Brief** (0.16, 0.17) — for each `building`, `verifying` or `shipped`
|
|
369
|
+
subject, and each `proposed` subject whose newest adopted change has a
|
|
370
|
+
brief (a canon `brief.md`, or an external `proposal.md`), a `brief:`
|
|
371
|
+
line under it says whether the brief is approved:
|
|
372
|
+
`brief: approved in <brief> — <the recorded words>`, or
|
|
373
|
+
`brief: not approved (…)`. Approval is an `Approved:` line in
|
|
374
|
+
`.canon/changes/<change>/brief.md` for the subject's newest adopted
|
|
375
|
+
change (the last one adopted; a live dir before an archived one), and
|
|
376
|
+
only there: a line that starts with `Approved:` (indent allowed), says
|
|
377
|
+
something after it, and sits outside HTML comments (`<!-- … -->`,
|
|
378
|
+
multi-line included) and fenced code blocks (```` ``` ```` or `~~~`),
|
|
379
|
+
written by the agent once the human answers, as
|
|
380
|
+
`Approved: <who> <when> — "<the human's words>"`. Fixes and follow-ups
|
|
381
|
+
live as tasks in the approved change, so a newly adopted change is a
|
|
382
|
+
new scope: an older change's approval does not cover it. A change from
|
|
383
|
+
an external plan has its brief in the same canon file, with a
|
|
384
|
+
`Source: <path>` line pointing at the external document; an `Approved:`
|
|
385
|
+
line inside an external `proposal.md` is not read. Before that canon
|
|
386
|
+
file exists, an external openspec `proposal.md` under the plan sources
|
|
387
|
+
(the first source holding the change; with no `plans:` section, the
|
|
388
|
+
`openspec` source rooted at the repo that `canon gate task` also falls
|
|
389
|
+
back to) still makes the brief one to answer, and the line reads
|
|
390
|
+
``brief: not approved (.canon/changes/<c>/brief.md does not exist yet:
|
|
391
|
+
change `<c>` comes from the external plan <proposal>, which canon reads
|
|
392
|
+
and never edits, so its approval lives in that canon file)``. With a
|
|
393
|
+
canon brief and no line it reads
|
|
394
|
+
``brief: not approved (<brief> has no `Approved:` line)``.
|
|
395
|
+
There is no record kind for it; the file is the record.
|
|
290
396
|
- **Unowned** — scenarios with no `@subject:` tag.
|
|
291
397
|
- **Audits** — how many scenarios are sampled for
|
|
292
398
|
`spec_coverage.require_audit`, confirmed, owed (no audit or
|
|
@@ -296,23 +402,62 @@ uses, so status and the gate cannot disagree about a gap:
|
|
|
296
402
|
one).
|
|
297
403
|
- **Dropped bindings** — `dropped bindings (N):` lists every recorded
|
|
298
404
|
`--drop-attachment` (scenario, paths, reason); `drops[]` in `--json`.
|
|
299
|
-
-
|
|
405
|
+
- **Brief overrides** — a `warning:` line per evidence record attested
|
|
406
|
+
with `canon evidence add --override-reason` while its subject's brief
|
|
407
|
+
was not approved: what it attests, who, when, the subject and the
|
|
408
|
+
reason (see `canon-gate`).
|
|
409
|
+
- **`next:`** — at most five steps, each with a one-line reason; the
|
|
300
410
|
rest are counted. Rules, in order: no `canon.yaml` → `canon init`; an
|
|
301
411
|
unreadable one → `canon init --check-config`; a scenario tagged with a
|
|
302
412
|
subject that has no record → `canon subject new <that id>`; no subject
|
|
303
|
-
at all → `canon subject new`; subjects
|
|
304
|
-
`canon inventory sync
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
`canon
|
|
311
|
-
`
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
413
|
+
at all → `canon subject new`; subjects and `.feature` scenarios but no
|
|
414
|
+
scenario records → `canon inventory sync` (status refreshes the index
|
|
415
|
+
itself first, so this means the refresh refused on a format violation
|
|
416
|
+
or a duplicate id, which the sync names). Then per subject, verifying
|
|
417
|
+
first, then building, specced, proposed, shipped: an unapproved brief
|
|
418
|
+
(building and later, and proposed once its change has a brief)
|
|
419
|
+
→ the human step below, which is then the subject's
|
|
420
|
+
only step; no scenario → `canon scenario new … --subject <id>` (and, when no
|
|
421
|
+
`.feature` file holds a scenario yet, `canon feature new` first, unless
|
|
422
|
+
every subject waits on its brief); a missing case → `canon scenario new
|
|
423
|
+
<surface>.<next nn> … --case <case>`; (building and later) an
|
|
424
|
+
unevidenced or divergent scenario → `canon evidence add --scenario-id
|
|
425
|
+
…` (its reason adds "then `canon gate promote`" only under
|
|
426
|
+
`write.promote: manual`); an open blocker → `canon finding close …`; a
|
|
427
|
+
due, unreviewed, evidenced scenario → `canon review add …` from another
|
|
428
|
+
session; none of these → `canon subject status <id> <next status>`.
|
|
429
|
+
Last, unowned scenarios → `canon status`, after tagging them in their
|
|
430
|
+
`.feature` files (status re-indexes the edit; no sync step). With more
|
|
431
|
+
than one `specs.roots[]` entry, every suggested `canon scenario new`
|
|
432
|
+
carries `--project`: the owning root for a missing case, `<root-id>`
|
|
433
|
+
(the reason lists the roots) for a subject that owns no scenario yet.
|
|
434
|
+
- **The one human step.** Every step is the agent's to run except one:
|
|
435
|
+
a subject whose brief is enforced (its newest change's canon
|
|
436
|
+
`brief.md` has no `Approved:` line, or it has no canon `brief.md` yet
|
|
437
|
+
and comes from an external openspec `proposal.md`) gets
|
|
438
|
+
`human: stop and report the brief in <brief>; build and attest nothing for <id> until the human answers (one answer approves brief and policy)`,
|
|
439
|
+
where `<brief>` is the canon `brief.md`, or the external `proposal.md`
|
|
440
|
+
when no canon brief exists yet. Its reason reads ``<id>: <why
|
|
441
|
+
unanswered> (an older change's approval does not cover a new scope),
|
|
442
|
+
and the approval is never assumed: `canon evidence add` refuses <id>'s
|
|
443
|
+
scenarios and tasks until it is recorded; once the human answers,
|
|
444
|
+
<record> and run `canon policy approve --by <the human> --note "<their
|
|
445
|
+
words>"` ``, where `<record>` is ``record their words as `Approved: <who>
|
|
446
|
+
<when> — "<their words>"` in <brief>``, or, for an external change,
|
|
447
|
+
``create <brief> holding a `Source: <proposal>` line and record their
|
|
448
|
+
words there as `Approved: <who> <when> — "<their words>"` ``.
|
|
449
|
+
It is the subject's only step, whatever its status: nothing suggests
|
|
450
|
+
`canon feature new`, `canon scenario new`, `canon evidence add`, a
|
|
451
|
+
review or a move-on for it before the human answers, and
|
|
452
|
+
`canon subject status <id> shipped` refuses on the same check. The
|
|
453
|
+
approval is never assumed. A subject with no brief to answer gets no human step;
|
|
454
|
+
its `brief:` line says
|
|
455
|
+
`not approved (no adopted change; advisory, nothing to approve yet)` or
|
|
456
|
+
``not approved (change `<id>` has no brief; advisory, nothing to
|
|
457
|
+
approve yet)`` and nothing is withheld (a `proposed` subject with no
|
|
458
|
+
brief, and a `specced` subject, show no `brief:` line at all). Once nothing blocks, a
|
|
459
|
+
ship-ready verifying subject gets the agent's own
|
|
460
|
+
`canon subject status <id> shipped`; status never asks a human to ship.
|
|
316
461
|
|
|
317
462
|
Status fails closed rather than suggest progress the gate would refuse:
|
|
318
463
|
|
|
@@ -330,19 +475,24 @@ Status fails closed rather than suggest progress the gate would refuse:
|
|
|
330
475
|
|
|
331
476
|
```json
|
|
332
477
|
{
|
|
333
|
-
"statusVersion":
|
|
334
|
-
"canonVersion": "0.
|
|
478
|
+
"statusVersion": 3,
|
|
479
|
+
"canonVersion": "0.17.0",
|
|
335
480
|
"policy": { "present": true, "spec_coverage": { "require_evidence": true, "scope": [], "exclude_lanes": [], "require_cases": ["failure"], "require_review": { "scope": ["verifying", "shipped"], "distinct_actor": true, "block_on_findings": true } } },
|
|
336
481
|
"warnings": [],
|
|
337
482
|
"subjects": [
|
|
338
483
|
{ "id": "cart", "title": "Cart", "status": "building", "scenarios": 2, "excluded": 0, "evidenced": 2, "divergent": 0,
|
|
339
|
-
"reviewed": 1, "staleReviews": 0, "unsignedReviews": 0, "reviewDue": true, "openBlockers": 1, "missingCases": []
|
|
484
|
+
"reviewed": 1, "staleReviews": 0, "unsignedReviews": 0, "reviewDue": true, "openBlockers": 1, "missingCases": [],
|
|
485
|
+
"brief": { "approved": true, "enforced": false, "change": "c-cart", "brief": ".canon/changes/c-cart/brief.md", "briefExists": true, "approval": "Journey 2026-10-11 — \"approved\"" } },
|
|
486
|
+
{ "id": "promo", "title": "Promo", "status": "verifying", "scenarios": 1, "excluded": 0, "evidenced": 1, "divergent": 0,
|
|
487
|
+
"reviewed": 1, "staleReviews": 0, "unsignedReviews": 0, "reviewDue": true, "openBlockers": 0, "missingCases": [],
|
|
488
|
+
"brief": { "approved": false, "enforced": true, "change": "c-promo", "brief": ".canon/changes/c-promo/brief.md", "briefExists": false, "source": "openspec/changes/c-promo/proposal.md" } }
|
|
340
489
|
],
|
|
341
490
|
"unowned": ["cart.promo.01"],
|
|
342
491
|
"next": [
|
|
343
|
-
{ "command": "
|
|
344
|
-
{ "command": "canon
|
|
345
|
-
{ "command": "canon
|
|
492
|
+
{ "command": "stop and report the brief in openspec/changes/c-promo/proposal.md; build and attest nothing for promo until the human answers (one answer approves brief and policy)", "why": "promo: .canon/changes/c-promo/brief.md does not exist yet: change `c-promo` comes from the external plan openspec/changes/c-promo/proposal.md, which canon reads and never edits, so its approval lives in that canon file (an older change's approval does not cover a new scope), and the approval is never assumed: … once the human answers, create .canon/changes/c-promo/brief.md holding a `Source: openspec/changes/c-promo/proposal.md` line and record their words there as `Approved: <who> <when> — \"<their words>\"` and run `canon policy approve --by <the human> --note \"<their words>\"`", "actor": "human" },
|
|
493
|
+
{ "command": "canon finding close --change-id c-cart --round 1 --seq 1 --disposition fixed --resolution-sha <sha> --actor-id <unit> --session-id <session>", "why": "cart: 1 open blocker finding(s) (first: …); fix it, commit, then close it", "actor": "agent" },
|
|
494
|
+
{ "command": "canon review add --project-id root --scenario-id cart.add.02 --reviewer <reviewer> --actor-id <reviewer> --session-id <review-session> --role reviewer --pin <sha> --original-spec-ref \"<feature file>\"", "why": "cart: 1 of 2 scenario(s) lack a qualifying review; …", "actor": "agent" },
|
|
495
|
+
{ "command": "canon status", "why": "1 scenario(s) carry no @subject tag (first: cart.promo.01); add `@subject:<id>` above each in its .feature file, then check again: …", "actor": "agent" }
|
|
346
496
|
],
|
|
347
497
|
"nextOmitted": 0
|
|
348
498
|
}
|
|
@@ -351,9 +501,30 @@ Status fails closed rather than suggest progress the gate would refuse:
|
|
|
351
501
|
Each `missingCases` entry is `{ "projectId", "surface", "case" }`: the
|
|
352
502
|
spec root, the `<area>.<surface>`, and the required case it lacks.
|
|
353
503
|
|
|
504
|
+
`brief` is present for `building`, `verifying` and `shipped` subjects,
|
|
505
|
+
and for a `proposed` subject whose newest adopted change has a brief (a
|
|
506
|
+
canon `brief.md` or an external `proposal.md`). `change` is the newest
|
|
507
|
+
adopted change (omitted when the subject adopts none); `brief` its canon
|
|
508
|
+
`.canon/changes/<change>/brief.md`, the one file the approval is read
|
|
509
|
+
from (omitted when the change has no brief to answer); `briefExists`
|
|
510
|
+
(always present) whether that file exists (`false` for an external
|
|
511
|
+
change whose canon brief is still to be created); `source` the external
|
|
512
|
+
plan document the change comes from (the canon brief's `Source:` line,
|
|
513
|
+
or, before that file exists, the external `proposal.md`; omitted for a
|
|
514
|
+
canon-written change); `approval` the text after `Approved:`; and
|
|
515
|
+
`enforced` whether the missing approval blocks (a brief exists to
|
|
516
|
+
answer). The 0.16 `proposal` field is gone.
|
|
517
|
+
|
|
518
|
+
Each `next` entry's `actor` is `agent` (its `command` is a canon command
|
|
519
|
+
line) or `human` (its `command` says what to get from the human).
|
|
520
|
+
|
|
354
521
|
`policy.spec_coverage` has the shape `canon context --json` prints
|
|
355
522
|
(`null` when absent, `{"invalid": …}` when unusable). `statusVersion`
|
|
356
|
-
changes only when a field changes meaning or is removed.
|
|
523
|
+
changes only when a field changes meaning or is removed. It became `2`
|
|
524
|
+
in 0.16, when `next[].command` stopped always being a command line
|
|
525
|
+
(`actor` tells which), and `brief` was added alongside. It is `3` since
|
|
526
|
+
0.17, when `brief.proposal` was removed: the canon `brief.md` is
|
|
527
|
+
`brief.brief`, with `briefExists` and `source`.
|
|
357
528
|
|
|
358
529
|
`subject new`, `adopt` and `status` write their records directly; the
|
|
359
530
|
human-readable success line ends `— written directly; nothing to
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@journeykit/canon",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.17.0",
|
|
4
4
|
"description": "canon — harness knowledge substrate. JS launcher: platform detection + native binary resolution.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -30,9 +30,9 @@
|
|
|
30
30
|
},
|
|
31
31
|
"dependencies": {},
|
|
32
32
|
"optionalDependencies": {
|
|
33
|
-
"@journeykit/canon-core-darwin-arm64": "0.
|
|
34
|
-
"@journeykit/canon-core-linux-x64": "0.
|
|
35
|
-
"@journeykit/canon-core-win32-x64": "0.
|
|
33
|
+
"@journeykit/canon-core-darwin-arm64": "0.17.0",
|
|
34
|
+
"@journeykit/canon-core-linux-x64": "0.17.0",
|
|
35
|
+
"@journeykit/canon-core-win32-x64": "0.17.0"
|
|
36
36
|
},
|
|
37
37
|
"devDependencies": {
|
|
38
38
|
"@types/node": "^20.0.0",
|