wowbagger 0.1.0-alpha.9 → 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.
@@ -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-beta.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-beta.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-beta.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
@@ -184,6 +199,41 @@ work read as ready.
184
199
  redaction and no access control. Say that plainly if a user asks for a report
185
200
  that hides work from a reader.
186
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
+
187
237
  ## Writing
188
238
 
189
239
  Every write is an explicit, reviewable Git change. Show the user the command
@@ -232,7 +282,9 @@ wowbagger patch --ledger <dir> --input request.json --json
232
282
  with a `patch` that brings the successor under the bound.
233
283
  - `create` starts an empty ledger on schema version 2 and returns the selected
234
284
  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.
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`.
236
288
  - `transition` changes **one** item. If the change would require touching a
237
289
  dependent or a child, it refuses. That refusal is correct — make it a
238
290
  reviewable multi-file Git change instead of forcing it.
@@ -294,21 +346,30 @@ and `number` is refused because it is immutable identity.
294
346
  are required, so a null one returns `candidate-invalid`, exit 2,
295
347
  `unchanged`. Use `[]` for a list; send the corrected string for a title.
296
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.
297
354
  - Patch appends no decision; the Git diff is the audit trail. It never mutates
298
355
  a second item, and it cannot touch status or provenance.
299
356
 
300
357
  **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:
358
+ the refusal — every frontmatter member is in exactly one of four classes:
302
359
 
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.
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.
306
364
  - **Consumer-editable through `patch`**: `title`, `priority`, `depends_on`,
307
365
  `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`.
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.
312
373
 
313
374
  **Extension members are patchable only where the ledger declares them.**
314
375
  `tags`, `tier`, and a consumer's own identifier fields are consumer-owned. Send
@@ -407,17 +468,26 @@ On a **provisioned** ledger (`claim capabilities` reports
407
468
  ```sh
408
469
  wowbagger create --ledger <dir> --input request.json --json
409
470
  git add <dir> && git commit -m "Record the mutation"
410
- wowbagger claim-verify --ledger <dir> --json
471
+ wowbagger claim-verify --ledger <dir> --id <item> --json
411
472
  wowbagger transition --ledger <dir> --input next.json --json
412
473
  ```
413
474
 
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.
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`.
421
491
 
422
492
  **`claim-verify` is the reconciliation procedure for that refusal.** Do not go
423
493
  looking for another verb; there is none. Read `details.findings`, do exactly
@@ -425,30 +495,60 @@ what each finding's `remediation` string says (it names the path), run
425
495
  `claim-verify` until it exits 0, then repeat the refused command. Never hand-
426
496
  edit a ledger file to get past it.
427
497
 
428
- For migrated ledgers, run the explicit extension declaration workflow before
429
- 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:
430
506
 
431
507
  ```sh
432
508
  wowbagger extensions-provision --ledger <dir> --input declaration.json --json --dry-run
433
509
  wowbagger extensions-provision --ledger <dir> --input declaration.json --json
434
510
  ```
435
511
 
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.
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`.
440
520
 
441
- 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
442
522
  CAS-fenced relation migration:
443
523
 
444
524
  ```sh
445
525
  wowbagger parent-migrate --ledger <dir> --input relation.json --json [--auto-commit]
446
526
  ```
447
527
 
448
- Request must carry `id`, `expected_revision`, `expected_parent`, `parent` (or
449
- `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
450
536
  accounting before publication.
451
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
+
452
552
  Successful mutation responses also return `result.changed_paths`: the exact
453
553
  ledger-relative paths changed by that invocation. Use this set for manual
454
554
  staging; never broaden it to `git add <ledger>` and never treat it as proof of
@@ -456,23 +556,40 @@ commit. With `--auto-commit`, `changed_paths` matches `commit_paths`, and
456
556
  `git_commit` proves the commit.
457
557
 
458
558
  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.
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.
460
565
 
461
566
  ### Or use --auto-commit and let one invocation do it
462
567
 
463
568
  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.
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.
467
572
 
468
573
  ```sh
469
574
  wowbagger transition --ledger <dir> --input next.json --json --auto-commit
470
575
  ```
471
576
 
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.
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
+
476
593
  Success adds `git_commit`, `commit_paths`, and `claim_verified` to `result`.
477
594
 
478
595
  Say these limits plainly when you use it:
@@ -535,7 +652,7 @@ wowbagger provision --ledger <dir> --json
535
652
  wowbagger claim capabilities --ledger <dir> --json
536
653
  wowbagger claim read|acquire|renew|release --ledger <dir> --input request.json --json
537
654
  wowbagger publish-claimed --ledger <dir> --input request.json --json [--auto-commit]
538
- wowbagger claim-verify --ledger <dir> --json
655
+ wowbagger claim-verify --ledger <dir> [--id <item>] --json
539
656
  wowbagger claim-sync --ledger <dir> --json
540
657
  wowbagger claim-merge-verify --ledger <dir> --base <ref> --head <ref> --json
541
658
  ```
@@ -565,26 +682,84 @@ envelope's `limits.cross_worktree_coordination: false` as permission to write
565
682
  with hostile or noncooperating tools — it only says the core never synchronizes
566
683
  checkouts.
567
684
 
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.
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.
573
709
 
574
710
  Read `error.details.findings[0].reason` and act on the named item:
575
711
 
576
712
  - `git-finalization-required` — you wrote the item here and have not committed.
577
713
  Commit, then `claim-verify`.
578
- - `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
579
717
  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.
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.
583
734
  - `unauthorized-revision` — the item changed outside the protocol. Two remedies
584
735
  are explicit: **restore** the authorized revision and run `claim-verify` to
585
736
  discard the edit, or **adopt** the committed revision and run `claim-verify`
586
737
  to keep it. Ask before discarding reviewed work.
587
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
+
588
763
  Adoption is per item and per revision explicit. Name the item and both
589
764
  revisions, take them from the finding, and commit the edited bytes first:
590
765
 
@@ -626,7 +801,7 @@ Use the claimed write path as one complete loop:
626
801
  not in an accessible Git checkout. Stop before mutation.
627
802
  2. Run `provision` once for the ledger. Keep its `ledger_namespace`.
628
803
  3. Run `claim capabilities --ledger <dir> --json` again. Require
629
- `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
630
805
  response's top-level `contract_version` with the core version; it is the
631
806
  legacy claim-envelope marker. Stop if the namespace is absent or the mode is
632
807
  not `merge-coordinated`.
@@ -638,12 +813,15 @@ Use the claimed write path as one complete loop:
638
813
  revision, the candidate bytes and digest, and the active claim fence. Never
639
814
  retry with only the operation ID; retry the complete request.
640
815
  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.
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.
647
825
  10. Release the claim with its current observed state.
648
826
  11. Run `validate` and show the resulting diff.
649
827
 
@@ -666,8 +844,11 @@ response envelopes, refusal precedence, and recovery rules.
666
844
  6. `validate`, then show the diff.
667
845
  7. On a provisioned ledger, commit the ledger change now:
668
846
  `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`.
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.
671
852
 
672
853
  Write, commit, `claim-verify`, next write. The unclaimed loop obeys the same
673
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',