wowbagger 0.1.0-alpha.13 → 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 CHANGED
@@ -7,6 +7,206 @@ 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
+
10
210
  ## 0.1.0-alpha.13 - 2026-08-28
11
211
 
12
212
  ### 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.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.13
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.13 # exact plugin-matched release
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.13
109
- wowbagger --version # 0.1.0-alpha.13
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.13` installs this
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.13 # immutable Git release
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 `patch` in one worktree refuses every
796
- mutation in the others with exit 6 `claim-store-unavailable`, reason
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. Clones do not share the common directory, so
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-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"`.
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 except the current namespace reconciliation
2797
- log for a command that owns and commits that log.
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`; `claim-store-locked` is
2816
- retryable, while persistent reconciliation is not retryable. Every other
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. `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.
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