wowbagger 0.1.0-alpha.12 → 0.1.0-alpha.14
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 +253 -0
- package/README.md +8 -8
- package/docs/mutation-contract.md +104 -20
- package/docs/work-claim-contract.md +365 -40
- package/package.json +1 -1
- package/skills/wowbagger/SKILL.md +116 -21
- package/src/claim-coordinator.js +36 -4
- package/src/claim-journal.js +52 -4
- package/src/claim-publication.js +199 -99
- package/src/claim-store.js +9 -4
- package/src/cli.js +24 -1
- package/src/git-autocommit.js +32 -21
- package/src/git-reconciliation.js +72 -35
- package/src/git-worktrees.js +73 -0
- package/src/instrumentation.js +1 -0
- package/src/mutation.js +15 -2
- package/src/reconciliation-classifier.js +117 -0
- package/src/worktree-identity.js +165 -0
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,259 @@ consolidation. The first tagged release inherits this file.
|
|
|
7
7
|
|
|
8
8
|
## Unreleased
|
|
9
9
|
|
|
10
|
+
## 0.1.0-alpha.14 - 2026-08-29
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
|
|
14
|
+
- **Upgrading is a hard cutover: upgrade every writer in one Git coordination
|
|
15
|
+
domain before the first alpha.14 create.** The journal grammar widened —
|
|
16
|
+
`legacy-mutation-intent` and `legacy-mutation` accept `command: "create-v1"`,
|
|
17
|
+
a create intent accepts `expected_revision: null`, and a create abort requires
|
|
18
|
+
`command: "create-v1"` with `observed_revision: null` — and an alpha.13 binary
|
|
19
|
+
cannot read the new create entry. Run against a ledger whose shared journal
|
|
20
|
+
already carries one, alpha.13's `create` exits 6 with `error.code`
|
|
21
|
+
`claim-store-unavailable`, message `The durable claim store is unavailable.`,
|
|
22
|
+
and `error.details.reason` `claim-store-unreadable`, leaves state unchanged,
|
|
23
|
+
and writes no item. Failing closed is the compatibility guarantee: an old
|
|
24
|
+
writer cannot commit another duplicate. It is also the operational limit,
|
|
25
|
+
because alpha.13 is immutable and can only emit that generic unreadable-store
|
|
26
|
+
message. Read it as "this repository was written by a newer Wowbagger; upgrade
|
|
27
|
+
this worktree to continue." There is no automatic migration and no
|
|
28
|
+
mixed-version grace period; a partial upgrade leaves the remaining alpha.13
|
|
29
|
+
worktrees unable to make claim-protected mutations until they move. Item #185
|
|
30
|
+
owns general version-drift detection; nothing here can retrofit a message into
|
|
31
|
+
an already-published executable. The new reader executes every journal
|
|
32
|
+
alpha.13 emitted, which is the backward-compatibility direction that had to
|
|
33
|
+
hold.
|
|
34
|
+
|
|
35
|
+
- **A create must be committed before the next mutation, and there is still no
|
|
36
|
+
batch operation.** A created item has no earlier authorized revision, so Git
|
|
37
|
+
`HEAD` is the only surface that can carry its authorized bytes; an uncommitted
|
|
38
|
+
create raises the global `git-finalization-required` barrier for every later
|
|
39
|
+
mutation, including the next create. It cannot occupy the authorized
|
|
40
|
+
predecessor/successor window a `patch` or `transition` may occupy, and that
|
|
41
|
+
window was never permission to skip a commit. The supported bulk pattern is
|
|
42
|
+
the create-then-commit loop — filing ten items is ten cycles, and
|
|
43
|
+
`--auto-commit` on each create is its shortest form. Safe batch design is
|
|
44
|
+
item #186.
|
|
45
|
+
|
|
46
|
+
- **The measured cost of the fence is two extra fsync'd journal appends.** A
|
|
47
|
+
provisioned create already took the namespace lock, replayed the journal,
|
|
48
|
+
loaded the ledger, read Git `HEAD`, and reconciled, so the fence adds no new
|
|
49
|
+
Git roster or history traversal to a clean create; on a 1,500-item fixture the
|
|
50
|
+
captured `git` invocation sequence is identical before and after. The durable
|
|
51
|
+
cost is journal growth: each successful create adds one intent and one
|
|
52
|
+
committed terminal, so with no other activity the 65,536-entry limit permits
|
|
53
|
+
at most 21,845 three-entry create cycles, and the 8 MiB byte limit may bind
|
|
54
|
+
first. Other claim and mutation activity lowers that ceiling. Capacity is
|
|
55
|
+
reserved before the intent append, so an exhausted journal refuses unchanged
|
|
56
|
+
rather than stranding an attempt. Journal compaction is not part of this
|
|
57
|
+
change.
|
|
58
|
+
|
|
59
|
+
### Fixed
|
|
60
|
+
|
|
61
|
+
- **Two worktrees can no longer commit two items carrying the same number.**
|
|
62
|
+
Through alpha.13, `create` was journal-silent by design: it derived
|
|
63
|
+
`1 + max(existing numbers)` from the items its own checkout held and recorded
|
|
64
|
+
nothing in the shared claim journal. Two worktrees that had not integrated
|
|
65
|
+
each other's commits therefore derived the same number and both published,
|
|
66
|
+
and they did not have to race to do it — a create today and a create tomorrow
|
|
67
|
+
collided just as reliably, because the loser was a stale checkout rather than
|
|
68
|
+
a lost race. `number` is immutable and `patch` correctly refuses it, so the
|
|
69
|
+
collision surfaced only at integration, as a global `duplicate-number`
|
|
70
|
+
validation failure that stopped the whole ledger. On a provisioned ledger,
|
|
71
|
+
`create` is now a journaled legacy mutation: under the shared namespace lock
|
|
72
|
+
it reconciles, validates the complete candidate ledger, reserves journal
|
|
73
|
+
capacity, appends a `legacy-mutation-intent` with `command: "create-v1"` and
|
|
74
|
+
`expected_revision: null` before any byte reaches the item path, publishes
|
|
75
|
+
atomically and no-clobber, verifies the exact bytes, and appends its terminal
|
|
76
|
+
— `legacy-mutation` when the bytes landed, or an abort carrying
|
|
77
|
+
`command: "create-v1"` and `observed_revision: null` when the path stayed
|
|
78
|
+
absent. Allocation is fenced in front of all of that: every global finding
|
|
79
|
+
blocks `create` as it blocks any write, and in addition any coordinated item
|
|
80
|
+
this checkout does not hold blocks `create`, because its number cannot be
|
|
81
|
+
read here. A stale revision of an item this checkout does hold stays
|
|
82
|
+
nonblocking, because a number is immutable. A fenced create refuses with
|
|
83
|
+
exit 6, `state: "unchanged"`, `error.code: "claim-store-unavailable"`,
|
|
84
|
+
`error.details.reason: "publication-reconciliation-required"`, and no item
|
|
85
|
+
file; integrate the named item, run `claim-verify` to exit 0, and resend the
|
|
86
|
+
same request ID to take the next number. **What this fixes and what it does
|
|
87
|
+
not:** it closes the reported PropertyCompass2 collision, so cooperating
|
|
88
|
+
alpha.14 worktrees of one clone that share one Git common directory can no
|
|
89
|
+
longer commit the same number, because every such create is visible through
|
|
90
|
+
the shared journal before publication even without branch integration.
|
|
91
|
+
Separate clones, separate machines, alpha.13 writers before the hard cutover,
|
|
92
|
+
and noncooperating writes stay outside that fence and still rely on branch
|
|
93
|
+
integration plus `validate`. A ledger that already carries duplicate numbers
|
|
94
|
+
is not repaired here; item #182 owns that fenced recovery. Core
|
|
95
|
+
`contract_version` stays `5` and the create success and refusal envelopes add,
|
|
96
|
+
remove, and rename no member.
|
|
97
|
+
|
|
98
|
+
- **`create --auto-commit` commits its reconciliation log with the item.**
|
|
99
|
+
Because create now owns journal entries, its commit set is exactly the created
|
|
100
|
+
item and `<ledger>/.wowbagger/reconcile-<namespace>.md`, and
|
|
101
|
+
`mutation-finalize` accepts that same two-path recovery token. Between the
|
|
102
|
+
fence landing and this fix, `create --auto-commit` failed outright with
|
|
103
|
+
`git-commit-failed`, `failure_stage: "prepare-commit-set"`, and
|
|
104
|
+
`reason: "tree-changed"`, because the commit set excluded a log the mutation
|
|
105
|
+
had just written. Owning that log is not absorbing what was already in it:
|
|
106
|
+
`create` still refuses a reconciliation log that was dirty before the
|
|
107
|
+
invocation, and every other dirty ledger path still refuses.
|
|
108
|
+
|
|
109
|
+
- **A revision Git can already reach is no longer an instruction to wait for
|
|
110
|
+
it.** When the expected revision was reachable only from a tag, a
|
|
111
|
+
remote-tracking ref, a branch no worktree had checked out, or a live worktree
|
|
112
|
+
on a detached `HEAD`, the `worktree-synchronization-required` finding rendered
|
|
113
|
+
the not-yet-reachable sentence: "wait for the owning worktree to commit, then
|
|
114
|
+
synchronize this worktree and run claim-verify." The commit already existed
|
|
115
|
+
and no named worktree was ever going to publish it, so that wait could not
|
|
116
|
+
end. Those cases now render: "Revision <revision> of <path> is reachable in
|
|
117
|
+
Git, but no active named worktree owner is established; inspect the reachable
|
|
118
|
+
history, restore or explicitly adopt reviewed bytes, then run claim-verify."
|
|
119
|
+
A revision no reachable commit carries keeps the not-yet-reachable sentence
|
|
120
|
+
and its wait, and an item that has never existed in this checkout keeps the
|
|
121
|
+
cannot-be-established sentence. **A consumer that matches on `remediation`
|
|
122
|
+
text must update its patterns:** a finding that used to match "not yet
|
|
123
|
+
reachable" now matches "reachable in Git" for the four reachable-unowned
|
|
124
|
+
cases. Nothing else moves. The `reason` stays
|
|
125
|
+
`worktree-synchronization-required`, the code stays `stale-write-detected`,
|
|
126
|
+
`owner_unavailable: true` with no `owner_ref` or `owner_commit` is unchanged,
|
|
127
|
+
the finding stays advisory — it blocks a mutation targeting its own item and
|
|
128
|
+
lets an unrelated mutation proceed — and core `contract_version` stays `5`,
|
|
129
|
+
because no field, code, reason, or scope changes and the text correction
|
|
130
|
+
replaces a false instruction.
|
|
131
|
+
|
|
132
|
+
### Documentation
|
|
133
|
+
|
|
134
|
+
- **Worktree identity and its refusals are now documented.** Alpha.13 shipped an
|
|
135
|
+
explicit worktree identity — an opaque random UUID a worktree creates once in
|
|
136
|
+
its private Git directory — and started recording it as the optional
|
|
137
|
+
`writer_worktree_id` member on `legacy-mutation-intent`, `legacy-mutation`,
|
|
138
|
+
`publish-intent`, and `publish-final` journal entries. Reconciliation reads it
|
|
139
|
+
to tell this worktree's own unreachable successor from a sibling's, which is
|
|
140
|
+
what turns that topology into a global `unauthorized-revision` barrier instead
|
|
141
|
+
of advisory `worktree-synchronization-required`. Alpha.13 also began refusing
|
|
142
|
+
before it classifies anything when two live worktrees answer to one UUID, or
|
|
143
|
+
when the worktree roster cannot be read to completion: exit 6
|
|
144
|
+
`claim-store-unavailable`, reason `claim-store-unreadable`, plus one new
|
|
145
|
+
`error.details.identity_diagnostic` carrying `duplicate-worktree-identity`
|
|
146
|
+
with `worktree_id` and `live_worktree_count`, or `worktree-enumeration-failed`
|
|
147
|
+
with no further member, and the same diagnostic inside
|
|
148
|
+
`auto-commit-preflight-failed` with `retryable: false`. All of that shipped
|
|
149
|
+
without a release note; the alpha.13 entry mentioned writer identity only in
|
|
150
|
+
passing while describing which callers read it. The work-claim contract
|
|
151
|
+
section 3.1, the mutation contract auto-commit preflight details, and the
|
|
152
|
+
installed skill now state the journal field, its alpha.12-and-earlier
|
|
153
|
+
compatibility (such entries stay valid, attribute nothing, and leave the
|
|
154
|
+
writer unknown), what the identity discloses in committed history, and both
|
|
155
|
+
diagnostics. Core `contract_version` stays `5` and no public request or
|
|
156
|
+
success envelope changes.
|
|
157
|
+
|
|
158
|
+
- **Owner evidence names only an active named worktree, and the contract now
|
|
159
|
+
says so.** Through alpha.12 the owner search was a reachability search, so a
|
|
160
|
+
finding could report `owner_ref` naming a tag, a remote-tracking ref, or a
|
|
161
|
+
branch no worktree had checked out — an owner that could never publish
|
|
162
|
+
anything, and an instruction to wait forever. Alpha.13 replaced that with the
|
|
163
|
+
live worktree roster: this checkout first, then live worktrees on a branch
|
|
164
|
+
ordered by branch ref and then path, with bare and prunable records excluded.
|
|
165
|
+
`owner_ref` is therefore always the branch of a live worktree. A revision
|
|
166
|
+
reachable only from a tag, a remote-tracking ref, an unchecked-out branch, or
|
|
167
|
+
a live worktree on a detached `HEAD` now reports `owner_unavailable: true` and
|
|
168
|
+
no `owner_ref`. That narrowing shipped in alpha.13 with no release note, and
|
|
169
|
+
the alpha.11 note promising that a finding "retains that `owner_ref` and
|
|
170
|
+
`owner_commit`" no longer describes those cases. The contract and the
|
|
171
|
+
installed skill now state the roster rule, the three distinct meanings of
|
|
172
|
+
`owner_unavailable`, and which remediation sentence each one gets.
|
|
173
|
+
|
|
174
|
+
- **Repository-wide `claim-verify` and target-scoped mutation are stated as
|
|
175
|
+
different questions.** The mutation contract still said a recorded write in
|
|
176
|
+
one worktree "refuses every mutation in the others", which target scoping
|
|
177
|
+
stopped being true before alpha.11. It now states the item scope, and both
|
|
178
|
+
contracts and the installed skill now say plainly that a successful mutation
|
|
179
|
+
is not proof of a globally clean claim store: `claim-verify` names no target,
|
|
180
|
+
so it stays exit 6 while any item in the repository carries a blocking
|
|
181
|
+
finding, including an unrelated one. Item #184 is open in triage to decide the
|
|
182
|
+
supported verification surface; nothing about that behavior changed here, and
|
|
183
|
+
a gate that demands a globally clean `claim-verify` on a repository with live
|
|
184
|
+
sibling work is still unsatisfiable.
|
|
185
|
+
|
|
186
|
+
- **A refused reconciliation still persists the clock floor.** Reconciliation
|
|
187
|
+
has written its clock entry before it classifies anything since work claims
|
|
188
|
+
were implemented, so a command that then refuses with
|
|
189
|
+
`publication-reconciliation-required` has already advanced the durable
|
|
190
|
+
monotonic floor to `max(physical_utc, previous_floor)`. Section 4 documented
|
|
191
|
+
the floor only for authoritative lease decisions, so the pre-decision advance
|
|
192
|
+
and its one observable consequence were undocumented: because the floor never
|
|
193
|
+
rolls back, a lease or fence observed expired at that floor can never read
|
|
194
|
+
live again, and a holder on a ledger whose floor runs ahead of its own wall
|
|
195
|
+
clock may find its lease expired the moment it asks after clearing a barrier.
|
|
196
|
+
Behavior is unchanged; the contract now says it.
|
|
197
|
+
|
|
198
|
+
- **The reconciliation topology classifier moved without changing an answer.**
|
|
199
|
+
The topology decision that was spread across owner lookup, diagnosis, and
|
|
200
|
+
scope inference now lives in one pure module,
|
|
201
|
+
`src/reconciliation-classifier.js`, which takes normalized evidence and
|
|
202
|
+
returns a typed decision. That type is internal: scope travels beside a
|
|
203
|
+
finding and no response has ever carried it, so a consumer must keep reading
|
|
204
|
+
the refusal rather than the `reason` string. Every public seam — reason,
|
|
205
|
+
blocking behavior, owner fields, remediation sentence, exit, state, and
|
|
206
|
+
envelope domain — is byte-identical to alpha.13 on every matrix row, which is
|
|
207
|
+
the whole point of the change and the only claim made for it. No behavior
|
|
208
|
+
shipped with it.
|
|
209
|
+
|
|
210
|
+
## 0.1.0-alpha.13 - 2026-08-28
|
|
211
|
+
|
|
212
|
+
### Fixed
|
|
213
|
+
|
|
214
|
+
- **Every reconciling command reads the same writer evidence.** `claim-verify`
|
|
215
|
+
and ordinary mutations named the worktree they spoke for when they
|
|
216
|
+
classified reconciliation; `publish-claimed`, `claim-adopt`, and the
|
|
217
|
+
`claim acquire`, `claim renew`, and `claim release` lifecycle commands did
|
|
218
|
+
not. An unreachable successor written by the current worktree therefore read
|
|
219
|
+
as advisory sibling synchronization on those surfaces, whose target scoping
|
|
220
|
+
let `publish-claimed` commit a publication and `claim acquire` grant a claim
|
|
221
|
+
in exactly the state a `patch` refused. `publish-claimed`, `claim acquire`,
|
|
222
|
+
and `claim renew` now report `unauthorized-revision` and refuse, matching
|
|
223
|
+
`claim-verify`. `claim release` stays available: it relinquishes authority
|
|
224
|
+
rather than extending it, and refusing it would strand the lease in the
|
|
225
|
+
worktree least able to clear the barrier, because no other worktree can take
|
|
226
|
+
the item over while the claim is held. The release compare-and-swap is
|
|
227
|
+
unchanged, so a wrong owner, epoch, or expiry still refuses with
|
|
228
|
+
`claim-conflict`. A genuine sibling successor and an authorization written
|
|
229
|
+
before writer identity existed remain advisory synchronization on every
|
|
230
|
+
surface. `claim-adopt` semantics are unchanged: it reports no reconciliation
|
|
231
|
+
diagnosis and stays available as the remedy. It now judges its own identity
|
|
232
|
+
bytes before the worktree roster, so its own malformed identity reports as
|
|
233
|
+
such instead of as a failed sibling enumeration.
|
|
234
|
+
|
|
235
|
+
### Known limitations
|
|
236
|
+
|
|
237
|
+
- **Creates in more than one worktree can commit duplicate item numbers, even
|
|
238
|
+
when the creates are sequential, and no sanctioned repair exists** (items
|
|
239
|
+
#181 and #182). Create is journal-silent, so nothing coordinates the number
|
|
240
|
+
it derives. Any two worktrees whose checkouts have not been integrated derive
|
|
241
|
+
the next schema-v2 `number` from the base each can see, and both derive the
|
|
242
|
+
same one. The two creates need not overlap in time: a create in one worktree
|
|
243
|
+
today and a create in another worktree tomorrow collide just as surely, so
|
|
244
|
+
long as neither worktree has seen the other's commit. Each create succeeds,
|
|
245
|
+
each refuses nothing, and reconciliation reports nothing. The collision only
|
|
246
|
+
exists once the branches are integrated. `validate` then fails globally with
|
|
247
|
+
`duplicate-number` on every colliding item, and because an invalid ledger
|
|
248
|
+
blocks every mutation, the whole ledger stops accepting work. `number` is
|
|
249
|
+
immutable and `patch` correctly rejects it, so Wowbagger currently offers no
|
|
250
|
+
operation that repairs the collision.
|
|
251
|
+
|
|
252
|
+
Until both items ship, serialize `create` through a single worktree, and run
|
|
253
|
+
`validate` immediately after you integrate branches so a collision surfaces
|
|
254
|
+
at the merge rather than at the next mutation.
|
|
255
|
+
|
|
256
|
+
The PropertyCompass repair — editing `number` in the item source by hand,
|
|
257
|
+
committing it, then running `claim-adopt` — was an emergency intervention
|
|
258
|
+
taken under an outage. It is **not a supported workaround**. It bypasses the
|
|
259
|
+
number-collision and reference checks every mutation performs, and it can
|
|
260
|
+
leave dangling `depends_on`, `related`, and parent references that nothing
|
|
261
|
+
reports. Do not adopt it as routine practice.
|
|
262
|
+
|
|
10
263
|
## 0.1.0-alpha.12 - 2026-08-28
|
|
11
264
|
|
|
12
265
|
### 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.
|
|
34
|
-
> the `next` tag and on this repository's `v0.1.0-alpha.
|
|
33
|
+
> **Status: alpha, published, and self-hosted.** `0.1.0-alpha.14` is on npm under
|
|
34
|
+
> the `next` tag and on this repository's `v0.1.0-alpha.14` 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.
|
|
80
|
+
wowbagger --version # require 0.1.0-alpha.14
|
|
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.
|
|
106
|
+
npm install -g wowbagger@0.1.0-alpha.14 # exact plugin-matched release
|
|
107
107
|
# or, from this release's Git tag:
|
|
108
|
-
# npm install -g github:lstutzman/wowbagger#v0.1.0-alpha.
|
|
109
|
-
wowbagger --version # 0.1.0-alpha.
|
|
108
|
+
# npm install -g github:lstutzman/wowbagger#v0.1.0-alpha.14
|
|
109
|
+
wowbagger --version # 0.1.0-alpha.14
|
|
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.
|
|
328
|
+
`npm install -g github:lstutzman/wowbagger#v0.1.0-alpha.14` 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.
|
|
400
|
+
npm install -g github:lstutzman/wowbagger#v0.1.0-alpha.14 # immutable Git release
|
|
401
401
|
git pull && npm ci # or: a direct checkout
|
|
402
402
|
```
|
|
403
403
|
|
|
@@ -792,10 +792,19 @@ another. It is **not** a statement that worktrees write independently.
|
|
|
792
792
|
|
|
793
793
|
On a provisioned Git-backed ledger they do not. One claim journal lives in the
|
|
794
794
|
shared Git common directory, so it serializes every worktree of that
|
|
795
|
-
repository: a recorded `transition` or
|
|
796
|
-
mutation in the others
|
|
795
|
+
repository. The serialization is scoped to the item: a recorded `transition` or
|
|
796
|
+
`patch` in one worktree refuses a mutation in the others that targets *that
|
|
797
|
+
item* with exit 6 `claim-store-unavailable`, reason
|
|
797
798
|
`publication-reconciliation-required`, until the writing commit is visible in
|
|
798
|
-
the blocked checkout.
|
|
799
|
+
the blocked checkout. A mutation targeting an unrelated item still runs, and
|
|
800
|
+
the sibling's finding remains visible. Own uncommitted work and
|
|
801
|
+
out-of-protocol revisions are global barriers and refuse every mutation. A
|
|
802
|
+
repository-wide `claim-verify` names no target, so it reports exit 6 while any
|
|
803
|
+
item carries a blocking finding, including an unrelated one: a successful
|
|
804
|
+
mutation is therefore not proof of a globally clean claim store. See
|
|
805
|
+
[the work-claim contract](work-claim-contract.md), section 3.2, for the scopes
|
|
806
|
+
and for the open item #184 that owns the verification-surface decision. Clones
|
|
807
|
+
do not share the common directory, so
|
|
799
808
|
`limits.cross_clone_coordination: false` carries no such consequence.
|
|
800
809
|
|
|
801
810
|
That serialization is discoverable, per ledger, at
|
|
@@ -1558,6 +1567,62 @@ no-clobber publication still protects an intervening creator.
|
|
|
1558
1567
|
Successful create returns state committed and the inspect item shape from
|
|
1559
1568
|
section 5.
|
|
1560
1569
|
|
|
1570
|
+
### Journal-fenced allocation on a provisioned ledger
|
|
1571
|
+
|
|
1572
|
+
On a provisioned merge-coordinated ledger, a schema-version-2 create is a
|
|
1573
|
+
journaled legacy mutation. Under the shared namespace lock it reconciles,
|
|
1574
|
+
applies the allocation fence below, loads and validates the ledger, derives
|
|
1575
|
+
`1 + max(existing numbers)`, serializes the candidate, validates the complete
|
|
1576
|
+
candidate ledger, reserves journal capacity, and only then appends its
|
|
1577
|
+
`legacy-mutation-intent` with `command: "create-v1"` before any byte reaches
|
|
1578
|
+
the item path. Publication, exact-byte verification, and the terminal append
|
|
1579
|
+
all happen under that same lock. The intent carries
|
|
1580
|
+
`expected_revision: null`, which is valid only for `create-v1`. The committed
|
|
1581
|
+
terminal is `legacy-mutation` with `command: "create-v1"`; the create abort
|
|
1582
|
+
carries `command: "create-v1"` and `observed_revision: null`. No entry records
|
|
1583
|
+
the assigned number, because the candidate revision binds the complete item
|
|
1584
|
+
bytes and the ledger remains the one number authority.
|
|
1585
|
+
|
|
1586
|
+
The allocation fence reads reconciliation with one extra barrier. Every global
|
|
1587
|
+
finding blocks create as it blocks every write. On top of those, any
|
|
1588
|
+
coordinated item this checkout does not hold blocks `create`, because the
|
|
1589
|
+
journal records a committed terminal for an item whose number this working
|
|
1590
|
+
ledger cannot read. A stale revision of an item this checkout holds does not
|
|
1591
|
+
block `create`, because a number is immutable and the local maximum is already
|
|
1592
|
+
correct. A fenced create refuses with exit 6, `state: "unchanged"`,
|
|
1593
|
+
`error.code: "claim-store-unavailable"`, and
|
|
1594
|
+
`error.details.reason: "publication-reconciliation-required"` in the
|
|
1595
|
+
`ledger-mutation` domain as `create-v1`, having written no item byte. Resolve
|
|
1596
|
+
each finding by its own `remediation`, run `claim-verify` until it exits 0, and
|
|
1597
|
+
retry the same request ID; the retry takes the next number.
|
|
1598
|
+
|
|
1599
|
+
A create has no authorized predecessor, so Git `HEAD` is the only surface that
|
|
1600
|
+
can carry its authorized bytes, and an uncommitted create raises the global
|
|
1601
|
+
`git-finalization-required` barrier for every later mutation. Commit each
|
|
1602
|
+
created item before the next mutating command. This release adds no batch
|
|
1603
|
+
mutation; the supported bulk pattern is the create-then-commit loop, and item
|
|
1604
|
+
#186 owns safe batch design. A ledger that already carries duplicate numbers is
|
|
1605
|
+
item #182's recovery work: it fails validation and refuses every mutation
|
|
1606
|
+
before allocation, and nothing here renumbers it.
|
|
1607
|
+
|
|
1608
|
+
**What this guarantees, and what it does not.** The fence closes the reported
|
|
1609
|
+
PropertyCompass2 collision: cooperating alpha.14 worktrees of one clone that
|
|
1610
|
+
share one Git common directory can no longer commit two items carrying the same
|
|
1611
|
+
number, because every such create is visible through the shared journal before
|
|
1612
|
+
publication even without branch integration. Separate clones, separate
|
|
1613
|
+
machines, alpha.13 writers before the hard cutover, and noncooperating writes
|
|
1614
|
+
stay outside the fence and still rely on branch integration plus `validate`.
|
|
1615
|
+
|
|
1616
|
+
The widened journal grammar forces a hard cutover, so upgrade every writer in
|
|
1617
|
+
one Git coordination domain before the first alpha.14 create. An alpha.13
|
|
1618
|
+
binary cannot read a `create-v1` intent: it refuses with exit 6,
|
|
1619
|
+
`error.code` `claim-store-unavailable`, message
|
|
1620
|
+
`The durable claim store is unavailable.`, and `error.details.reason`
|
|
1621
|
+
`claim-store-unreadable`, leaving state unchanged and writing no item. That
|
|
1622
|
+
refusal means **this repository was written by a newer Wowbagger; upgrade this
|
|
1623
|
+
worktree to continue.** There is no automatic migration and no mixed-version
|
|
1624
|
+
grace period.
|
|
1625
|
+
|
|
1561
1626
|
## 8. Transition
|
|
1562
1627
|
|
|
1563
1628
|
### Request
|
|
@@ -2626,6 +2691,13 @@ That window produces no finding, so another mutation can run before the first
|
|
|
2626
2691
|
one is committed. The later command's acceptance does not prove durability;
|
|
2627
2692
|
the operating rule remains write, commit, `claim-verify`, next write.
|
|
2628
2693
|
|
|
2694
|
+
`create` never occupies that window. A created item has no earlier authorized
|
|
2695
|
+
revision, so Git `HEAD` is the only surface that can carry its authorized
|
|
2696
|
+
bytes, and an uncommitted create raises the global `git-finalization-required`
|
|
2697
|
+
barrier for every later mutation, including the next create. Filing ten items
|
|
2698
|
+
is therefore ten create-then-commit cycles, not one commit at the end. This
|
|
2699
|
+
release adds no batch mutation; item #186 owns safe batch design.
|
|
2700
|
+
|
|
2629
2701
|
The loop that works:
|
|
2630
2702
|
|
|
2631
2703
|
~~~sh
|
|
@@ -2697,7 +2769,13 @@ envelope, subject and commit set included.
|
|
|
2697
2769
|
For every blocking finding:
|
|
2698
2770
|
|
|
2699
2771
|
1. Do what `remediation` says, for each finding, using its `expected_path`.
|
|
2700
|
-
`git-finalization-required` means commit that path.
|
|
2772
|
+
`git-finalization-required` means commit that path. A
|
|
2773
|
+
`worktree-synchronization-required` finding means wait only when its
|
|
2774
|
+
`remediation` names an owner to wait for or says the expected revision is not
|
|
2775
|
+
yet reachable. When it says the revision is reachable in Git while no active
|
|
2776
|
+
named worktree owner is established, there is no commit left to wait for:
|
|
2777
|
+
inspect that reachable history, then restore or explicitly adopt reviewed
|
|
2778
|
+
bytes.
|
|
2701
2779
|
2. Run `wowbagger claim-verify --ledger <dir> --json`.
|
|
2702
2780
|
3. Exit 0 with `state: "committed"` means the ledger is reconciled and the next
|
|
2703
2781
|
mutating command may run. Exit 6 means findings remain; repeat from step 1.
|
|
@@ -2747,7 +2825,7 @@ by sending the flag, not by reading a version.
|
|
|
2747
2825
|
|
|
2748
2826
|
Only the paths this invocation owns:
|
|
2749
2827
|
|
|
2750
|
-
| `create` | the created item |
|
|
2828
|
+
| `create` | the created item and one `<ledger>/.wowbagger/reconcile-<namespace>.md` |
|
|
2751
2829
|
| `transition` | the changed item and one `<ledger>/.wowbagger/reconcile-<namespace>.md` |
|
|
2752
2830
|
| `parent-migrate` | the changed item and one reconciliation log |
|
|
2753
2831
|
| `snooze` | the changed item and one reconciliation log |
|
|
@@ -2759,13 +2837,11 @@ item bytes at `HEAD`. A byte-identical `parent-migrate`, `snooze`, or `patch`
|
|
|
2759
2837
|
still records its decision in the reconciliation log, so its auto-commit set
|
|
2760
2838
|
contains the log alone.
|
|
2761
2839
|
|
|
2762
|
-
`create` is journal-
|
|
2763
|
-
|
|
2764
|
-
log
|
|
2765
|
-
|
|
2766
|
-
|
|
2767
|
-
entry before it may be staged; if it does not, the invocation reports
|
|
2768
|
-
`git-commit-failed` with `reason: "log-unavailable"`.
|
|
2840
|
+
`create` is journal-visible from the item's birth, so a successful `create`
|
|
2841
|
+
commits exactly the created item and its reconciliation log. For every command,
|
|
2842
|
+
the log must already carry this invocation's terminal entry before it may be
|
|
2843
|
+
staged; if it does not, the invocation reports `git-commit-failed` with
|
|
2844
|
+
`reason: "log-unavailable"`.
|
|
2769
2845
|
|
|
2770
2846
|
Commit subjects are fixed:
|
|
2771
2847
|
|
|
@@ -2793,8 +2869,10 @@ Before the mutation runs, the invocation takes a per-working-tree mutex and
|
|
|
2793
2869
|
requires all of:
|
|
2794
2870
|
|
|
2795
2871
|
- No path staged anywhere in the repository.
|
|
2796
|
-
- No dirty path under the ledger
|
|
2797
|
-
|
|
2872
|
+
- No dirty path under the ledger, with one exception: `transition`,
|
|
2873
|
+
`parent-migrate`, `snooze`, `patch`, and `publish-claimed` tolerate a dirty
|
|
2874
|
+
current-namespace reconciliation log, because each rebuilds that one derived
|
|
2875
|
+
file from the authoritative journal. `create` does not tolerate it.
|
|
2798
2876
|
- A Git identity Git can resolve without committing.
|
|
2799
2877
|
- A `HEAD` that exists. A detached `HEAD` is supported, because a commit works
|
|
2800
2878
|
from one; an unborn `HEAD` refuses.
|
|
@@ -2812,18 +2890,24 @@ staging, or commit occurs. `details.reason` is one of `staged-paths-present`,
|
|
|
2812
2890
|
Every `auto-commit-preflight-failed` refusal also carries
|
|
2813
2891
|
`details.retryable`. `mutex-held` is retryable inside one working tree.
|
|
2814
2892
|
`claim-state-unreconciled` also carries optional `claim_verify_code`,
|
|
2815
|
-
`claim_verify_reason`, and bounded `findings`;
|
|
2816
|
-
retryable, while persistent reconciliation is not
|
|
2893
|
+
`claim_verify_reason`, `identity_diagnostic`, and bounded `findings`;
|
|
2894
|
+
`claim-store-locked` is retryable, while persistent reconciliation is not
|
|
2895
|
+
retryable. An `identity_diagnostic` reports an ambiguous or unreadable
|
|
2896
|
+
worktree identity — `duplicate-worktree-identity` with `worktree_id` and
|
|
2897
|
+
`live_worktree_count`, or `worktree-enumeration-failed` with no further member
|
|
2898
|
+
— and is never retryable. Every other
|
|
2817
2899
|
preflight reason is not retryable. Clients branch on this boolean and the
|
|
2818
2900
|
underlying verification reason, never on the generic message.
|
|
2819
2901
|
|
|
2820
2902
|
The rule is deliberately strict rather than preserving foreign staged work in
|
|
2821
2903
|
a temporary index. A journal-owning auto-commit validates and rebuilds the
|
|
2822
2904
|
dirty derived reconciliation log from the authoritative journal, then commits
|
|
2823
|
-
it with the mutation.
|
|
2824
|
-
|
|
2825
|
-
|
|
2826
|
-
|
|
2905
|
+
it with the mutation. Owning the log is not absorbing what was already in it:
|
|
2906
|
+
`create` still refuses a dirty reconciliation log, because residue that
|
|
2907
|
+
predates the invocation is foreign to the entries create is about to write.
|
|
2908
|
+
Every other dirty ledger path refuses. Claim refusal evidence is never
|
|
2909
|
+
suppressed: both the authoritative journal and its projected log retain the
|
|
2910
|
+
decision before a later command rebuilds or commits the log.
|
|
2827
2911
|
|
|
2828
2912
|
A flagged invocation on an advisory or non-provisioned ledger returns exit 5
|
|
2829
2913
|
`capability-unavailable` with `state: "unchanged"` before the mutation. It does
|