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.
package/CHANGELOG.md CHANGED
@@ -7,6 +7,255 @@ consolidation. The first tagged release inherits this file.
7
7
 
8
8
  ## Unreleased
9
9
 
10
+ ## 0.1.0-alpha.17 - 2026-08-30
11
+
12
+ - **Breaking:** raise the supported Node.js floor from 20 to 24. Node 20 and
13
+ Node 22 no longer satisfy `engines.node`; Node 26 remains outside the
14
+ supported matrix because of the separate Vitest incompatibility reported by
15
+ Lee.
16
+
17
+ The alpha.14 hard cutover remains the baseline: `claim-store-unavailable`
18
+ answers **The durable claim store is unavailable.** with
19
+ `claim-store-unreadable`; upgrade every writer before the first alpha.14 create.
20
+ There is no automatic migration or mixed-version grace period. There is no
21
+ batch mutation, the create-then-commit loop remains supported, item #186 owns
22
+ batch design, and item #182 owns existing duplicate numbers. The fence adds no
23
+ new Git roster or history traversal and costs two extra fsync'd journal appends
24
+ within the 65,536-entry limit.
25
+
26
+
27
+ ## 0.1.0-alpha.16 - 2026-08-30
28
+
29
+ - Added `version-drift --json` to detect stale installed skill pins, core
30
+ contract versions, and package provenance before ledger mutation.
31
+
32
+ The alpha.14 hard cutover remains the baseline: `claim-store-unavailable`
33
+ answers **The durable claim store is unavailable.** with
34
+ `claim-store-unreadable`; upgrade every writer before the first alpha.14
35
+ create. There is no automatic migration or mixed-version grace period. There is
36
+ no batch mutation, the create-then-commit loop remains supported, item #186 owns
37
+ batch design, and item #182 owns existing duplicate numbers. The fence adds no
38
+ new Git roster or history traversal and costs two extra fsync'd journal appends
39
+ within the 65,536-entry limit.
40
+
41
+ ## 0.1.0-alpha.15 - 2026-08-30
42
+
43
+ - **Duplicate-number recovery now has a separate `ledger-repair` contract
44
+ version 1.** Use the read-only `number-repair-proposal` command to review a
45
+ complete mapping, then apply it with `number-repair`. The repair path requires
46
+ duplicate-number errors to be the complete validation failure, preserves ULID
47
+ identities and relation values, uses the shared namespace fence, records
48
+ durable intent/final entries, and supports bounded auto-commit recovery.
49
+
50
+ The alpha.14 hard cutover remains the baseline: `claim-store-unavailable`
51
+ answers **The durable claim store is unavailable.** with
52
+ `claim-store-unreadable`; upgrade every writer before the first alpha.14
53
+ create. There is no automatic migration or mixed-version grace period. There is
54
+ no batch mutation, the create-then-commit loop remains supported, item #186 owns
55
+ batch design, and item #182 owns existing duplicate numbers.
56
+ The fence adds no new Git roster or history traversal and costs two extra
57
+ fsync'd journal appends within the 65,536-entry limit.
58
+
59
+ ## 0.1.0-alpha.14 - 2026-08-29
60
+
61
+ ### Changed
62
+
63
+ - **Upgrading is a hard cutover: upgrade every writer in one Git coordination
64
+ domain before the first alpha.14 create.** The journal grammar widened —
65
+ `legacy-mutation-intent` and `legacy-mutation` accept `command: "create-v1"`,
66
+ a create intent accepts `expected_revision: null`, and a create abort requires
67
+ `command: "create-v1"` with `observed_revision: null` — and an alpha.13 binary
68
+ cannot read the new create entry. Run against a ledger whose shared journal
69
+ already carries one, alpha.13's `create` exits 6 with `error.code`
70
+ `claim-store-unavailable`, message `The durable claim store is unavailable.`,
71
+ and `error.details.reason` `claim-store-unreadable`, leaves state unchanged,
72
+ and writes no item. Failing closed is the compatibility guarantee: an old
73
+ writer cannot commit another duplicate. It is also the operational limit,
74
+ because alpha.13 is immutable and can only emit that generic unreadable-store
75
+ message. Read it as "this repository was written by a newer Wowbagger; upgrade
76
+ this worktree to continue." There is no automatic migration and no
77
+ mixed-version grace period; a partial upgrade leaves the remaining alpha.13
78
+ worktrees unable to make claim-protected mutations until they move. Item #185
79
+ owns general version-drift detection; nothing here can retrofit a message into
80
+ an already-published executable. The new reader executes every journal
81
+ alpha.13 emitted, which is the backward-compatibility direction that had to
82
+ hold.
83
+
84
+ - **A create must be committed before the next mutation, and there is still no
85
+ batch operation.** A created item has no earlier authorized revision, so Git
86
+ `HEAD` is the only surface that can carry its authorized bytes; an uncommitted
87
+ create raises the global `git-finalization-required` barrier for every later
88
+ mutation, including the next create. It cannot occupy the authorized
89
+ predecessor/successor window a `patch` or `transition` may occupy, and that
90
+ window was never permission to skip a commit. The supported bulk pattern is
91
+ the create-then-commit loop — filing ten items is ten cycles, and
92
+ `--auto-commit` on each create is its shortest form. Safe batch design is
93
+ item #186.
94
+
95
+ - **The measured cost of the fence is two extra fsync'd journal appends.** A
96
+ provisioned create already took the namespace lock, replayed the journal,
97
+ loaded the ledger, read Git `HEAD`, and reconciled, so the fence adds no new
98
+ Git roster or history traversal to a clean create; on a 1,500-item fixture the
99
+ captured `git` invocation sequence is identical before and after. The durable
100
+ cost is journal growth: each successful create adds one intent and one
101
+ committed terminal, so with no other activity the 65,536-entry limit permits
102
+ at most 21,845 three-entry create cycles, and the 8 MiB byte limit may bind
103
+ first. Other claim and mutation activity lowers that ceiling. Capacity is
104
+ reserved before the intent append, so an exhausted journal refuses unchanged
105
+ rather than stranding an attempt. Journal compaction is not part of this
106
+ change.
107
+
108
+ ### Fixed
109
+
110
+ - **Two worktrees can no longer commit two items carrying the same number.**
111
+ Through alpha.13, `create` was journal-silent by design: it derived
112
+ `1 + max(existing numbers)` from the items its own checkout held and recorded
113
+ nothing in the shared claim journal. Two worktrees that had not integrated
114
+ each other's commits therefore derived the same number and both published,
115
+ and they did not have to race to do it — a create today and a create tomorrow
116
+ collided just as reliably, because the loser was a stale checkout rather than
117
+ a lost race. `number` is immutable and `patch` correctly refuses it, so the
118
+ collision surfaced only at integration, as a global `duplicate-number`
119
+ validation failure that stopped the whole ledger. On a provisioned ledger,
120
+ `create` is now a journaled legacy mutation: under the shared namespace lock
121
+ it reconciles, validates the complete candidate ledger, reserves journal
122
+ capacity, appends a `legacy-mutation-intent` with `command: "create-v1"` and
123
+ `expected_revision: null` before any byte reaches the item path, publishes
124
+ atomically and no-clobber, verifies the exact bytes, and appends its terminal
125
+ — `legacy-mutation` when the bytes landed, or an abort carrying
126
+ `command: "create-v1"` and `observed_revision: null` when the path stayed
127
+ absent. Allocation is fenced in front of all of that: every global finding
128
+ blocks `create` as it blocks any write, and in addition any coordinated item
129
+ this checkout does not hold blocks `create`, because its number cannot be
130
+ read here. A stale revision of an item this checkout does hold stays
131
+ nonblocking, because a number is immutable. A fenced create refuses with
132
+ exit 6, `state: "unchanged"`, `error.code: "claim-store-unavailable"`,
133
+ `error.details.reason: "publication-reconciliation-required"`, and no item
134
+ file; integrate the named item, run `claim-verify` to exit 0, and resend the
135
+ same request ID to take the next number. **What this fixes and what it does
136
+ not:** it closes the reported PropertyCompass2 collision, so cooperating
137
+ alpha.14 worktrees of one clone that share one Git common directory can no
138
+ longer commit the same number, because every such create is visible through
139
+ the shared journal before publication even without branch integration.
140
+ Separate clones, separate machines, alpha.13 writers before the hard cutover,
141
+ and noncooperating writes stay outside that fence and still rely on branch
142
+ integration plus `validate`. A ledger that already carries duplicate numbers
143
+ is not repaired here; item #182 owns that fenced recovery. Core
144
+ `contract_version` stays `5` and the create success and refusal envelopes add,
145
+ remove, and rename no member.
146
+
147
+ - **`create --auto-commit` commits its reconciliation log with the item.**
148
+ Because create now owns journal entries, its commit set is exactly the created
149
+ item and `<ledger>/.wowbagger/reconcile-<namespace>.md`, and
150
+ `mutation-finalize` accepts that same two-path recovery token. Between the
151
+ fence landing and this fix, `create --auto-commit` failed outright with
152
+ `git-commit-failed`, `failure_stage: "prepare-commit-set"`, and
153
+ `reason: "tree-changed"`, because the commit set excluded a log the mutation
154
+ had just written. Owning that log is not absorbing what was already in it:
155
+ `create` still refuses a reconciliation log that was dirty before the
156
+ invocation, and every other dirty ledger path still refuses.
157
+
158
+ - **A revision Git can already reach is no longer an instruction to wait for
159
+ it.** When the expected revision was reachable only from a tag, a
160
+ remote-tracking ref, a branch no worktree had checked out, or a live worktree
161
+ on a detached `HEAD`, the `worktree-synchronization-required` finding rendered
162
+ the not-yet-reachable sentence: "wait for the owning worktree to commit, then
163
+ synchronize this worktree and run claim-verify." The commit already existed
164
+ and no named worktree was ever going to publish it, so that wait could not
165
+ end. Those cases now render: "Revision <revision> of <path> is reachable in
166
+ Git, but no active named worktree owner is established; inspect the reachable
167
+ history, restore or explicitly adopt reviewed bytes, then run claim-verify."
168
+ A revision no reachable commit carries keeps the not-yet-reachable sentence
169
+ and its wait, and an item that has never existed in this checkout keeps the
170
+ cannot-be-established sentence. **A consumer that matches on `remediation`
171
+ text must update its patterns:** a finding that used to match "not yet
172
+ reachable" now matches "reachable in Git" for the four reachable-unowned
173
+ cases. Nothing else moves. The `reason` stays
174
+ `worktree-synchronization-required`, the code stays `stale-write-detected`,
175
+ `owner_unavailable: true` with no `owner_ref` or `owner_commit` is unchanged,
176
+ the finding stays advisory — it blocks a mutation targeting its own item and
177
+ lets an unrelated mutation proceed — and core `contract_version` stays `5`,
178
+ because no field, code, reason, or scope changes and the text correction
179
+ replaces a false instruction.
180
+
181
+ ### Documentation
182
+
183
+ - **Worktree identity and its refusals are now documented.** Alpha.13 shipped an
184
+ explicit worktree identity — an opaque random UUID a worktree creates once in
185
+ its private Git directory — and started recording it as the optional
186
+ `writer_worktree_id` member on `legacy-mutation-intent`, `legacy-mutation`,
187
+ `publish-intent`, and `publish-final` journal entries. Reconciliation reads it
188
+ to tell this worktree's own unreachable successor from a sibling's, which is
189
+ what turns that topology into a global `unauthorized-revision` barrier instead
190
+ of advisory `worktree-synchronization-required`. Alpha.13 also began refusing
191
+ before it classifies anything when two live worktrees answer to one UUID, or
192
+ when the worktree roster cannot be read to completion: exit 6
193
+ `claim-store-unavailable`, reason `claim-store-unreadable`, plus one new
194
+ `error.details.identity_diagnostic` carrying `duplicate-worktree-identity`
195
+ with `worktree_id` and `live_worktree_count`, or `worktree-enumeration-failed`
196
+ with no further member, and the same diagnostic inside
197
+ `auto-commit-preflight-failed` with `retryable: false`. All of that shipped
198
+ without a release note; the alpha.13 entry mentioned writer identity only in
199
+ passing while describing which callers read it. The work-claim contract
200
+ section 3.1, the mutation contract auto-commit preflight details, and the
201
+ installed skill now state the journal field, its alpha.12-and-earlier
202
+ compatibility (such entries stay valid, attribute nothing, and leave the
203
+ writer unknown), what the identity discloses in committed history, and both
204
+ diagnostics. Core `contract_version` stays `5` and no public request or
205
+ success envelope changes.
206
+
207
+ - **Owner evidence names only an active named worktree, and the contract now
208
+ says so.** Through alpha.12 the owner search was a reachability search, so a
209
+ finding could report `owner_ref` naming a tag, a remote-tracking ref, or a
210
+ branch no worktree had checked out — an owner that could never publish
211
+ anything, and an instruction to wait forever. Alpha.13 replaced that with the
212
+ live worktree roster: this checkout first, then live worktrees on a branch
213
+ ordered by branch ref and then path, with bare and prunable records excluded.
214
+ `owner_ref` is therefore always the branch of a live worktree. A revision
215
+ reachable only from a tag, a remote-tracking ref, an unchecked-out branch, or
216
+ a live worktree on a detached `HEAD` now reports `owner_unavailable: true` and
217
+ no `owner_ref`. That narrowing shipped in alpha.13 with no release note, and
218
+ the alpha.11 note promising that a finding "retains that `owner_ref` and
219
+ `owner_commit`" no longer describes those cases. The contract and the
220
+ installed skill now state the roster rule, the three distinct meanings of
221
+ `owner_unavailable`, and which remediation sentence each one gets.
222
+
223
+ - **Repository-wide `claim-verify` and target-scoped mutation are stated as
224
+ different questions.** The mutation contract still said a recorded write in
225
+ one worktree "refuses every mutation in the others", which target scoping
226
+ stopped being true before alpha.11. It now states the item scope, and both
227
+ contracts and the installed skill now say plainly that a successful mutation
228
+ is not proof of a globally clean claim store: `claim-verify` names no target,
229
+ so it stays exit 6 while any item in the repository carries a blocking
230
+ finding, including an unrelated one. Item #184 is open in triage to decide the
231
+ supported verification surface; nothing about that behavior changed here, and
232
+ a gate that demands a globally clean `claim-verify` on a repository with live
233
+ sibling work is still unsatisfiable.
234
+
235
+ - **A refused reconciliation still persists the clock floor.** Reconciliation
236
+ has written its clock entry before it classifies anything since work claims
237
+ were implemented, so a command that then refuses with
238
+ `publication-reconciliation-required` has already advanced the durable
239
+ monotonic floor to `max(physical_utc, previous_floor)`. Section 4 documented
240
+ the floor only for authoritative lease decisions, so the pre-decision advance
241
+ and its one observable consequence were undocumented: because the floor never
242
+ rolls back, a lease or fence observed expired at that floor can never read
243
+ live again, and a holder on a ledger whose floor runs ahead of its own wall
244
+ clock may find its lease expired the moment it asks after clearing a barrier.
245
+ Behavior is unchanged; the contract now says it.
246
+
247
+ - **The reconciliation topology classifier moved without changing an answer.**
248
+ The topology decision that was spread across owner lookup, diagnosis, and
249
+ scope inference now lives in one pure module,
250
+ `src/reconciliation-classifier.js`, which takes normalized evidence and
251
+ returns a typed decision. That type is internal: scope travels beside a
252
+ finding and no response has ever carried it, so a consumer must keep reading
253
+ the refusal rather than the `reason` string. Every public seam — reason,
254
+ blocking behavior, owner fields, remediation sentence, exit, state, and
255
+ envelope domain — is byte-identical to alpha.13 on every matrix row, which is
256
+ the whole point of the change and the only claim made for it. No behavior
257
+ shipped with it.
258
+
10
259
  ## 0.1.0-alpha.13 - 2026-08-28
11
260
 
12
261
  ### Fixed
package/README.md CHANGED
@@ -30,8 +30,8 @@ agent to use those guarantees instead of hand-editing your Markdown.
30
30
 
31
31
  **Start here:** [install the core and set up a ledger](#start-here).
32
32
 
33
- > **Status: alpha, published, and self-hosted.** `0.1.0-alpha.13` is on npm under
34
- > the `next` tag and on this repository's `v0.1.0-alpha.13` tag. It is the
33
+ > **Status: alpha, published, and self-hosted.** `0.1.0-alpha.17` is on npm under
34
+ > the `next` tag and on this repository's `v0.1.0-alpha.17` tag. It is the
35
35
  > version this repository runs its own backlog on. The API is not frozen and the
36
36
  > version will move before a stable release.
37
37
  >
@@ -77,7 +77,7 @@ Wowbagger is the core authority for a Git-native work ledger. Use it instead
77
77
  of editing ledger Markdown by hand.
78
78
 
79
79
  ```sh
80
- wowbagger --version # require 0.1.0-alpha.13
80
+ wowbagger --version # require 0.1.0-alpha.17
81
81
  wowbagger capabilities --json # require contract_version: 5
82
82
  wowbagger validate --ledger ledger --json
83
83
  wowbagger ready --ledger ledger --as-of YYYY-MM-DD --json
@@ -103,10 +103,10 @@ cooperating writers; they are not exclusive locks.
103
103
  Install the core CLI, then verify it. The core requires Node.js 20 or later:
104
104
 
105
105
  ```sh
106
- npm install -g wowbagger@0.1.0-alpha.13 # exact plugin-matched release
106
+ npm install -g wowbagger@0.1.0-alpha.17 # exact plugin-matched release
107
107
  # or, from this release's Git tag:
108
- # npm install -g github:lstutzman/wowbagger#v0.1.0-alpha.13
109
- wowbagger --version # 0.1.0-alpha.13
108
+ # npm install -g github:lstutzman/wowbagger#v0.1.0-alpha.17
109
+ wowbagger --version # 0.1.0-alpha.17
110
110
  wowbagger capabilities --json # must report contract_version: 5
111
111
  ```
112
112
 
@@ -325,7 +325,7 @@ two supported install routes:
325
325
  registry requires a `latest` tag), so a bare install resolves to the same
326
326
  bytes.
327
327
  - **git tag** —
328
- `npm install -g github:lstutzman/wowbagger#v0.1.0-alpha.13` installs this
328
+ `npm install -g github:lstutzman/wowbagger#v0.1.0-alpha.17` installs this
329
329
  release. Installing at a ref installs the core and every adapter that ref
330
330
  carries.
331
331
 
@@ -397,7 +397,7 @@ Upgrade the pieces you installed:
397
397
 
398
398
  ```sh
399
399
  npm install -g wowbagger@next # public npm registry
400
- npm install -g github:lstutzman/wowbagger#v0.1.0-alpha.13 # immutable Git release
400
+ npm install -g github:lstutzman/wowbagger#v0.1.0-alpha.17 # immutable Git release
401
401
  git pull && npm ci # or: a direct checkout
402
402
  ```
403
403
 
@@ -445,6 +445,16 @@ core, these are the changes most likely to touch you:
445
445
  `create` and refuses a caller-supplied one; `patch` refuses it because it is
446
446
  immutable identity. Keep a legacy identifier in a declared extension member or
447
447
  in the item body.
448
+
449
+ - **Repair duplicate numbers through `ledger-repair` version 1.** Generate a
450
+ read-only proposal with `number-repair-proposal`, review every
451
+ `expected_revision` and `replacement_number`, then apply the complete mapping
452
+ with `number-repair`. The command preserves ULID identities and relations and
453
+ does not change core contract version 5.
454
+
455
+ - **Run `version-drift --json` before mutation.** It compares the installed
456
+ skill pin, required core contract, and running core, and names the stale
457
+ package, plugin cache, or linked checkout with remediation.
448
458
  - **Delete your local ULID generator.** `wowbagger mint-id --json` prints a
449
459
  canonical ID; `--date YYYY-MM-DD` selects the creation date the ID must
450
460
  encode.
@@ -1171,18 +1181,20 @@ the finding as a ledger item rather than leaving it in a transcript.
1171
1181
 
1172
1182
  ### The verification gate
1173
1183
 
1174
- Four commands. All four must pass, and the test commands run on **both** the
1175
- current Node runtime and Node 20:
1184
+ Four commands. All four must pass, and the test commands run on **Node 24.20.0**:
1176
1185
 
1177
1186
  ```sh
1178
- TMPDIR=/tmp node --test test/*.test.js
1179
- TMPDIR=/tmp /opt/homebrew/opt/node@20/bin/node --test test/*.test.js
1180
- TMPDIR=/tmp node spec/run-adapter-implementation.js
1181
- node bin/wowbagger.js validate --ledger ledger --json
1187
+ TMPDIR=/tmp /opt/homebrew/opt/node@24/bin/node --test test/*.test.js
1188
+ TMPDIR=/tmp /opt/homebrew/opt/node@24/bin/node --pending-deprecation --throw-deprecation --test test/*.test.js
1189
+ TMPDIR=/tmp /opt/homebrew/opt/node@24/bin/node spec/run-adapter-implementation.js
1190
+ TMPDIR=/tmp /opt/homebrew/opt/node@24/bin/node bin/wowbagger.js validate --ledger ledger --json
1182
1191
  ```
1183
1192
 
1184
1193
  `TMPDIR=/tmp` is not optional: the default macOS temporary path makes the claim
1185
- lock socket path too long. Substitute your own Node 20 binary path.
1194
+ lock socket path too long. Use an explicit Node 24.20.0 binary path.
1195
+
1196
+ The supported runtime matrix is Node 24.20.0. Node 26 remains excluded until
1197
+ the separate Vitest incompatibility reported by Lee is resolved.
1186
1198
 
1187
1199
  `npm test`, `npm audit --omit=dev`, and `git diff --check` are useful alongside
1188
1200
  it; they are not a substitute for the four commands above.
@@ -26,10 +26,13 @@ shell: an absolute Node executable, the absolute `wowbagger.js` the package
26
26
  installed, an argument array, and `shell: false`. Neither path is discovered by
27
27
  searching a global npm directory, and neither is a platform command shim.
28
28
 
29
- Wowbagger requires Node.js 20 or later. The package declares that floor in
29
+ Wowbagger requires Node.js 24 or later. The package declares that floor in
30
30
  `engines.node`, and the launch seam exports it as `MINIMUM_NODE_MAJOR` for a
31
31
  host that resolves its own runtime instead of reusing the one it is running on.
32
32
 
33
+ The supported release matrix is Node 24.20.0. Node 26 is excluded until the
34
+ separate Vitest incompatibility reported by Lee is resolved.
35
+
33
36
  ~~~js
34
37
  import { resolveCoreLaunch } from 'wowbagger';
35
38
 
@@ -258,6 +261,9 @@ not a fetch URL.
258
261
  | `bare-ready-result.json` | bare result | a `ready` success |
259
262
  | `ledger-mutation-refusal.json` | ledger-mutation 1 | the legacy-write fence refusals |
260
263
  | `report-config-v1.json` | report config 1 | `<ledger>/.wowbagger/report.json` at version 1 |
264
+ | `ledger-repair-request.json` | ledger-repair 1 | the strict `number-repair` request |
265
+ | `ledger-repair-proposal.json` | ledger-repair 1 | the read-only `number-repair-proposal` result |
266
+ | `ledger-repair-response.json` | ledger-repair 1 | every `number-repair-proposal` and `number-repair` response |
261
267
  | `report-config-v2.json` | report config 2 | the same file at version 2, which names views |
262
268
 
263
269
  Every schema fixes its root members exactly and pins the version of its own
@@ -376,6 +376,7 @@ answer in two domains.
376
376
  | work-claim | `work-claim` | 1, the legacy envelope marker | `result.operations.work_claim.api_version` of `claim capabilities --json` |
377
377
  | ledger-publication | `ledger-publication` | 1, the legacy envelope marker | the same work-claim `api_version` |
378
378
  | ledger-mutation | `ledger-mutation` | 1, the legacy envelope marker | the same work-claim `api_version` |
379
+ | ledger-repair | `ledger-repair` | 1 | `contract_version` of `number-repair-proposal` or `number-repair` |
379
380
  | bare result | absent, and no `ok` member either | none | none |
380
381
 
381
382
  The rule has three steps:
@@ -401,6 +402,7 @@ legacy claim-envelope marker, and a consumer must never compare it with the core
401
402
  | `inspect` | core | core |
402
403
  | `list` | core | core |
403
404
  | `mint-id` | core | core |
405
+ | `version-drift` | core | core |
404
406
  | `report` | core | core |
405
407
  | `create` | core | core, or ledger-mutation when the claim fence refuses |
406
408
  | `transition` | core | core, or ledger-mutation when the claim fence refuses |
@@ -415,7 +417,8 @@ legacy claim-envelope marker, and a consumer must never compare it with the core
415
417
  | `claim-sync` | work-claim | work-claim |
416
418
  | `claim-adopt` | work-claim | work-claim |
417
419
  | `mutation-finalize` | work-claim | work-claim |
418
- | `claim verify` | ledger-publication, `command: "read"` | ledger-publication |
420
+ | `number-repair-proposal` | ledger-repair | ledger-repair |
421
+ | `number-repair` | ledger-repair | ledger-repair |
419
422
  | `publish-claimed` | ledger-publication | ledger-publication |
420
423
 
421
424
  **Exact root members**
@@ -792,10 +795,19 @@ another. It is **not** a statement that worktrees write independently.
792
795
 
793
796
  On a provisioned Git-backed ledger they do not. One claim journal lives in the
794
797
  shared Git common directory, so it serializes every worktree of that
795
- repository: a recorded `transition` or `patch` in one worktree refuses every
796
- mutation in the others with exit 6 `claim-store-unavailable`, reason
798
+ repository. The serialization is scoped to the item: a recorded `transition` or
799
+ `patch` in one worktree refuses a mutation in the others that targets *that
800
+ item* with exit 6 `claim-store-unavailable`, reason
797
801
  `publication-reconciliation-required`, until the writing commit is visible in
798
- the blocked checkout. Clones do not share the common directory, so
802
+ the blocked checkout. A mutation targeting an unrelated item still runs, and
803
+ the sibling's finding remains visible. Own uncommitted work and
804
+ out-of-protocol revisions are global barriers and refuse every mutation. A
805
+ repository-wide `claim-verify` names no target, so it reports exit 6 while any
806
+ item carries a blocking finding, including an unrelated one: a successful
807
+ mutation is therefore not proof of a globally clean claim store. See
808
+ [the work-claim contract](work-claim-contract.md), section 3.2, for the scopes
809
+ and for the open item #184 that owns the verification-surface decision. Clones
810
+ do not share the common directory, so
799
811
  `limits.cross_clone_coordination: false` carries no such consequence.
800
812
 
801
813
  That serialization is discoverable, per ledger, at
@@ -1558,6 +1570,68 @@ no-clobber publication still protects an intervening creator.
1558
1570
  Successful create returns state committed and the inspect item shape from
1559
1571
  section 5.
1560
1572
 
1573
+ ### Journal-fenced allocation on a provisioned ledger
1574
+
1575
+ On a provisioned merge-coordinated ledger, a schema-version-2 create is a
1576
+ journaled legacy mutation. Under the shared namespace lock it reconciles,
1577
+ applies the allocation fence below, loads and validates the ledger, derives
1578
+ `1 + max(existing numbers)`, serializes the candidate, validates the complete
1579
+ candidate ledger, reserves journal capacity, and only then appends its
1580
+ `legacy-mutation-intent` with `command: "create-v1"` before any byte reaches
1581
+ the item path. Publication, exact-byte verification, and the terminal append
1582
+ all happen under that same lock. The intent carries
1583
+ `expected_revision: null`, which is valid only for `create-v1`. The committed
1584
+ terminal is `legacy-mutation` with `command: "create-v1"`; the create abort
1585
+ carries `command: "create-v1"` and `observed_revision: null`. No entry records
1586
+ the assigned number, because the candidate revision binds the complete item
1587
+ bytes and the ledger remains the one number authority.
1588
+
1589
+ The allocation fence reads reconciliation with one extra barrier. Every global
1590
+ finding blocks create as it blocks every write. On top of those, any
1591
+ coordinated item this checkout does not hold blocks `create`, because the
1592
+ journal records a committed terminal for an item whose number this working
1593
+ ledger cannot read. A stale revision of an item this checkout holds does not
1594
+ block `create`, because a number is immutable and the local maximum is already
1595
+ correct. A fenced create refuses with exit 6, `state: "unchanged"`,
1596
+ `error.code: "claim-store-unavailable"`, and
1597
+ `error.details.reason: "publication-reconciliation-required"` in the
1598
+ `ledger-mutation` domain as `create-v1`, having written no item byte. Resolve
1599
+ each finding by its own `remediation`, run `claim-verify` until it exits 0, and
1600
+ retry the same request ID; the retry takes the next number.
1601
+
1602
+ A create has no authorized predecessor, so Git `HEAD` is the only surface that
1603
+ can carry its authorized bytes, and an uncommitted create raises the global
1604
+ `git-finalization-required` barrier for every later mutation. Commit each
1605
+ created item before the next mutating command. This release adds no batch
1606
+ mutation; the supported bulk pattern remains the create-then-commit loop.
1607
+ A ledger that already carries duplicate numbers is item #182 recovery work and
1608
+ is repaired through the separate `ledger-repair` contract, not through core
1609
+ version 5 mutation. Run
1610
+ `number-repair-proposal --ledger <dir> --json`, review the complete mapping, then
1611
+ run `number-repair --ledger <dir> --input <repair.json> --json`. The repair
1612
+ command operates only when duplicate-number errors are the complete validation
1613
+ failure, preserves ULID identities and relation values, and publishes all
1614
+ affected items under the shared namespace fence. Arbitrary hand edits remain
1615
+ unsupported because they can damage IDs, paths, or references.
1616
+
1617
+ **What this guarantees, and what it does not.** The fence closes the reported
1618
+ PropertyCompass2 collision: cooperating alpha.14 worktrees of one clone that
1619
+ share one Git common directory can no longer commit two items carrying the same
1620
+ number, because every such create is visible through the shared journal before
1621
+ publication even without branch integration. Separate clones, separate
1622
+ machines, alpha.13 writers before the hard cutover, and noncooperating writes
1623
+ stay outside the fence and still rely on branch integration plus `validate`.
1624
+
1625
+ The widened journal grammar forces a hard cutover, so upgrade every writer in
1626
+ one Git coordination domain before the first alpha.14 create. An alpha.13
1627
+ binary cannot read a `create-v1` intent: it refuses with exit 6,
1628
+ `error.code` `claim-store-unavailable`, message
1629
+ `The durable claim store is unavailable.`, and `error.details.reason`
1630
+ `claim-store-unreadable`, leaving state unchanged and writing no item. That
1631
+ refusal means **this repository was written by a newer Wowbagger; upgrade this
1632
+ worktree to continue.** There is no automatic migration and no mixed-version
1633
+ grace period.
1634
+
1561
1635
  ## 8. Transition
1562
1636
 
1563
1637
  ### Request
@@ -2626,6 +2700,13 @@ That window produces no finding, so another mutation can run before the first
2626
2700
  one is committed. The later command's acceptance does not prove durability;
2627
2701
  the operating rule remains write, commit, `claim-verify`, next write.
2628
2702
 
2703
+ `create` never occupies that window. A created item has no earlier authorized
2704
+ revision, so Git `HEAD` is the only surface that can carry its authorized
2705
+ bytes, and an uncommitted create raises the global `git-finalization-required`
2706
+ barrier for every later mutation, including the next create. Filing ten items
2707
+ is therefore ten create-then-commit cycles, not one commit at the end. This
2708
+ release adds no batch mutation; item #186 owns safe batch design.
2709
+
2629
2710
  The loop that works:
2630
2711
 
2631
2712
  ~~~sh
@@ -2697,7 +2778,13 @@ envelope, subject and commit set included.
2697
2778
  For every blocking finding:
2698
2779
 
2699
2780
  1. Do what `remediation` says, for each finding, using its `expected_path`.
2700
- `git-finalization-required` means commit that path.
2781
+ `git-finalization-required` means commit that path. A
2782
+ `worktree-synchronization-required` finding means wait only when its
2783
+ `remediation` names an owner to wait for or says the expected revision is not
2784
+ yet reachable. When it says the revision is reachable in Git while no active
2785
+ named worktree owner is established, there is no commit left to wait for:
2786
+ inspect that reachable history, then restore or explicitly adopt reviewed
2787
+ bytes.
2701
2788
  2. Run `wowbagger claim-verify --ledger <dir> --json`.
2702
2789
  3. Exit 0 with `state: "committed"` means the ledger is reconciled and the next
2703
2790
  mutating command may run. Exit 6 means findings remain; repeat from step 1.
@@ -2747,7 +2834,7 @@ by sending the flag, not by reading a version.
2747
2834
 
2748
2835
  Only the paths this invocation owns:
2749
2836
 
2750
- | `create` | the created item |
2837
+ | `create` | the created item and one `<ledger>/.wowbagger/reconcile-<namespace>.md` |
2751
2838
  | `transition` | the changed item and one `<ledger>/.wowbagger/reconcile-<namespace>.md` |
2752
2839
  | `parent-migrate` | the changed item and one reconciliation log |
2753
2840
  | `snooze` | the changed item and one reconciliation log |
@@ -2759,13 +2846,11 @@ item bytes at `HEAD`. A byte-identical `parent-migrate`, `snooze`, or `patch`
2759
2846
  still records its decision in the reconciliation log, so its auto-commit set
2760
2847
  contains the log alone.
2761
2848
 
2762
- `create` is journal-silent by design, so its commit set has no log. The
2763
- pre-mutation and post-mutation verification steps do not materialize an empty
2764
- log for `create`; this lets the first auto-commit run after namespace
2765
- provisioning commit the created item without an extra metadata ceremony. For
2766
- the other commands, the log must already carry this invocation's terminal
2767
- entry before it may be staged; if it does not, the invocation reports
2768
- `git-commit-failed` with `reason: "log-unavailable"`.
2849
+ `create` is journal-visible from the item's birth, so a successful `create`
2850
+ commits exactly the created item and its reconciliation log. For every command,
2851
+ the log must already carry this invocation's terminal entry before it may be
2852
+ staged; if it does not, the invocation reports `git-commit-failed` with
2853
+ `reason: "log-unavailable"`.
2769
2854
 
2770
2855
  Commit subjects are fixed:
2771
2856
 
@@ -2793,8 +2878,10 @@ Before the mutation runs, the invocation takes a per-working-tree mutex and
2793
2878
  requires all of:
2794
2879
 
2795
2880
  - No path staged anywhere in the repository.
2796
- - No dirty path under the ledger except the current namespace reconciliation
2797
- log for a command that owns and commits that log.
2881
+ - No dirty path under the ledger, with one exception: `transition`,
2882
+ `parent-migrate`, `snooze`, `patch`, and `publish-claimed` tolerate a dirty
2883
+ current-namespace reconciliation log, because each rebuilds that one derived
2884
+ file from the authoritative journal. `create` does not tolerate it.
2798
2885
  - A Git identity Git can resolve without committing.
2799
2886
  - A `HEAD` that exists. A detached `HEAD` is supported, because a commit works
2800
2887
  from one; an unborn `HEAD` refuses.
@@ -2812,18 +2899,24 @@ staging, or commit occurs. `details.reason` is one of `staged-paths-present`,
2812
2899
  Every `auto-commit-preflight-failed` refusal also carries
2813
2900
  `details.retryable`. `mutex-held` is retryable inside one working tree.
2814
2901
  `claim-state-unreconciled` also carries optional `claim_verify_code`,
2815
- `claim_verify_reason`, and bounded `findings`; `claim-store-locked` is
2816
- retryable, while persistent reconciliation is not retryable. Every other
2902
+ `claim_verify_reason`, `identity_diagnostic`, and bounded `findings`;
2903
+ `claim-store-locked` is retryable, while persistent reconciliation is not
2904
+ retryable. An `identity_diagnostic` reports an ambiguous or unreadable
2905
+ worktree identity — `duplicate-worktree-identity` with `worktree_id` and
2906
+ `live_worktree_count`, or `worktree-enumeration-failed` with no further member
2907
+ — and is never retryable. Every other
2817
2908
  preflight reason is not retryable. Clients branch on this boolean and the
2818
2909
  underlying verification reason, never on the generic message.
2819
2910
 
2820
2911
  The rule is deliberately strict rather than preserving foreign staged work in
2821
2912
  a temporary index. A journal-owning auto-commit validates and rebuilds the
2822
2913
  dirty derived reconciliation log from the authoritative journal, then commits
2823
- it with the mutation. `create` still refuses a dirty reconciliation log because
2824
- it does not own that path. Every other dirty ledger path refuses. Claim refusal
2825
- evidence is never suppressed: both the authoritative journal and its projected
2826
- log retain the decision before a later command rebuilds or commits the log.
2914
+ it with the mutation. Owning the log is not absorbing what was already in it:
2915
+ `create` still refuses a dirty reconciliation log, because residue that
2916
+ predates the invocation is foreign to the entries create is about to write.
2917
+ Every other dirty ledger path refuses. Claim refusal evidence is never
2918
+ suppressed: both the authoritative journal and its projected log retain the
2919
+ decision before a later command rebuilds or commits the log.
2827
2920
 
2828
2921
  A flagged invocation on an advisory or non-provisioned ledger returns exit 5
2829
2922
  `capability-unavailable` with `state: "unchanged"` before the mutation. It does