wowbagger 0.1.0-alpha.13 → 0.1.0-alpha.17

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.
@@ -0,0 +1,170 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://github.com/lstutzman/wowbagger/schemas/ledger-repair-proposal.json",
4
+ "title": "number-repair-proposal result, ledger-repair version 1",
5
+ "description": "The `result` member of a successful `number-repair-proposal` response. The proposal is read-only: it reads the raw items of a ledger whose complete error set is duplicate numbers and computes the mapping that would repair it, writing nothing. The caller confirms or replaces `suggested_changes` and submits them as a `number-repair` request, which validates every witness again under the repair lock.",
6
+ "type": "object",
7
+ "additionalProperties": false,
8
+ "required": [
9
+ "ledger_snapshot_revision",
10
+ "duplicate_groups",
11
+ "items",
12
+ "suggested_changes",
13
+ "preserved_items",
14
+ "validation_errors"
15
+ ],
16
+ "properties": {
17
+ "ledger_snapshot_revision": {
18
+ "$ref": "https://github.com/lstutzman/wowbagger/schemas/common.json#/$defs/revision",
19
+ "description": "The digest over the complete relevant source bytes this proposal was computed from. A repair request replays it so a changed ledger is a conflict rather than a surprise."
20
+ },
21
+ "duplicate_groups": {
22
+ "type": "array",
23
+ "minItems": 1,
24
+ "description": "One entry per duplicated number, naming every item that carries it. A repair request must resolve every group.",
25
+ "items": {
26
+ "type": "object",
27
+ "additionalProperties": false,
28
+ "required": [
29
+ "number",
30
+ "item_ids"
31
+ ],
32
+ "properties": {
33
+ "number": {
34
+ "type": "integer",
35
+ "minimum": 1
36
+ },
37
+ "item_ids": {
38
+ "type": "array",
39
+ "minItems": 2,
40
+ "uniqueItems": true,
41
+ "items": {
42
+ "$ref": "https://github.com/lstutzman/wowbagger/schemas/common.json#/$defs/itemId"
43
+ }
44
+ }
45
+ }
46
+ }
47
+ },
48
+ "items": {
49
+ "type": "array",
50
+ "minItems": 1,
51
+ "description": "Every item in a duplicate group, with the configured path and exact source revision a repair request witnesses.",
52
+ "items": {
53
+ "type": "object",
54
+ "additionalProperties": false,
55
+ "required": [
56
+ "item_id",
57
+ "path",
58
+ "revision",
59
+ "number"
60
+ ],
61
+ "properties": {
62
+ "item_id": {
63
+ "$ref": "https://github.com/lstutzman/wowbagger/schemas/common.json#/$defs/itemId"
64
+ },
65
+ "path": {
66
+ "type": "string",
67
+ "minLength": 1,
68
+ "description": "The item's configured path, relative to the ledger directory."
69
+ },
70
+ "revision": {
71
+ "$ref": "https://github.com/lstutzman/wowbagger/schemas/common.json#/$defs/revision"
72
+ },
73
+ "number": {
74
+ "type": "integer",
75
+ "minimum": 1
76
+ }
77
+ }
78
+ }
79
+ },
80
+ "suggested_changes": {
81
+ "type": "array",
82
+ "minItems": 1,
83
+ "description": "The computed mapping, in the exact shape of the `changes` member of a number-repair request. Each group keeps the number on its lexicographically smallest ULID and moves the rest above the current maximum number.",
84
+ "items": {
85
+ "type": "object",
86
+ "additionalProperties": false,
87
+ "required": [
88
+ "item_id",
89
+ "expected_revision",
90
+ "expected_number",
91
+ "replacement_number"
92
+ ],
93
+ "properties": {
94
+ "item_id": {
95
+ "$ref": "https://github.com/lstutzman/wowbagger/schemas/common.json#/$defs/itemId"
96
+ },
97
+ "expected_revision": {
98
+ "$ref": "https://github.com/lstutzman/wowbagger/schemas/common.json#/$defs/revision"
99
+ },
100
+ "expected_number": {
101
+ "type": "integer",
102
+ "minimum": 1
103
+ },
104
+ "replacement_number": {
105
+ "type": "integer",
106
+ "minimum": 1
107
+ }
108
+ }
109
+ }
110
+ },
111
+ "preserved_items": {
112
+ "type": "array",
113
+ "minItems": 1,
114
+ "description": "Item IDs the proposal keeps at their current number.",
115
+ "uniqueItems": true,
116
+ "items": {
117
+ "$ref": "https://github.com/lstutzman/wowbagger/schemas/common.json#/$defs/itemId"
118
+ }
119
+ },
120
+ "references": {
121
+ "type": "array",
122
+ "description": "The affected items' relations, exactly as the source carries them. Relations are ULIDs, so a number repair preserves them; they are reported so a caller can confirm that nothing identity-bearing is being rewritten.",
123
+ "items": {
124
+ "type": "object",
125
+ "additionalProperties": false,
126
+ "required": [
127
+ "item_id",
128
+ "depends_on",
129
+ "related",
130
+ "parent"
131
+ ],
132
+ "properties": {
133
+ "item_id": {
134
+ "$ref": "https://github.com/lstutzman/wowbagger/schemas/common.json#/$defs/itemId"
135
+ },
136
+ "depends_on": {
137
+ "type": "array",
138
+ "items": {
139
+ "$ref": "https://github.com/lstutzman/wowbagger/schemas/common.json#/$defs/itemId"
140
+ }
141
+ },
142
+ "related": {
143
+ "type": "array",
144
+ "items": {
145
+ "$ref": "https://github.com/lstutzman/wowbagger/schemas/common.json#/$defs/itemId"
146
+ }
147
+ },
148
+ "parent": {
149
+ "oneOf": [
150
+ {
151
+ "$ref": "https://github.com/lstutzman/wowbagger/schemas/common.json#/$defs/itemId"
152
+ },
153
+ {
154
+ "type": "null"
155
+ }
156
+ ]
157
+ }
158
+ }
159
+ }
160
+ },
161
+ "validation_errors": {
162
+ "type": "array",
163
+ "minItems": 1,
164
+ "description": "The complete current validation errors. A proposal is produced only when every one of them is a duplicate-number error; any other error returns ledger-repair-not-applicable instead.",
165
+ "items": {
166
+ "$ref": "https://github.com/lstutzman/wowbagger/schemas/common.json#/$defs/validationError"
167
+ }
168
+ }
169
+ }
170
+ }
@@ -0,0 +1,61 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://github.com/lstutzman/wowbagger/schemas/ledger-repair-request.json",
4
+ "title": "number-repair request, ledger-repair version 1",
5
+ "description": "The strict JSON request `number-repair` reads from stdin or a host-created request file. One request covers the complete duplicate set: a partial mapping is refused, because moving one group cannot produce a valid complete ledger while another group remains. Each change carries the witnesses the apply path compares under the repair lock. `ledger-repair` is its own contract version, separate from the core contract version and the work-claim contract version.",
6
+ "type": "object",
7
+ "additionalProperties": false,
8
+ "required": [
9
+ "repair_id",
10
+ "ledger_snapshot_revision",
11
+ "date",
12
+ "changes"
13
+ ],
14
+ "properties": {
15
+ "repair_id": {
16
+ "type": "string",
17
+ "pattern": "^nr_[0-9]{8}_[0-9]{4}$",
18
+ "description": "The bounded recovery key: `nr_`, the repair date, and a four-digit sequence within that date. An interrupted apply is resumed by this ID rather than retried blindly."
19
+ },
20
+ "ledger_snapshot_revision": {
21
+ "$ref": "https://github.com/lstutzman/wowbagger/schemas/common.json#/$defs/revision",
22
+ "description": "The complete-snapshot witness the proposal was computed against. A changed snapshot is a conflict, never a repair applied to bytes the caller never read."
23
+ },
24
+ "date": {
25
+ "$ref": "https://github.com/lstutzman/wowbagger/schemas/common.json#/$defs/isoDate"
26
+ },
27
+ "changes": {
28
+ "type": "array",
29
+ "minItems": 1,
30
+ "description": "One entry per moved item. No `item_id` repeats, because an item moves once, and no `replacement_number` repeats, because two items cannot land on one number. An empty list is an invalid request rather than a no-op.",
31
+ "items": {
32
+ "type": "object",
33
+ "additionalProperties": false,
34
+ "required": [
35
+ "item_id",
36
+ "expected_revision",
37
+ "expected_number",
38
+ "replacement_number"
39
+ ],
40
+ "properties": {
41
+ "item_id": {
42
+ "$ref": "https://github.com/lstutzman/wowbagger/schemas/common.json#/$defs/itemId"
43
+ },
44
+ "expected_revision": {
45
+ "$ref": "https://github.com/lstutzman/wowbagger/schemas/common.json#/$defs/revision"
46
+ },
47
+ "expected_number": {
48
+ "type": "integer",
49
+ "minimum": 1,
50
+ "description": "The number the item carries now, compared as an integer. It is a witness, not a label."
51
+ },
52
+ "replacement_number": {
53
+ "type": "integer",
54
+ "minimum": 1,
55
+ "description": "The number the item is moved to. The apply path refuses a collision with any item that is not being moved."
56
+ }
57
+ }
58
+ }
59
+ }
60
+ }
61
+ }
@@ -0,0 +1,90 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://github.com/lstutzman/wowbagger/schemas/ledger-repair-response.json",
4
+ "title": "ledger-repair responses, contract version 1",
5
+ "description": "Every `number-repair-proposal` and `number-repair` --json response. The root carries a `namespace` member: dispatch on `namespace` before reading any version field. `contract_version` is the repair domain's own version 1 — the core contract version and the work-claim contract version are separate and unchanged by it. A future change to the request or the repaired source shape requires a new repair contract version rather than a silent reinterpretation of version 1.",
6
+ "type": "object",
7
+ "oneOf": [
8
+ {
9
+ "title": "repair success",
10
+ "type": "object",
11
+ "additionalProperties": false,
12
+ "required": [
13
+ "ok",
14
+ "namespace",
15
+ "command",
16
+ "contract_version",
17
+ "state",
18
+ "result"
19
+ ],
20
+ "properties": {
21
+ "ok": {
22
+ "const": true
23
+ },
24
+ "namespace": {
25
+ "const": "ledger-repair"
26
+ },
27
+ "command": {
28
+ "enum": [
29
+ "number-repair-proposal",
30
+ "number-repair"
31
+ ]
32
+ },
33
+ "contract_version": {
34
+ "const": 1
35
+ },
36
+ "state": {
37
+ "enum": [
38
+ "unchanged",
39
+ "committed"
40
+ ],
41
+ "description": "A proposal changes nothing, so it is always `unchanged`. An applied repair is `committed`."
42
+ },
43
+ "result": {
44
+ "type": "object",
45
+ "description": "The proposal result is the ledger-repair-proposal schema. The apply result names the repair ID, the snapshot it was applied against, the changed items, and the Git commit when one was established."
46
+ }
47
+ }
48
+ },
49
+ {
50
+ "title": "repair refusal",
51
+ "type": "object",
52
+ "additionalProperties": false,
53
+ "required": [
54
+ "ok",
55
+ "namespace",
56
+ "command",
57
+ "contract_version",
58
+ "state",
59
+ "error"
60
+ ],
61
+ "properties": {
62
+ "ok": {
63
+ "const": false
64
+ },
65
+ "namespace": {
66
+ "const": "ledger-repair"
67
+ },
68
+ "command": {
69
+ "enum": [
70
+ "number-repair-proposal",
71
+ "number-repair"
72
+ ]
73
+ },
74
+ "contract_version": {
75
+ "const": 1
76
+ },
77
+ "state": {
78
+ "enum": [
79
+ "unchanged",
80
+ "unknown"
81
+ ],
82
+ "description": "`unchanged` states that no ledger byte was written. `unknown` is reserved for an outcome this core could not resolve; it never accompanies a refusal raised before any write was attempted."
83
+ },
84
+ "error": {
85
+ "$ref": "https://github.com/lstutzman/wowbagger/schemas/common.json#/$defs/error"
86
+ }
87
+ }
88
+ }
89
+ ]
90
+ }
@@ -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.13
17
+ npm install -g wowbagger@0.1.0-alpha.17
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.13` and core `contract_version: 5`.**
41
+ `0.1.0-alpha.17` and core `contract_version: 5`.**
38
42
 
39
- The distribution pin names the published `0.1.0-alpha.13` release; the cut that
43
+ The distribution pin names the published `0.1.0-alpha.17` 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,32 @@ 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
+ Say what the fix does and does not cover. It closes the reported
222
+ PropertyCompass2 collision: cooperating alpha.14 worktrees of one clone that
223
+ share one Git common directory can no longer commit two items carrying the same
224
+ number. Separate clones, separate machines, alpha.13 writers before the hard
225
+ cutover, and noncooperating writes stay outside that fence and still rely on
226
+ branch integration plus `validate`.
227
+
187
228
  ## Writing
188
229
 
189
230
  Every write is an explicit, reviewable Git change. Show the user the command
@@ -434,6 +475,11 @@ authorized predecessor/successor window produces no finding, so another
434
475
  mutation can run before the first is committed. Do not mistake acceptance for
435
476
  durability. Commit each mutation anyway, then run `claim-verify`.
436
477
 
478
+ `create` never gets that window. A new item has no earlier authorized revision,
479
+ so Git `HEAD` is the only place its authorized bytes can live, and an
480
+ uncommitted create blocks every later mutation — including the next create —
481
+ with `git-finalization-required`.
482
+
437
483
  **`claim-verify` is the reconciliation procedure for that refusal.** Do not go
438
484
  looking for another verb; there is none. Read `details.findings`, do exactly
439
485
  what each finding's `remediation` string says (it names the path), run
@@ -491,7 +537,10 @@ commit. With `--auto-commit`, `changed_paths` matches `commit_paths`, and
491
537
  `git_commit` proves the commit.
492
538
 
493
539
  Batch work is where this bites: filing ten items means ten commits, not one
494
- commit at the end. Tell the user that before starting a batch.
540
+ commit at the end. Tell the user that before starting a batch. There is no
541
+ batch mutation to reach for — the create-then-commit loop is the supported bulk
542
+ pattern, and item #186 is open to design a safe batch create. `--auto-commit`
543
+ on each `create` is the shortest form of that loop.
495
544
 
496
545
  ### Or use --auto-commit and let one invocation do it
497
546
 
@@ -508,9 +557,11 @@ One flagged invocation refuses if anything is staged anywhere or any foreign
508
557
  path under the ledger is dirty, reconciles, runs the mutation unchanged,
509
558
  commits exactly the changed item plus at most one
510
559
  `.wowbagger/reconcile-<namespace>.md` with a fixed subject, verifies that commit,
511
- and runs `claim-verify` before it answers. A command that owns the claim journal
512
- may rebuild only its derived reconciliation log during preflight. `create`
513
- remains strict, and every other dirty ledger path still refuses.
560
+ and runs `claim-verify` before it answers. A successful `create --auto-commit`
561
+ commits exactly two paths: the created item and that reconciliation log. Every
562
+ command rebuilds its own derived reconciliation log during preflight, but
563
+ `create` refuses a log that was already dirty when you invoked it, and every
564
+ other dirty ledger path still refuses.
514
565
 
515
566
  Preflight and post-commit reconciliation block findings for the requested item.
516
567
  An unrelated `worktree-synchronization-required` finding remains visible to
@@ -610,29 +661,87 @@ envelope's `limits.cross_worktree_coordination: false` as permission to write
610
661
  with hostile or noncooperating tools — it only says the core never synchronizes
611
662
  checkouts.
612
663
 
613
- A recorded `transition`, `patch`, or claimed publication blocks mutations
614
- targeting that same item with exit 6 `claim-store-unavailable`, reason
615
- `publication-reconciliation-required`. An unrelated item mutation may proceed
616
- when the only finding is `worktree-synchronization-required`. `create` records
617
- nothing, so it never creates a publication block.
664
+ A recorded `create`, `transition`, `patch`, or claimed publication blocks
665
+ mutations targeting that same item with exit 6 `claim-store-unavailable`,
666
+ reason `publication-reconciliation-required`. An unrelated item mutation may
667
+ proceed when the only finding is `worktree-synchronization-required`.
668
+
669
+ `create` is the one exception to that scoping, because it allocates the next
670
+ number from the items this checkout can see. It also refuses when the journal
671
+ records a committed item this worktree does not hold at all: that item carries
672
+ a number nobody here can read, so the next number allocated here might already
673
+ be taken. A stale revision of an item this worktree does hold is not a blocker
674
+ for `create` — a number is immutable, so the local maximum is still right. The
675
+ refusal is the same exit 6 `claim-store-unavailable` with reason
676
+ `publication-reconciliation-required`, state `unchanged`, and no item file
677
+ written; integrate the missing item, run `claim-verify` until it exits 0, and
678
+ resend the same request, which then takes the next number.
679
+
680
+ That fence stops new collisions; it does not repair old ones through core
681
+ version 5 or claim operations. Existing duplicate numbers are item #182
682
+ recovery work. Use the separate `ledger-repair` contract:
683
+ `number-repair-proposal --ledger <dir> --json` is read-only and writes no item
684
+ file; `number-repair --ledger <dir> --input <repair.json> --json` applies a
685
+ reviewed complete mapping under the shared namespace fence. Number-only repair
686
+ preserves ULID identities and relation values. Never hand-edit the ledger:
687
+ arbitrary edits can damage IDs, paths, or references.
618
688
 
619
689
  Read `error.details.findings[0].reason` and act on the named item:
620
690
 
621
691
  - `git-finalization-required` — you wrote the item here and have not committed.
622
692
  Commit, then `claim-verify`.
623
- - `worktree-synchronization-required` — another worktree wrote the item. If the
693
+ - `worktree-synchronization-required` — another worktree wrote the item.
694
+ `owner_ref` names an **active named worktree** and nothing else: it is always
695
+ the branch of a live worktree that carries the expected revision. If the
624
696
  finding names `owner_ref` and `owner_commit`, WAIT for that owner to publish,
625
- then synchronize this checkout and run `claim-verify`. If it carries
626
- `owner_unavailable: true`, follow its `remediation`: a revision that is not
627
- yet reachable means WAIT for the owning worktree to commit, then synchronize;
628
- only ownership that cannot be established from reachable refs calls for
629
- inspecting reachable or dangling commits with explicit restore or
630
- `claim-adopt`. Never merge unrelated live work.
697
+ then synchronize this checkout and run `claim-verify`. `owner_unavailable:
698
+ true` means no such worktree exists, and it covers three cases: the expected
699
+ revision is not reachable at all, a live sibling holds it on a detached
700
+ `HEAD`, or it is reachable only from a tag, a remote-tracking ref, or a
701
+ branch no worktree has checked out. Reachability is not ownership; a ref you
702
+ can see is not a worktree that can publish. Follow the `remediation`, which
703
+ separates those cases. A revision that is **not yet reachable** means WAIT for
704
+ the owning worktree to commit, then synchronize. A revision that is
705
+ **reachable in Git while no active named worktree owner is established** —
706
+ a tag, a remote-tracking ref, an unchecked-out branch, or a detached sibling
707
+ carries it — is not a wait at all: inspect that reachable history, then
708
+ restore the authorized bytes or use explicit `claim-adopt` after review.
709
+ Ownership that **cannot be established from reachable refs** — the item has
710
+ never existed in this checkout — calls for inspecting reachable or dangling
711
+ commits with the same explicit restore or `claim-adopt`. Never merge unrelated
712
+ live work.
631
713
  - `unauthorized-revision` — the item changed outside the protocol. Two remedies
632
714
  are explicit: **restore** the authorized revision and run `claim-verify` to
633
715
  discard the edit, or **adopt** the committed revision and run `claim-verify`
634
716
  to keep it. Ask before discarding reviewed work.
635
717
 
718
+ Two live worktrees answering to one identity, or a worktree roster the
719
+ coordinator could not finish reading, refuse before anything is classified.
720
+ You get exit 6 `claim-store-unavailable`, reason `claim-store-unreadable`, with
721
+ `error.details.identity_diagnostic`: `duplicate-worktree-identity` naming the
722
+ `worktree_id` and `live_worktree_count`, or `worktree-enumeration-failed` with
723
+ no further member. Auto-commit reports the same diagnostic inside
724
+ `auto-commit-preflight-failed` with `retryable: false`. Neither is retryable
725
+ and neither is yours to repair by editing files. Report the diagnostic verbatim,
726
+ including the `worktree_id` and `live_worktree_count`: a duplicate means two
727
+ live worktrees hold the same identity file, usually because a private Git
728
+ directory was copied, and which worktree keeps the UUID is a person's decision.
729
+ An enumeration failure means a registered worktree path could not be read. The
730
+ identity itself is an opaque UUID a worktree writes once into its private Git
731
+ directory. Never create, copy, or edit it.
732
+
733
+ **`claim-verify` is repository-wide, and a clean mutation does not make it
734
+ exit 0.** It names no target, so any blocking finding anywhere in the
735
+ repository keeps it at exit 6 — including a finding on an item belonging to
736
+ work you have nothing to do with. On a repository with live sibling worktrees
737
+ you may commit every one of your own mutations and still never see
738
+ `claim-verify` exit 0. That is current behavior; item #184 is open in triage to
739
+ decide the supported verification surface. When the remaining findings all name
740
+ items you are not working on, say so and stop; do not hand-edit an item, and do
741
+ not run `claim-adopt` on a sibling's item to force exit 0. Adoption moves the
742
+ coordinator's authorized revision and is not a way to silence someone else's
743
+ finding.
744
+
636
745
  Adoption is per item and per revision explicit. Name the item and both
637
746
  revisions, take them from the finding, and commit the edited bytes first:
638
747
 
@@ -692,7 +801,9 @@ Use the claimed write path as one complete loop:
692
801
  9. Run `claim-verify` after the commit or merge. It finalizes the Git outcome,
693
802
  repairs response-loss cases, and reports later revision drift. Require exit
694
803
  0 before the next mutating command; exit 6 means findings remain, so act on
695
- each `remediation` string and run it again.
804
+ each `remediation` string and run it again. If every remaining finding names
805
+ an item you are not working on, that is the repository-wide scope described
806
+ above, not a failure of your work: report it instead of forcing exit 0.
696
807
  10. Release the claim with its current observed state.
697
808
  11. Run `validate` and show the resulting diff.
698
809
 
@@ -717,7 +828,8 @@ response envelopes, refusal precedence, and recovery rules.
717
828
  `git add <dir> && git commit`.
718
829
  8. On a provisioned ledger, run `claim-verify --ledger <dir> --json` and
719
830
  require exit 0 before the next `create`, `transition`, `parent-migrate`,
720
- `snooze`, `patch`, or `publish-claimed`.
831
+ `snooze`, `patch`, or `publish-claimed`. Findings that name only unrelated
832
+ items are repository-wide scope; report them rather than forcing exit 0.
721
833
 
722
834
  Write, commit, `claim-verify`, next write. The unclaimed loop obeys the same
723
835
  rule as the claimed one, because both run through the same coordinator. Steps 7
@@ -37,6 +37,12 @@ export async function withLegacyMutationFence(
37
37
  const capability = resolveWorkClaimCapability({ gitCommonDir, namespace });
38
38
  if (!capability.claim_protected_publication) return write();
39
39
 
40
+ // A create is the one mutation that reads an identity it was not given: it
41
+ // allocates the ledger's next number from the items this checkout holds. Its
42
+ // reconciliation therefore stays target-scoped like every other mutation, and
43
+ // gains one extra barrier below, for the coordinated items this working
44
+ // ledger cannot see at all.
45
+ const create = command === 'create-v1';
40
46
  const storePath = claimStorePath(gitCommonDir, namespace);
41
47
  const journalPath = claimJournalPath(gitCommonDir, namespace);
42
48
  let intent = null;
@@ -61,9 +67,15 @@ export async function withLegacyMutationFence(
61
67
  physicalNow: new Date().toISOString(),
62
68
  targetItemId: itemId,
63
69
  writeLogOnUnsafe: false,
64
- writeLogWhenEmpty: command !== 'create-v1',
70
+ writeLogWhenEmpty: !create,
65
71
  });
66
- if (reconciled.unsafe) {
72
+ // The extra create barrier promised above rides on the same refusal: a
73
+ // coordinated item this checkout does not hold carries a number nobody
74
+ // here can read, so the next number this create would allocate may be one
75
+ // a sibling worktree already published. A stale revision of an item that
76
+ // is present hides no number: an item's number is immutable, so target
77
+ // scoping above still lets that create through.
78
+ if (reconciled.unsafe || (create && reconciled.missingCoordinatedItems.length > 0)) {
67
79
  return claimStoreUnavailable(responseCommand, 'publication-reconciliation-required', {
68
80
  findings: reconciled.findings,
69
81
  });
@@ -75,7 +87,7 @@ export async function withLegacyMutationFence(
75
87
  const projected = [...reconciled.entries];
76
88
  const record = reconciled.state.claims.find((entry) => entry.item_id === itemId)
77
89
  ?? { item_id: itemId, last_epoch: '0', active: null };
78
- const mustRefuse = command === 'create-v1'
90
+ const mustRefuse = create
79
91
  ? record.last_epoch !== '0'
80
92
  : record.active !== null && observedAt < record.active.expires_at;
81
93
  if (mustRefuse) return legacyRefusal(responseCommand, namespace, itemId, observedAt, record);
@@ -110,6 +122,7 @@ export async function withLegacyMutationFence(
110
122
  attempt_id: attemptId,
111
123
  ledger_namespace: namespace,
112
124
  item_id: itemId,
125
+ ...(create ? { command } : {}),
113
126
  observed_revision: expectedRevision,
114
127
  observed_at: observedAt,
115
128
  };
@@ -180,6 +193,7 @@ export async function withLegacyMutationFence(
180
193
  attempt_id: intent.attempt_id,
181
194
  ledger_namespace: namespace,
182
195
  item_id: itemId,
196
+ ...(create ? { command } : {}),
183
197
  observed_revision: intent.expected_revision,
184
198
  observed_at: observedAt,
185
199
  }));