wowbagger 0.1.0-alpha.9 → 0.5.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.
Files changed (46) hide show
  1. package/CHANGELOG.md +509 -0
  2. package/README.md +272 -136
  3. package/docs/adapter-contract.md +1 -1
  4. package/docs/host-contract.md +7 -1
  5. package/docs/mutation-contract.md +354 -82
  6. package/docs/work-claim-contract.md +466 -88
  7. package/package.json +2 -2
  8. package/schemas/core-capabilities-response.json +1 -1
  9. package/schemas/core-envelope.json +4 -3
  10. package/schemas/index.json +18 -0
  11. package/schemas/ledger-repair-proposal.json +170 -0
  12. package/schemas/ledger-repair-request.json +61 -0
  13. package/schemas/ledger-repair-response.json +90 -0
  14. package/schemas/report-config-v1.json +4 -0
  15. package/schemas/report-config-v2.json +5 -0
  16. package/skills/wowbagger/SKILL.md +242 -59
  17. package/src/adapter/core-probe.js +3 -4
  18. package/src/adapter/process-outcome.js +8 -1
  19. package/src/claim-capabilities.js +3 -3
  20. package/src/claim-coordinator.js +61 -12
  21. package/src/claim-journal.js +212 -7
  22. package/src/claim-prospective.js +1 -28
  23. package/src/claim-publication.js +412 -78
  24. package/src/claim-request.js +9 -0
  25. package/src/claim-store.js +9 -4
  26. package/src/cli.js +302 -56
  27. package/src/extensions.js +1 -0
  28. package/src/git-autocommit.js +106 -43
  29. package/src/git-reconciliation.js +74 -19
  30. package/src/git-worktrees.js +73 -0
  31. package/src/instrumentation.js +1 -0
  32. package/src/launch.js +2 -2
  33. package/src/ledger-repair.js +1170 -0
  34. package/src/mutation.js +73 -15
  35. package/src/reconciliation-classifier.js +117 -0
  36. package/src/report-evidence.js +158 -41
  37. package/src/report-graph.js +201 -73
  38. package/src/report-html.js +358 -155
  39. package/src/report-impact.js +106 -0
  40. package/src/report-selection.js +97 -0
  41. package/src/report-sequencing.js +4 -4
  42. package/src/report-svg.js +74 -20
  43. package/src/report-view.js +14 -1
  44. package/src/report.js +109 -23
  45. package/src/version-drift.js +98 -0
  46. package/src/worktree-identity.js +165 -0
@@ -14,14 +14,18 @@ publish something.
14
14
  Install the core separately before using this skill:
15
15
 
16
16
  ```sh
17
- npm install -g wowbagger@0.1.0-alpha.9
17
+ npm install -g wowbagger@0.5.0
18
18
  ```
19
19
 
20
- The core requires Node.js 20 or later. This plugin ships only agent
20
+ The core requires Node.js 24 or later. This plugin ships only agent
21
21
  instructions; it does not bundle the core, an MCP server, a remote service, a
22
22
  hook, or a background process. It operates on the ledger and its Git working
23
23
  copy through the core's validated CLI.
24
24
 
25
+ The supported runtime matrix is Node 24.20.0. Node 26 remains excluded until
26
+ the separate Vitest incompatibility reported by Lee is resolved; do not certify
27
+ Node 26 based on ambient availability.
28
+
25
29
  ## Before anything else: check the core
26
30
 
27
31
  This skill does **not** bundle the wowbagger core. It drives an installed one,
@@ -34,9 +38,9 @@ wowbagger capabilities --json
34
38
 
35
39
  Read the plain distribution version from the first command and the top-level
36
40
  `contract_version` from the second. **This skill requires distribution version
37
- `0.1.0-alpha.9` and core `contract_version: 5`.**
41
+ `0.5.0` and core `contract_version: 5`.**
38
42
 
39
- The distribution pin names the published `0.1.0-alpha.9` release; the cut that
43
+ The distribution pin names the published `0.5.0` release; the cut that
40
44
  publishes core `contract_version: 5` moves it. Earlier cores report
41
45
  `contract_version: 3` or lower and lack behavior this skill requires, including
42
46
  the bounded item source, so the version check refuses them. Do not soften
@@ -63,6 +67,17 @@ either pin to make a check pass.
63
67
  Run both commands once per session before the first ledger command, not before
64
68
  every command.
65
69
 
70
+ Run the read-only drift preflight before the first ledger mutation:
71
+
72
+ ```sh
73
+ wowbagger version-drift --json
74
+ ```
75
+
76
+ It compares the installed skill pin with the required distribution and core
77
+ contract, then compares those requirements with the running core. If it refuses,
78
+ do not mutate the ledger. Update the stale package, plugin cache, or linked
79
+ checkout named by `result.error.details.provenance`, then rerun the preflight.
80
+
66
81
  You drive the core as an agent, through the commands below. A UI plugin or
67
82
  another non-agent consumer drives it as a process instead: absolute Node
68
83
  executable, absolute `wowbagger.js`, argument array, `shell: false`. If the
@@ -131,8 +146,10 @@ the absolute `result.output`. On failure, require `ok: false` and inspect
131
146
  publication preserves the prior report. Do not parse the generated HTML and do
132
147
  not parse human output: the JSON result is the only machine surface.
133
148
 
134
- The report ends its decision surface with a 3D dependency graph of the items the
135
- artifact covers. Its renderer is a pinned, checksummed `3d-force-graph` build
149
+ The report is one shared Items, Flow, and Dependencies workspace. The
150
+ Dependencies section holds the 3D dependency graph of the items the artifact
151
+ covers, under the same scope as Items and Flow. Its renderer is a pinned,
152
+ checksummed `3d-force-graph` build
136
153
  vendored at `vendor/3d-force-graph/` and inlined at generation time, so the
137
154
  report is still one self-contained file that fetches nothing — it is roughly
138
155
  1.3 MB larger for it. A browser without WebGL shows the graph section's plain
@@ -184,6 +201,41 @@ work read as ready.
184
201
  redaction and no access control. Say that plainly if a user asks for a report
185
202
  that hides work from a reader.
186
203
 
204
+ ## Every writer must be on the same core before the first create
205
+
206
+ On a provisioned ledger, `create` now records its allocation in the shared
207
+ claim journal before it publishes anything. That grammar is new, so the upgrade
208
+ is a hard cutover with no automatic migration and no mixed-version grace
209
+ period: **upgrade every writer in one Git coordination domain to the current
210
+ core before the first alpha.14 create.** A worktree left on the old core does
211
+ not write a duplicate — it stops making claim-protected mutations, which is the
212
+ safe outcome, not a usable one.
213
+
214
+ An old core cannot read the new create entry and says so badly. It answers
215
+ exit 6, `error.code` `claim-store-unavailable`, message
216
+ `The durable claim store is unavailable.`, and `error.details.reason`
217
+ `claim-store-unreadable`, leaves state unchanged, and writes no item. Read that
218
+ exact combination as **this repository was written by a newer Wowbagger;
219
+ upgrade this worktree to continue**, and say so to the user: the old binary is
220
+ immutable and can never print better guidance. (Item #185 is open for general
221
+ version-drift detection.)
222
+
223
+ Do not confuse that with a full journal. Exit 6
224
+ `claim-store-unavailable` with reason `journal-capacity-exceeded` means the
225
+ 65,536-entry or 8,388,608-byte bound was reached before publication. State is
226
+ unchanged and prior history stays intact. Do not upgrade, truncate, compact, or
227
+ hand-edit the journal; no automatic recovery verb exists. Report the capacity
228
+ refusal as the blocker. A real persistence failure remains
229
+ `clock-floor-persistence-failed`, and uncertainty after an intent remains an
230
+ outcome-unknown refusal.
231
+
232
+ Say what the fix does and does not cover. It closes the reported
233
+ PropertyCompass2 collision: cooperating alpha.14 worktrees of one clone that
234
+ share one Git common directory can no longer commit two items carrying the same
235
+ number. Separate clones, separate machines, alpha.13 writers before the hard
236
+ cutover, and noncooperating writes stay outside that fence and still rely on
237
+ branch integration plus `validate`.
238
+
187
239
  ## Writing
188
240
 
189
241
  Every write is an explicit, reviewable Git change. Show the user the command
@@ -232,7 +284,9 @@ wowbagger patch --ledger <dir> --input request.json --json
232
284
  with a `patch` that brings the successor under the bound.
233
285
  - `create` starts an empty ledger on schema version 2 and returns the selected
234
286
  version at `result.item.core.schema_version`. A non-empty schema-version-1
235
- ledger stays on version 1 until its complete ledger is migrated.
287
+ ledger stays on version 1 until its complete ledger is migrated. The create
288
+ request name `extensions` is reserved for the patch container; name extension
289
+ members directly on `item`.
236
290
  - `transition` changes **one** item. If the change would require touching a
237
291
  dependent or a child, it refuses. That refusal is correct — make it a
238
292
  reviewable multi-file Git change instead of forcing it.
@@ -294,21 +348,30 @@ and `number` is refused because it is immutable identity.
294
348
  are required, so a null one returns `candidate-invalid`, exit 2,
295
349
  `unchanged`. Use `[]` for a list; send the corrected string for a title.
296
350
  - `priority` takes a non-negative integer.
351
+ - For an item whose status is `done`, `killed`, `archived`, or `deferred`, the
352
+ request date must equal the existing `updated` date for `patch`, `snooze`, and
353
+ `parent-migrate`. Inspect immediately before the mutation and reuse that date;
354
+ an earlier date fails the request floor and a later date violates the
355
+ terminal-date invariant.
297
356
  - Patch appends no decision; the Git diff is the audit trail. It never mutates
298
357
  a second item, and it cannot touch status or provenance.
299
358
 
300
359
  **Which fields are yours.** Do not discover this by sending a patch and reading
301
- the refusal — every frontmatter member is in exactly one of three classes:
360
+ the refusal — every frontmatter member is in exactly one of four classes:
302
361
 
303
- - **Core-owned**, never yours: `schema_version`, `id`, `number`, `status`,
304
- `created`, `updated`, the terminal dates (`completed`, `killed`, `archived`,
305
- `deferred`), and `decisions`. `transition` writes these; nothing else does.
362
+ - **Core-owned**, never directly editable: `schema_version`, `id`, `number`,
363
+ `status`, `created`, `updated`, the terminal dates (`completed`, `killed`,
364
+ `archived`, `deferred`), and `decisions`. The core derives these through
365
+ lifecycle and mutation commands; `patch` cannot name them.
306
366
  - **Consumer-editable through `patch`**: `title`, `priority`, `depends_on`,
307
367
  `related`, the body, and every extension member the ledger declares.
308
- - **Create-once**: `kind`, `provenance`, `parent`, `snoozed_until`. Set at
309
- `create` and fixed after. `kind` is refused deliberately — a task-to-epic
310
- flip changes which parent and children rules apply and which lifecycle edges
311
- are allowed, so it needs its own verb, not a wider `set`.
368
+ - **Dedicated mutations**: use `parent-migrate` to repoint an existing item to
369
+ an epic or detach it, and use `snooze` to set or clear `snoozed_until`. Both
370
+ use exact-revision compare-and-swap and accept `--auto-commit`.
371
+ - **Create-once**: `kind` and `provenance`. `kind` is refused deliberately — a
372
+ task-to-epic flip changes which parent and children rules apply and which
373
+ lifecycle edges are allowed, so it needs its own future verb, not a wider
374
+ `set`. Provenance remains byte-identical after create.
312
375
 
313
376
  **Extension members are patchable only where the ledger declares them.**
314
377
  `tags`, `tier`, and a consumer's own identifier fields are consumer-owned. Send
@@ -407,17 +470,26 @@ On a **provisioned** ledger (`claim capabilities` reports
407
470
  ```sh
408
471
  wowbagger create --ledger <dir> --input request.json --json
409
472
  git add <dir> && git commit -m "Record the mutation"
410
- wowbagger claim-verify --ledger <dir> --json
473
+ wowbagger claim-verify --ledger <dir> --id <item> --json
411
474
  wowbagger transition --ledger <dir> --input next.json --json
412
475
  ```
413
476
 
414
- The claim store validates every recorded mutation against Git `HEAD`, never
415
- against working-tree bytes — that is what makes a mutation durable rather than
416
- a local edit. So an uncommitted mutation blocks the next one. Skip the commit
417
- and the next `create`, `transition`, `patch`, or `publish-claimed` returns
418
- exit 6 `claim-store-unavailable` with
419
- `details.reason: "publication-reconciliation-required"`. `state` is
420
- `unchanged`, so nothing was written.
477
+ The claim store reconciles every recorded mutation with Git `HEAD` and the
478
+ working tree. It refuses the next mutation when it finds an
479
+ `unauthorized-revision`, requires Git finalization, or requires synchronization
480
+ for the target item. A synchronization finding on an unrelated item remains
481
+ visible to targeted `claim-verify` without blocking that item.
482
+
483
+ One exact window is nonblocking: an existing item's latest authorized
484
+ working-tree bytes with an earlier authorized revision at `HEAD`. That
485
+ authorized predecessor/successor window produces no finding, so another
486
+ mutation can run before the first is committed. Do not mistake acceptance for
487
+ durability. Commit each mutation anyway, then run `claim-verify`.
488
+
489
+ `create` never gets that window. A new item has no earlier authorized revision,
490
+ so Git `HEAD` is the only place its authorized bytes can live, and an
491
+ uncommitted create blocks every later mutation — including the next create —
492
+ with `git-finalization-required`.
421
493
 
422
494
  **`claim-verify` is the reconciliation procedure for that refusal.** Do not go
423
495
  looking for another verb; there is none. Read `details.findings`, do exactly
@@ -425,30 +497,60 @@ what each finding's `remediation` string says (it names the path), run
425
497
  `claim-verify` until it exits 0, then repeat the refused command. Never hand-
426
498
  edit a ledger file to get past it.
427
499
 
428
- For migrated ledgers, run the explicit extension declaration workflow before
429
- managed corrections:
500
+ For an existing ledger, bootstrap patch authority before managed corrections.
501
+ For standard tags, `declaration.json` is:
502
+
503
+ ```json
504
+ {"members":{"tags":"string-list"}}
505
+ ```
506
+
507
+ Review the proposal, then publish the same declaration:
430
508
 
431
509
  ```sh
432
510
  wowbagger extensions-provision --ledger <dir> --input declaration.json --json --dry-run
433
511
  wowbagger extensions-provision --ledger <dir> --input declaration.json --json
434
512
  ```
435
513
 
436
- The request must name each member and one supported type explicitly. The core
437
- validates that every selected member exists across the complete ledger with the
438
- declared type; it never infers authority from one item. Commit the generated
439
- `.wowbagger/extensions.json` before the first `patch` that uses those members.
514
+ The request names every selected member and type explicitly. The core requires
515
+ a valid complete ledger, validates every occurrence of each selected member,
516
+ and reports occurrence counts; a member need not appear on every item, but it
517
+ must appear at least once. Dry-run writes nothing. Publication creates one
518
+ canonical declaration without changing item bytes and refuses to overwrite a
519
+ different declaration. Commit only `.wowbagger/extensions.json`, inspect the
520
+ target again, then patch `set.extensions.tags`. A YAML anchor or alias on that
521
+ item still refuses `extension-anchored`.
440
522
 
441
- To detach or reparent a live child without recreating its identity, use the
523
+ To detach or reparent an item without recreating its identity, use the
442
524
  CAS-fenced relation migration:
443
525
 
444
526
  ```sh
445
527
  wowbagger parent-migrate --ledger <dir> --input relation.json --json [--auto-commit]
446
528
  ```
447
529
 
448
- Request must carry `id`, `expected_revision`, `expected_parent`, `parent` (or
449
- `null`), and `date`. The complete ledger validates the old and new parent
530
+ The parent-migrate request is:
531
+
532
+ ```json
533
+ {"id":"wb_...","expected_revision":"sha256:...","expected_parent":"wb_...","parent":null,"date":"YYYY-MM-DD"}
534
+ ```
535
+
536
+ Every member is required; `expected_parent` and `parent` may be `null`. Inspect
537
+ immediately before sending it. The complete ledger validates old and new parent
450
538
  accounting before publication.
451
539
 
540
+ Set or clear a snooze date with:
541
+
542
+ ```sh
543
+ wowbagger snooze --ledger <dir> --input snooze.json --json [--auto-commit]
544
+ ```
545
+
546
+ The snooze request is:
547
+
548
+ ```json
549
+ {"id":"wb_...","expected_revision":"sha256:...","snoozed_until":"YYYY-MM-DD","date":"YYYY-MM-DD"}
550
+ ```
551
+
552
+ Use `snoozed_until: null` to clear it. Every member is required.
553
+
452
554
  Successful mutation responses also return `result.changed_paths`: the exact
453
555
  ledger-relative paths changed by that invocation. Use this set for manual
454
556
  staging; never broaden it to `git add <ledger>` and never treat it as proof of
@@ -456,23 +558,40 @@ commit. With `--auto-commit`, `changed_paths` matches `commit_paths`, and
456
558
  `git_commit` proves the commit.
457
559
 
458
560
  Batch work is where this bites: filing ten items means ten commits, not one
459
- commit at the end. Tell the user that before starting a batch.
561
+ commit at the end. Tell the user that before starting. There is permanently no
562
+ batch mutation in the direct-Markdown architecture. The create-then-commit loop,
563
+ implemented most briefly as serial `create --auto-commit` calls, is the
564
+ supported bulk path. Ledger item #186 records this permanent decision. Run calls in
565
+ request order and do not start the next until the previous result or recovery
566
+ is final.
460
567
 
461
568
  ### Or use --auto-commit and let one invocation do it
462
569
 
463
570
  On a provisioned ledger, `--auto-commit` performs that whole loop inside one
464
- invocation. It is accepted on `create`, `transition`, `patch`, and
465
- `publish-claimed`, once each, and only with the flag present — there is no
466
- setting that turns it on for you.
571
+ invocation. It is accepted on `create`, `transition`, `parent-migrate`, `snooze`,
572
+ `patch`, and `publish-claimed`, once each, and only with the flag present — there
573
+ is no setting that turns it on for you.
467
574
 
468
575
  ```sh
469
576
  wowbagger transition --ledger <dir> --input next.json --json --auto-commit
470
577
  ```
471
578
 
472
- One flagged invocation refuses if anything is staged anywhere or any path under
473
- the ledger is dirty, reconciles, runs the mutation unchanged, commits exactly
474
- the changed item plus at most one `.wowbagger/reconcile-<namespace>.md` with a
475
- fixed subject, verifies that commit, and runs `claim-verify` before it answers.
579
+ One flagged invocation refuses if anything is staged anywhere or any foreign
580
+ path under the ledger is dirty, reconciles, runs the mutation unchanged,
581
+ commits exactly the changed item plus at most one
582
+ `.wowbagger/reconcile-<namespace>.md` with a fixed subject, verifies that commit,
583
+ and runs `claim-verify` before it answers. A successful `create --auto-commit`
584
+ commits exactly two paths: the created item and that reconciliation log. Every
585
+ command rebuilds its own derived reconciliation log during preflight, but
586
+ `create` refuses a log that was already dirty when you invoked it, and every
587
+ other dirty ledger path still refuses.
588
+
589
+ Preflight and post-commit reconciliation block findings for the requested item.
590
+ An unrelated `worktree-synchronization-required` finding remains visible to
591
+ `claim-verify` without turning a successful mutation into failure. If claim
592
+ verification refuses, the result preserves `claim_verify_code` and
593
+ `claim_verify_reason`; only `claim-store-locked` is retryable.
594
+
476
595
  Success adds `git_commit`, `commit_paths`, and `claim_verified` to `result`.
477
596
 
478
597
  Say these limits plainly when you use it:
@@ -535,7 +654,7 @@ wowbagger provision --ledger <dir> --json
535
654
  wowbagger claim capabilities --ledger <dir> --json
536
655
  wowbagger claim read|acquire|renew|release --ledger <dir> --input request.json --json
537
656
  wowbagger publish-claimed --ledger <dir> --input request.json --json [--auto-commit]
538
- wowbagger claim-verify --ledger <dir> --json
657
+ wowbagger claim-verify --ledger <dir> [--id <item>] --json
539
658
  wowbagger claim-sync --ledger <dir> --json
540
659
  wowbagger claim-merge-verify --ledger <dir> --base <ref> --head <ref> --json
541
660
  ```
@@ -565,26 +684,84 @@ envelope's `limits.cross_worktree_coordination: false` as permission to write
565
684
  with hostile or noncooperating tools — it only says the core never synchronizes
566
685
  checkouts.
567
686
 
568
- A recorded `transition`, `patch`, or claimed publication blocks mutations
569
- targeting that same item with exit 6 `claim-store-unavailable`, reason
570
- `publication-reconciliation-required`. An unrelated item mutation may proceed
571
- when the only finding is `worktree-synchronization-required`. `create` records
572
- nothing, so it never creates a publication block.
687
+ A recorded `create`, `transition`, `patch`, or claimed publication blocks
688
+ mutations targeting that same item with exit 6 `claim-store-unavailable`,
689
+ reason `publication-reconciliation-required`. An unrelated item mutation may
690
+ proceed when the only finding is `worktree-synchronization-required`.
691
+
692
+ `create` is the one exception to that scoping, because it allocates the next
693
+ number from the items this checkout can see. It also refuses when the journal
694
+ records a committed item this worktree does not hold at all: that item carries
695
+ a number nobody here can read, so the next number allocated here might already
696
+ be taken. A stale revision of an item this worktree does hold is not a blocker
697
+ for `create` — a number is immutable, so the local maximum is still right. The
698
+ refusal is the same exit 6 `claim-store-unavailable` with reason
699
+ `publication-reconciliation-required`, state `unchanged`, and no item file
700
+ written; integrate the missing item, run `claim-verify` until it exits 0, and
701
+ resend the same request, which then takes the next number.
702
+
703
+ That fence stops new collisions; it does not repair old ones through core
704
+ version 5 or claim operations. Existing duplicate numbers are item #182
705
+ recovery work. Use the separate `ledger-repair` contract:
706
+ `number-repair-proposal --ledger <dir> --json` is read-only and writes no item
707
+ file; `number-repair --ledger <dir> --input <repair.json> --json` applies a
708
+ reviewed complete mapping under the shared namespace fence. Number-only repair
709
+ preserves ULID identities and relation values. Never hand-edit the ledger:
710
+ arbitrary edits can damage IDs, paths, or references.
573
711
 
574
712
  Read `error.details.findings[0].reason` and act on the named item:
575
713
 
576
714
  - `git-finalization-required` — you wrote the item here and have not committed.
577
715
  Commit, then `claim-verify`.
578
- - `worktree-synchronization-required` — another worktree wrote the item. If the
716
+ - `worktree-synchronization-required` — another worktree wrote the item.
717
+ `owner_ref` names an **active named worktree** and nothing else: it is always
718
+ the branch of a live worktree that carries the expected revision. If the
579
719
  finding names `owner_ref` and `owner_commit`, WAIT for that owner to publish,
580
- then synchronize this checkout and run `claim-verify`. If it carries
581
- `owner_unavailable: true`, inspect reachable or dangling commits and use
582
- explicit restore or `claim-adopt`; never merge unrelated live work.
720
+ then synchronize this checkout and run `claim-verify`. `owner_unavailable:
721
+ true` means no such worktree exists, and it covers three cases: the expected
722
+ revision is not reachable at all, a live sibling holds it on a detached
723
+ `HEAD`, or it is reachable only from a tag, a remote-tracking ref, or a
724
+ branch no worktree has checked out. Reachability is not ownership; a ref you
725
+ can see is not a worktree that can publish. Follow the `remediation`, which
726
+ separates those cases. A revision that is **not yet reachable** means WAIT for
727
+ the owning worktree to commit, then synchronize. A revision that is
728
+ **reachable in Git while no active named worktree owner is established** —
729
+ a tag, a remote-tracking ref, an unchecked-out branch, or a detached sibling
730
+ carries it — is not a wait at all: inspect that reachable history, then
731
+ restore the authorized bytes or use explicit `claim-adopt` after review.
732
+ Ownership that **cannot be established from reachable refs** — the item has
733
+ never existed in this checkout — calls for inspecting reachable or dangling
734
+ commits with the same explicit restore or `claim-adopt`. Never merge unrelated
735
+ live work.
583
736
  - `unauthorized-revision` — the item changed outside the protocol. Two remedies
584
737
  are explicit: **restore** the authorized revision and run `claim-verify` to
585
738
  discard the edit, or **adopt** the committed revision and run `claim-verify`
586
739
  to keep it. Ask before discarding reviewed work.
587
740
 
741
+ Two live worktrees answering to one identity, or a worktree roster the
742
+ coordinator could not finish reading, refuse before anything is classified.
743
+ You get exit 6 `claim-store-unavailable`, reason `claim-store-unreadable`, with
744
+ `error.details.identity_diagnostic`: `duplicate-worktree-identity` naming the
745
+ `worktree_id` and `live_worktree_count`, or `worktree-enumeration-failed` with
746
+ no further member. Auto-commit reports the same diagnostic inside
747
+ `auto-commit-preflight-failed` with `retryable: false`. Neither is retryable
748
+ and neither is yours to repair by editing files. Report the diagnostic verbatim,
749
+ including the `worktree_id` and `live_worktree_count`: a duplicate means two
750
+ live worktrees hold the same identity file, usually because a private Git
751
+ directory was copied, and which worktree keeps the UUID is a person's decision.
752
+ An enumeration failure means a registered worktree path could not be read. The
753
+ identity itself is an opaque UUID a worktree writes once into its private Git
754
+ directory. Never create, copy, or edit it.
755
+
756
+ **Choose verification scope deliberately.** Bare
757
+ `claim-verify --ledger <dir> --json` is strict repository diagnosis: any
758
+ blocking finding anywhere keeps it at exit 6. After working one item, use
759
+ `claim-verify --ledger <dir> --id <item> --json`. It keeps every repository
760
+ finding visible and marks each with `blocks_verification_scope`, but unrelated
761
+ target-scoped findings do not fail that item's gate. Global barriers still
762
+ fail every target. Never adopt or hand-edit a sibling's item to force either
763
+ scope clean.
764
+
588
765
  Adoption is per item and per revision explicit. Name the item and both
589
766
  revisions, take them from the finding, and commit the edited bytes first:
590
767
 
@@ -626,7 +803,7 @@ Use the claimed write path as one complete loop:
626
803
  not in an accessible Git checkout. Stop before mutation.
627
804
  2. Run `provision` once for the ledger. Keep its `ledger_namespace`.
628
805
  3. Run `claim capabilities --ledger <dir> --json` again. Require
629
- `result.operations.work_claim.api_version: 2`. Do not compare the claim
806
+ `result.operations.work_claim.api_version: 3`. Do not compare the claim
630
807
  response's top-level `contract_version` with the core version; it is the
631
808
  legacy claim-envelope marker. Stop if the namespace is absent or the mode is
632
809
  not `merge-coordinated`.
@@ -638,12 +815,15 @@ Use the claimed write path as one complete loop:
638
815
  revision, the candidate bytes and digest, and the active claim fence. Never
639
816
  retry with only the operation ID; retry the complete request.
640
817
  8. Commit the item change, or merge the worker commit into the coordinating
641
- branch. Do this now, not at the end of a batch — the next mutating command
642
- refuses while this one is uncommitted.
643
- 9. Run `claim-verify` after the commit or merge. It finalizes the Git outcome,
644
- repairs response-loss cases, and reports later revision drift. Require exit
645
- 0 before the next mutating command; exit 6 means findings remain, so act on
646
- each `remediation` string and run it again.
818
+ branch. Do this now, not at the end of a batch. Acceptance of another command
819
+ does not prove this mutation is durable, and a blocking finding still
820
+ refuses that command.
821
+ 9. Run `claim-verify --ledger <dir> --id <item> --json` after the commit or
822
+ merge. It finalizes the Git outcome, repairs response-loss cases, and reports
823
+ later revision drift. Require exit 0 before the next mutating command; exit
824
+ 6 means findings block this item, so act on each blocking finding's
825
+ `remediation` and run it again. Unrelated nonblocking findings remain
826
+ visible for repository diagnosis.
647
827
  10. Release the claim with its current observed state.
648
828
  11. Run `validate` and show the resulting diff.
649
829
 
@@ -666,8 +846,11 @@ response envelopes, refusal precedence, and recovery rules.
666
846
  6. `validate`, then show the diff.
667
847
  7. On a provisioned ledger, commit the ledger change now:
668
848
  `git add <dir> && git commit`.
669
- 8. On a provisioned ledger, run `claim-verify --ledger <dir> --json` and
670
- require exit 0 before the next `create`, `transition`, or `patch`.
849
+ 8. On a provisioned ledger, run
850
+ `claim-verify --ledger <dir> --id <item> --json` and require exit 0 before
851
+ the next `create`, `transition`, `parent-migrate`, `snooze`, `patch`, or
852
+ `publish-claimed`. Use bare verification separately for strict
853
+ repository-wide diagnosis.
671
854
 
672
855
  Write, commit, `claim-verify`, next write. The unclaimed loop obeys the same
673
856
  rule as the claimed one, because both run through the same coordinator. Steps 7
@@ -19,10 +19,9 @@ export const CORE_COMMAND_ORDER = Object.freeze([
19
19
  'capabilities', 'create', 'inspect', 'patch', 'ready', 'transition', 'validate',
20
20
  ]);
21
21
  export const CORE_CONTRACT_VERSION = 5;
22
- // The work-claim API lives in its own version domain. It moved to 2 with the
23
- // item-source refusal that replaced publish-claimed's version 1 error for an
24
- // oversized candidate.
25
- export const WORK_CLAIM_API_VERSION = 2;
22
+ // Work-claim API lives in its own version domain. Version 3 adds optional
23
+ // target-scoped claim verification while preserving strict bare verification.
24
+ export const WORK_CLAIM_API_VERSION = 3;
26
25
  // The report configuration versions this core accepts. Version 1 remains
27
26
  // supported unchanged; version 2 names views.
28
27
  export const REPORT_CONFIG_VERSIONS = Object.freeze([1, 2]);
@@ -18,6 +18,13 @@ const WOWBAGGER_ID = /^wb_[0-7][0-9A-HJKMNP-TV-Z]{25}$/;
18
18
  const NAMESPACE_ID = /^wbns_[a-f0-9]{32}$/;
19
19
  const CANONICAL_UINT64 = /^(0|[1-9][0-9]*)$/;
20
20
  const CONTROL_CHARACTER = /[\u0000-\u001F\u007F]/;
21
+ const LOCK_OWNER_OPERATIONS = new Set([
22
+ 'create',
23
+ 'transition',
24
+ 'parent-migrate',
25
+ 'snooze',
26
+ 'patch',
27
+ ]);
21
28
  const MESSAGES = Object.freeze({
22
29
  'mutation-outcome-unknown': 'The mutation may have been applied; inspect current state before retrying.',
23
30
  'output-limit-exceeded': 'The core output exceeded the requested bound.',
@@ -1133,7 +1140,7 @@ function validLockHeldDetails(value) {
1133
1140
  && hasExactMembers(value.owner, ['lock_version', 'item_id', 'operation', 'writer_id', 'started_at'])
1134
1141
  && value.owner.lock_version === 1
1135
1142
  && value.owner.item_id === value.id
1136
- && new Set(['create', 'transition', 'patch']).has(value.owner.operation)
1143
+ && LOCK_OWNER_OPERATIONS.has(value.owner.operation)
1137
1144
  && typeof value.owner.writer_id === 'string'
1138
1145
  && /^[\x21-\x7e]{1,128}$/.test(value.owner.writer_id)
1139
1146
  && isCoreRfc3339Utc(value.owner.started_at);
@@ -2,7 +2,7 @@ export function resolveWorkClaimCapability({ gitCommonDir, namespace = null }) {
2
2
  if (!gitCommonDir) {
3
3
  return {
4
4
  supported: false,
5
- api_version: 2,
5
+ api_version: 3,
6
6
  mode: 'advisory',
7
7
  claim_protected_publication: false,
8
8
  fencing_enforced_at: 'none',
@@ -12,7 +12,7 @@ export function resolveWorkClaimCapability({ gitCommonDir, namespace = null }) {
12
12
  if (!namespace) {
13
13
  return {
14
14
  supported: true,
15
- api_version: 2,
15
+ api_version: 3,
16
16
  mode: 'advisory',
17
17
  claim_protected_publication: false,
18
18
  fencing_enforced_at: 'none',
@@ -21,7 +21,7 @@ export function resolveWorkClaimCapability({ gitCommonDir, namespace = null }) {
21
21
  }
22
22
  return {
23
23
  supported: true,
24
- api_version: 2,
24
+ api_version: 3,
25
25
  mode: 'merge-coordinated',
26
26
  claim_protected_publication: true,
27
27
  fencing_enforced_at: 'git-history-reconciliation',