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