wowbagger 0.1.0-alpha.1 → 0.1.0-alpha.12

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.
Files changed (77) hide show
  1. package/CHANGELOG.md +1135 -0
  2. package/LICENSE +21 -201
  3. package/README.md +1032 -190
  4. package/adapters/claude-code/wowbagger-adapter.json +4 -4
  5. package/adapters/codex/wowbagger-adapter.json +1 -1
  6. package/adapters/opencode/wowbagger-adapter.json +1 -1
  7. package/assets/wowbagger-v1-more-whimsical.jpg +0 -0
  8. package/assets/wowbagger-v2-less-whimsical.jpg +0 -0
  9. package/assets/wowbagger-v3-herding-agent-cats.jpg +0 -0
  10. package/assets/wowbagger-v4-robot-agent-herding.jpg +0 -0
  11. package/assets/wowbagger-v5-typing-cats-circuit-staff.jpg +0 -0
  12. package/docs/adapter-contract.md +1572 -0
  13. package/docs/host-contract.md +287 -0
  14. package/docs/mutation-contract.md +2949 -0
  15. package/docs/work-claim-contract.md +1179 -0
  16. package/package.json +29 -3
  17. package/schemas/bare-ready-result.json +27 -0
  18. package/schemas/bare-validation-result.json +23 -0
  19. package/schemas/common.json +470 -0
  20. package/schemas/core-capabilities-response.json +346 -0
  21. package/schemas/core-envelope.json +141 -0
  22. package/schemas/core-inspect-response.json +37 -0
  23. package/schemas/core-inspect-workbench-response.json +340 -0
  24. package/schemas/core-list-error-response.json +242 -0
  25. package/schemas/core-list-query.json +98 -0
  26. package/schemas/core-list-response.json +140 -0
  27. package/schemas/core-report-response.json +379 -0
  28. package/schemas/core-transition-error-response.json +852 -0
  29. package/schemas/core-transition-request.json +47 -0
  30. package/schemas/core-transition-response.json +49 -0
  31. package/schemas/index.json +108 -0
  32. package/schemas/ledger-mutation-refusal.json +288 -0
  33. package/schemas/report-config-v1.json +140 -0
  34. package/schemas/report-config-v2.json +242 -0
  35. package/scripts/migrate-schema-2.js +7 -0
  36. package/skills/wowbagger/SKILL.md +617 -23
  37. package/src/adapter/core-probe.js +98 -10
  38. package/src/adapter/entrypoint-main.js +97 -17
  39. package/src/adapter/invoke.js +43 -24
  40. package/src/adapter/process-outcome.js +306 -25
  41. package/src/claim-capabilities.js +10 -3
  42. package/src/claim-coordinator.js +161 -10
  43. package/src/claim-journal.js +153 -11
  44. package/src/claim-prospective.js +124 -0
  45. package/src/claim-publication.js +800 -151
  46. package/src/claim-request.js +20 -0
  47. package/src/claim-store.js +40 -9
  48. package/src/claim-sync.js +66 -0
  49. package/src/cli.js +993 -39
  50. package/src/extension-provision.js +33 -0
  51. package/src/extensions.js +141 -0
  52. package/src/git-autocommit.js +1067 -0
  53. package/src/git-reconciliation.js +211 -15
  54. package/src/instrumentation.js +75 -0
  55. package/src/launch.js +52 -0
  56. package/src/ledger.js +163 -2
  57. package/src/lifecycle.js +153 -0
  58. package/src/limits.js +50 -0
  59. package/src/list.js +365 -0
  60. package/src/mutation.js +815 -192
  61. package/src/projection.js +39 -0
  62. package/src/ready.js +64 -38
  63. package/src/report-attention.js +102 -0
  64. package/src/report-evidence.js +303 -0
  65. package/src/report-graph.js +465 -0
  66. package/src/report-html.js +485 -0
  67. package/src/report-markdown.js +171 -0
  68. package/src/report-sequencing.js +285 -0
  69. package/src/report-svg.js +342 -0
  70. package/src/report-view.js +189 -0
  71. package/src/report.js +575 -0
  72. package/src/schema-migration.js +28 -4
  73. package/src/validate.js +19 -6
  74. package/src/workbench.js +117 -0
  75. package/vendor/3d-force-graph/3d-force-graph.min.js +5 -0
  76. package/vendor/3d-force-graph/LICENSE +21 -0
  77. package/vendor/3d-force-graph/VERSIONS.json +24 -0
package/CHANGELOG.md CHANGED
@@ -7,6 +7,1141 @@ consolidation. The first tagged release inherits this file.
7
7
 
8
8
  ## Unreleased
9
9
 
10
+ ## 0.1.0-alpha.12 - 2026-08-28
11
+
12
+ ### Fixed
13
+
14
+ - **Out-of-protocol local states remain global reconciliation barriers.**
15
+ Alpha.11 could misclassify an unknown committed revision, an authorized
16
+ working-tree predecessor over an unknown `HEAD`, or a working-tree deletion
17
+ over an authorized `HEAD` as advisory sibling synchronization when another
18
+ worktree owned the expected revision. `claim-verify` still failed, but an
19
+ unrelated mutation could proceed through the documented global barrier.
20
+ Alpha.12 classifies local state before owner and target scope, while genuine
21
+ authorized sibling predecessors remain target-scoped synchronization.
22
+ - **Windows report path resolution preserves the read-failure error class.**
23
+ Windows can report `ENOENT` where POSIX reports `ENOTDIR` when an output path
24
+ descends through a regular file. Report generation now recognizes that
25
+ non-directory ancestor and returns `report-read-failed` with
26
+ `resolve-output-path` and `ENOTDIR` before publication, instead of surfacing
27
+ `report-write-failed` with `EEXIST`.
28
+
29
+ ## 0.1.0-alpha.11 - 2026-08-27
30
+
31
+ ### Fixed
32
+
33
+ - **Uncommitted sibling revisions keep target-scoped reconciliation safe.**
34
+ Previously authorized predecessor bytes now identify an in-protocol sibling
35
+ window without turning genuine hand edits or same-branch regressions into
36
+ nonblocking findings.
37
+ Restoring earlier authorized bytes in the same working tree remains
38
+ `unauthorized-revision` when the current branch owns the expected revision.
39
+ Detached `HEAD` uses its reachable history for the same current-owner guard,
40
+ so it cannot turn that regression into advisory sibling synchronization.
41
+ When Git proves that a sibling ref owns the expected revision, the finding
42
+ retains that `owner_ref` and `owner_commit` instead of reporting the owner as
43
+ unavailable.
44
+ - **Journal-owning auto-commit rebuilds its derived reconciliation log.** Claim
45
+ decisions may dirty that tracked projection; patch, transition,
46
+ parent-migrate, snooze, and publish-claimed now validate and commit the rebuilt
47
+ log while every foreign dirty ledger path and create still refuse.
48
+ - **Claim-verification failures preserve their cause.** Preflight and
49
+ post-commit errors carry the underlying claim verification code and reason;
50
+ claim-store lock contention is retryable while persistent reconciliation is
51
+ not.
52
+ `mutation-finalize` recovery now carries the identical diagnostics without
53
+ inventing a `findings` member.
54
+ - **Auto-commit uses target scope before and after committing.** Unrelated
55
+ synchronization findings no longer turn a successfully committed mutation
56
+ into a reported post-commit failure; target-blocking findings remain fatal and
57
+ visible through claim-verify.
58
+
59
+ ### Changed
60
+
61
+ - **Parent migration and snooze now have complete contract guidance.** The
62
+ contract and installed skill document their requests, CAS and date rules,
63
+ response domains, auto-commit behavior, and legacy journal fence-family
64
+ semantics. Parent-migrate help no longer invents a live-item restriction.
65
+ - **Parent and snooze fields are documented as dedicated mutations, not
66
+ create-once values.** Existing items can be repointed to or from an epic with
67
+ `parent-migrate`, and `snooze` can set or clear `snoozed_until`; `kind` and
68
+ `provenance` remain genuinely create-once.
69
+ - **Release numbering preserves the unpublished alpha.10 cut.** Alpha.10 was
70
+ cut and tagged but never published to npm. This release advances to alpha.11
71
+ instead of moving that tag, so tag identity remains immutable and the
72
+ registry history honestly skips alpha.10.
73
+
74
+ ### Known limitations
75
+
76
+ - **Parent-migrate and snooze lock owners lose diagnostic detail.** Their real
77
+ lock still refuses every concurrent mutation, but the refusal currently
78
+ reports `owner: null` with `owner_diagnostic: "invalid-shape"`. Item #174
79
+ tracks restoring those owner details; mutual exclusion is unaffected.
80
+ - **Ownership classification still needs one consolidated topology audit.**
81
+ Items #173, #176, and #177 close every case identified so far. Item #178
82
+ remains open to audit the topology as one matrix because each point fix
83
+ revealed an adjacent gap.
84
+
85
+ ## 0.1.0-alpha.10 - 2026-08-26
86
+
87
+ ### Fixed
88
+
89
+ - **Cross-worktree reconciliation is target-scoped.** An unrelated item
90
+ mutation no longer refuses because another worktree has a committed revision.
91
+ When synchronization is required, the finding names the owning reference.
92
+ - **Fresh clones recover committed claim history.** Reconciliation hydrates an
93
+ empty local claim journal from the committed reconciliation log, while
94
+ lock-free claim reads project that history without writing. Invalid or
95
+ over-capacity committed sequences fail closed.
96
+ - **Auto-commit recovery handles refusal and no-op paths.** Refused preflight
97
+ checks no longer rewrite the tracked reconciliation log. The first
98
+ post-provision create works, byte-identical mutations commit only their log,
99
+ and `mutation-finalize` accepts the corresponding log-only recovery after
100
+ `HEAD` advances.
101
+ - **Parent migration and snooze expose their guarded write contracts.**
102
+ Both commands support auto-commit, report their own response domains, return
103
+ a single unchanged invalid-request envelope, and report stale parent
104
+ witnesses as conflicts.
105
+ - **Create reserves the `extensions` request container.** Item data must name
106
+ extension members directly so patch and extension declarations can address
107
+ them.
108
+ - **Auto-commit preflight reports retryability explicitly.** Only a held
109
+ auto-commit mutex is retryable.
110
+ - **CLI and release metadata stay discoverable and complete.** Help describes
111
+ claim verification and list requests, and the release-site manifest covers
112
+ historical version records.
113
+
114
+ ## 0.1.0-alpha.9 - 2026-08-23
115
+
116
+ ### Added
117
+
118
+ - **A public Wowbagger brand asset.** The repository now carries an optimized
119
+ 1024px image of Bowerick Wowbagger directing robotic agent cats at consoles
120
+ with a circuit-lit shepherd's staff. The asset is included in the npm
121
+ package for README and GitHub presentation.
122
+
123
+ ### Changed
124
+
125
+ - **Public product prose now describes the current engine.** README, npm
126
+ metadata, Claude plugin metadata, marketplace metadata, and the installed
127
+ skill now explain report sequencing dashboards, named views, facets, graph
128
+ filtering, guarded CAS mutations, claims, fencing, reconciliation, and the
129
+ separation between core and host responsibilities.
130
+ - **Agent onboarding is explicit.** The README and skill include a concise
131
+ agent TL;DR, exact core setup checks, the separate plugin/skills installer
132
+ routes, and the response-loss rule.
133
+
134
+ ## 0.1.0-alpha.8 - 2026-08-23
135
+
136
+ ### Added
137
+
138
+ - **A published launch seam for a host that cannot run a shell.** The package
139
+ now declares `exports`, and its main entry `wowbagger` exposes
140
+ `CORE_SCRIPT_PATH`, `MINIMUM_NODE_MAJOR`, and `resolveCoreLaunch(argv)`, which
141
+ returns the exact process tuple a direct-core host needs: an absolute Node
142
+ executable, an argument array whose first element is the absolute
143
+ `bin/wowbagger.js`, and `shell: false`. A host that resolves its own runtime
144
+ gets `resolveCoreLaunch(argv, { nodeExecutable })`, and a relative or bare
145
+ executable name is refused rather than left for PATH to answer. The script
146
+ path is also resolvable on its own as `wowbagger/wowbagger.js` for a host that
147
+ wants the path without importing anything. Deep imports that already worked —
148
+ `wowbagger/src/limits.js` and every other published path — keep resolving
149
+ through an explicit `"./*"` subpath. No command accepts, refuses, emits, or
150
+ writes anything different.
151
+
152
+ - **The machine contract ships as JSON Schema, not only as prose and fixtures.**
153
+ Seventeen JSON Schema 2020-12 documents under `schemas/` are published with
154
+ the package and resolvable as `wowbagger/schemas/<file>.json`, with
155
+ `schemas/index.json` naming each one's response domain and that domain's
156
+ version. They cover the core envelope and its four exact root shapes, the
157
+ capabilities envelope with every advertised limit as a constant, the two bare
158
+ results, the list query and its page and refusals, the default and workbench
159
+ inspect projections, the transition request, success, and every documented
160
+ refusal with its mutation state, the `ledger-mutation` fence refusals, report
161
+ configuration versions 1 and 2, and the report responses including a named
162
+ view. Every schema fixes its root members exactly and pins the version of its
163
+ own domain, so a core envelope refuses a namespaced refusal, a version 4
164
+ envelope, or an extra root member, and a version 1 report configuration
165
+ refuses a version 2 one. Exactness reaches the members too: an item core must
166
+ carry both relation lists, because the core view always emits them, and a
167
+ refusal raised before a commit is established pins its state to `unchanged`
168
+ rather than admitting the indeterminate state that only an interrupted write
169
+ can report. The validator is a test-only dependency; the runtime still has
170
+ exactly one dependency.
171
+
172
+ ### Documentation
173
+
174
+ - **`docs/host-contract.md` publishes the direct-core host boundary.** One
175
+ document now states what a UI plugin or other non-agent consumer needs and
176
+ what it must supply itself: the package resolution seam and the four-part
177
+ launch — Node.js 20 or later, an absolute Node executable, the absolute
178
+ `wowbagger.js`, an argument array, and `shell: false`; bounded stdin or a
179
+ host-created request file, never shell source and never inline unbounded argv
180
+ JSON; captured stdout and stderr; the owning-host path rule for a worktree, a
181
+ plain folder, direct SSH, and WSL, with no cross-runtime path guessing;
182
+ namespace-first response dispatch and the exit table; every advertised limit
183
+ with its exact value; the once-only dispatch sequence for a lost response; and
184
+ the seventeen packaged JSON Schemas by domain. It states that a missing
185
+ executable is a host-level result rather than malformed Wowbagger JSON, that
186
+ the host owns executable discovery, working directory, timeout, cancellation,
187
+ process-tree containment, stream caps, and routing, and that Wowbagger will
188
+ not add automatic transitions, mirrored ledger state, operation identity,
189
+ remote routing, or a daemon. The full `inspect` read is documented as
190
+ deliberately unbounded, with the reason. The README, the installed skill, and
191
+ `SPEC.md` section 10 point at it, and it ships in the npm package.
192
+ - **The public version and lifecycle vocabulary agrees with the runtime.**
193
+ `SPEC.md` section 10 said core mutation contracts 1 and 2 were defined and
194
+ that the runtime emitted version 2; it now says 1 through 5 and version 5. The
195
+ mutation contract's status line said versions 1, 2, and 3 with the runtime on
196
+ 3; it now says 1 through 5 and version 5. `deferred` has been a real status
197
+ since it shipped, but `SPEC.md` omitted it from the status field, the
198
+ lifecycle table, the transition table, the terminal-date invariants, the
199
+ decision-action list, and the terminal-decision table, and the mutation
200
+ contract omitted the `deferred` date from the lossless core view; all seven
201
+ now name it, along with the `resolve`, `defer`, and `undefer` decision actions
202
+ the validator has always accepted. No behaviour changed: these were prose
203
+ omissions, and the tests that guard them read the vocabularies from
204
+ `src/lifecycle.js` and `src/validate.js` rather than retyping them.
205
+ - **Response loss is a named contract instead of folklore.** The mutation
206
+ contract, the adapter contract, the README, and the installed skill now carry
207
+ the same sequence for a mutation whose response never arrived: dispatch once,
208
+ never replay, invalidate the inspected revision, reconnect, then re-read the
209
+ ledger. The mutation contract's new section 10 table separates the outcomes a
210
+ caller may act on — committed success, proven non-write, committed recovery,
211
+ unknown publication — from the two that establish nothing, a signalled or
212
+ timed-out transport and a missing envelope, and states that a later item state
213
+ never proves that the lost dispatch caused it. Adapter contract section 6.2
214
+ documents the `mutation_outcome: "unknown"` envelope and its per-command
215
+ `recovery` object exactly as the adapter emits them, and states that exit 4
216
+ `revision-conflict` is a proven non-write that is never relabelled response
217
+ loss. There is no operation ID, durable outcome store, or replay endpoint;
218
+ adding correlation requires a new contract decision. No command accepts,
219
+ refuses, emits, or writes anything different. Two conformance vectors now pin
220
+ the core's own exit-6 `write-outcome-unknown` and `post-commit-recovery-required`
221
+ envelopes at the adapter's process-outcome seam, taking the adapter vector set
222
+ to 212 assertions.
223
+ - **The exit tables state where `report` actually lands.** Both contracts filed
224
+ exit 1 as a bare-result-only condition and exit 3 as every invalid ledger,
225
+ while `report` answers an invalid ledger, an unreadable input, and a failed
226
+ publication at exit 1. The mutation contract now carries an exit 1 row naming
227
+ those codes, lists `report-config-invalid` and `report-view-not-found` in its
228
+ exit 2 row, and scopes exit 3 to every command except `report`; the host
229
+ contract's exit 1 and exit 3 conditions say the same. Two guards assert the
230
+ rows against the codes the runtime emits.
231
+
232
+ ### Changed
233
+
234
+ - **The report filters by facet groups instead of one value at a time.** The
235
+ drill-down's single-value mapped-field selects and its All/Ready/Blocked/
236
+ Ineligible buttons are gone. In their place is one group of checkbox chips per
237
+ dimension of the open set — Readiness, Status, Kind, and every configured
238
+ mapped field, so a mapped `class: bug` is a chip rather than a value hidden in
239
+ a dropdown. Values inside a group are alternatives, groups narrow each other,
240
+ and the search box is one more condition; every chip states the count it would
241
+ leave, measured against the search and the other groups but never against its
242
+ own, so two selections in one group cannot make their siblings read zero. A
243
+ visible result count, per-chip selected state, and `Clear filters` are new, and
244
+ opening a Work next or Attention row still clears whatever detached its card,
245
+ facets included.
246
+ - **The ledger graph filters by lifecycle status.** One chip group above the
247
+ stage carries the statuses the ledger holds, all selected, with `Select all`
248
+ and `Clear`. Deselecting a status drops its nodes, every link incident to one,
249
+ and their labels, reheats the layout in place, and takes a hidden node off the
250
+ hover card; the roster and node count follow the same selection, and an empty
251
+ selection draws an empty graph that says so. The legend, the WebGL-less
252
+ roster, camera interaction, and the no-fetch contract are unchanged, and
253
+ nothing here reads or writes the ledger.
254
+
255
+ ### Fixed
256
+
257
+ - **A report failure before publication says so, and never arrives causeless.**
258
+ An output path the filesystem cannot resolve — a `--out` under a regular
259
+ file, an unreadable directory on the way to it — was a raw runtime error
260
+ escaping into the command's catch-all, answered as `report-write-failed` with
261
+ empty `details` even though nothing had been rendered or replaced. It is now
262
+ `report-read-failed` with `details.operation` naming which resolution failed,
263
+ `details.path` naming the path the caller configured or passed, and
264
+ `details.cause` naming the filesystem's own code. An error no report path
265
+ throws on purpose still answers `report-write-failed`, because nothing
266
+ reached the output path, but it now carries `{operation, cause}` instead of
267
+ `{}`. Every `details.cause` is a bounded token — an error code, or the
268
+ error's kind when the runtime gave no code — so a publication failure no
269
+ longer republishes a runtime message, and the paths, credentials, and
270
+ run-specific values a message carries stay out of the envelope. Atomic
271
+ publication is unchanged: a report already at the selected path survives
272
+ every one of these refusals.
273
+ - **An empty named view stops blaming the reader.** A view whose criteria
274
+ matched nothing rendered the drill-down's `No items match these filters.` and
275
+ the graph's `No status is selected`, sending a reader to controls that could
276
+ not bring an item back. A named artifact with no items now states
277
+ `No ledger item matches this view's criteria.` and, in the graph, that it has
278
+ nothing to draw — visibly, without waiting for scripting. A report that holds
279
+ items keeps the filter and status copy, which is the honest answer when the
280
+ reader is the one who narrowed it, and the base report's bytes are unchanged.
281
+ - **`core-report-response.json` named a member the report never emitted.** The
282
+ published `ledger-invalid` refusal required `details.validation_errors`,
283
+ which is the mutation commands' member name; `report` has always emitted
284
+ `details.errors`. A consumer validating a real refusal against the shipped
285
+ schema failed on the runtime's own bytes. The schema now states `errors`, and
286
+ a live invalid-ledger report run is validated against it.
287
+
288
+ ## 0.1.0-alpha.7 - 2026-08-18
289
+
290
+ ### Changed
291
+
292
+ - **The item source is bounded at every candidate door, and the core contract
293
+ moves to 4.** The published version 3 core accepted a 50-MiB `create` with
294
+ exit `0` and state `committed` in 0.70 s: `create`, `transition`, and `patch`
295
+ had no bound
296
+ anywhere, and `publish-claimed` alone bounded candidates but reported an
297
+ oversized, perfectly canonical candidate as `The candidate source is not
298
+ canonical base64.` One shared `MAX_ITEM_SOURCE_BYTES` of 8,388,608 now bounds
299
+ the complete serialized successor at all four doors, and every one of them
300
+ answers the same named refusal: `item-source-too-large`, exit 2, state
301
+ `unchanged`, details exactly `{id, size_bytes, limit_bytes}` in the core
302
+ domain and `{item_id, size_bytes, limit_bytes}` in the ledger-publication
303
+ domain. The measurement is serialized UTF-8 bytes, so frontmatter, decisions,
304
+ extensions, and body all draw on the same budget; `transition` is bounded
305
+ because its decision block can push a legal stored item past it. Core
306
+ capabilities advertises the value at `result.limits.max_item_source_bytes`.
307
+ This narrows accepted input against a published version, so the core contract
308
+ moves to 4 and version 3 consumers fail closed at negotiation. A ledger
309
+ committed before the bound does not brick: an oversized item still validates,
310
+ still inspects, and a patch that shrinks it under the bound is accepted.
311
+
312
+ - **The work-claim API moves to 2.** The oversized-candidate response replaces
313
+ the error `publish-claimed` version 1 pinned for that input, so
314
+ `result.operations.work_claim.api_version` is now `2` and version 1 consumers
315
+ fail closed. Malformed base64 keeps its version 1 `invalid-request`: without
316
+ canonical base64 there is no item source to measure. The base64-character
317
+ precheck is gone — the 11,534,336-byte serialized-request bound already caps
318
+ what a candidate can decode to, and the precheck was the remaining path that
319
+ answered a genuine size refusal with a false base64 message. That transport
320
+ bound is unchanged and still measures a different object.
321
+ - **A claimed publication no longer takes one cooperative lock per ledger
322
+ item.** `publish-claimed` computed its lock closure from every item in the
323
+ loaded ledger, so publishing one item on a 1,500-item ledger created, wrote,
324
+ fsynced, and unlinked 1,503 lock files. It runs from journal replay through
325
+ its terminal record inside the namespace write lock, and every other
326
+ cooperative writer of a provisioned ledger enters that same lock before it
327
+ writes, so the per-item closure excluded nobody the namespace lock did not
328
+ already exclude. Publication now takes no per-item locks. Newly instrumented
329
+ phase counters measured the cost this removes: on 1,500 items the lock phase
330
+ was 11.2 s of the 12.6 s the publication took, against 0.2 s for reading Git
331
+ HEAD. Everything else is unchanged and proved byte-for-byte identical against
332
+ the previous implementation across all six publication outcome classes —
333
+ success, fence refusal, revision conflict, validation refusal, idempotent
334
+ replay, and indeterminate publication — in envelope, claim journal, and item
335
+ bytes. Every cooperative writer of one ledger must be upgraded together: a
336
+ writer that honors only per-ID locks can race one that honors only the
337
+ namespace lock.
338
+ - **The README installs with `@next` and says why.** The registry mandates a
339
+ `latest` dist-tag, so `latest` mirrors `next`; `@next` stays the documented
340
+ spelling and the explicit prerelease consent.
341
+ - **Cuts happen on the release branch, not in a session worktree.** Merge
342
+ session work first, then cut; the cut command refuses to run anywhere but the
343
+ branch tip. The previous two-phase topology is why the last two release tags
344
+ name merge commits rather than their cut commits.
345
+ `docs/adapter-release-path.md` records the ritual.
346
+
347
+ ### Fixed
348
+
349
+ - **A live publication is no longer reported as a broken lock.** Publication
350
+ lock files recorded `"operation": "publish-claimed"`, but the lock reader
351
+ accepts only `create`, `transition`, and `patch`, so a concurrent writer that
352
+ hit a live publication lock classified it `invalid-shape` — a diagnostic that
353
+ says the lock is corrupt. Publication writes no lock files at all now, so
354
+ there is nothing to misclassify.
355
+
356
+ - **A committed `patch` is forwarded instead of reported as an unknown
357
+ outcome.** The shipped adapter engine still named the pre-widening patchable
358
+ pair `number` and `priority`, so every patch a consumer actually sends — a
359
+ body rewrite, a title correction, a relation-list replacement, a declared
360
+ extension member — read as a non-canonical request, failed result
361
+ correlation, and came back `mutation-outcome-unknown` with recovery guidance
362
+ about a write that had provably committed. `number` is the immutable item
363
+ identity and was never patchable at all. The patchable field set is now what
364
+ mutation contract section 9 names — `title`, `priority`, `depends_on`,
365
+ `related`, `body`, `body_append`, and `extensions` — with the two body write
366
+ modes mutually exclusive and the extensions container judged only on its own
367
+ shape, and correlation follows each member to the surface it is observable
368
+ on. The independent reference model already had all of this, so the drift was
369
+ one-sided and no differential test could see it; the new end-to-end
370
+ core-outcome vectors found it on their first run.
371
+
372
+ - **`publish-claimed` now reconciles the journal unconditionally, like every
373
+ other mutating command.** The work-claim contract has always said an
374
+ uncommitted prior mutation refuses the next `create`, `transition`, `patch`,
375
+ **or `publish-claimed`**. The code only reconciled when it happened to
376
+ observe an unresolved `publish-intent`, and an uncommitted legacy mutation
377
+ leaves none behind. A fixture pinned the gap: with a legacy create and
378
+ transition sitting uncommitted, `claim-verify` returned exit 6 and a legacy
379
+ `create` refused with `publication-reconciliation-required`, while
380
+ `publish-claimed` on a claimed item published straight over the unreconciled
381
+ ledger. It now reconciles before the fence decision on every publication and
382
+ refuses with exit 6 `claim-store-unavailable`,
383
+ `details.reason: "publication-reconciliation-required"`, and
384
+ `details.findings` — the same envelope the legacy fence emits, so one
385
+ `claim-verify` clears every blocked path. **A publication behind an
386
+ unresolvable prior intent now returns that refusal instead of exit 6
387
+ `publication-outcome-unknown`.** The old code named the refused publication's
388
+ own outcome uncertain when it had not run at all; `state: "unchanged"` is the
389
+ honest answer, and the blocking finding still travels in `details.findings`.
390
+ The cost is honest about which read is new. Reconciliation adds no
391
+ complete-ledger read: it produces the snapshot the candidate validation and
392
+ the mutation engine's pre-lock read already share, so a claimed publication
393
+ still reads the working-tree ledger exactly twice. It does add the Git `HEAD`
394
+ read — `rev-parse`, one `ls-tree`, and a batched `cat-file` over every item
395
+ blob at `HEAD` — to every publication that previously had no unresolved
396
+ intent. That is the same read `create`, `transition`, `patch`, and every
397
+ claim lifecycle command already perform, so `publish-claimed` now pays the
398
+ toll its peers pay rather than a new one. Each publication persists one clock
399
+ floor, as it did before; a publication behind a pending intent, which used to
400
+ persist two, now persists one as well.
401
+
402
+ - **A shipped adapter no longer advertises trusted approval it cannot
403
+ exercise.** The three shipped entrypoints declared
404
+ `trusted_approval: {"supported": true, "sources": ["consumer"]}` while
405
+ `runAdapterEntrypoint` passed no approval, clock, nonce store, or core
406
+ executable identity to the invoke engine, so `create`, `transition`, and
407
+ `patch` through every shipped adapter refused `consumer-approval-required`
408
+ and could not succeed on any input. The declaration now reflects the runtime
409
+ of the invocation, the way `optional_features.claims` already reflects the
410
+ core probe: a bare entrypoint run declares no trusted approval and refuses a
411
+ mutation `capability-unavailable` with `missing: ["trusted-approval"]`, the
412
+ refusal section 5.1 already required for an absent declaration, and a host
413
+ that wires an approval source declares it truthfully. The two refusals stay
414
+ distinguishable, because they are different facts — no approval source at all
415
+ versus a source that produced no approval for this invocation — and conformance
416
+ case `07-mutation-approval` now pins both against the runtimes that produce
417
+ them, adding one assertion to the conformance suite.
418
+ - **A committed `create` is forwarded instead of reported as an unknown
419
+ outcome.** Both adapter engines required `schema_version: 1` in a create
420
+ result and re-serialized the expected candidate with the schema 1 default, so
421
+ every create against a real ledger — an empty ledger is schema 2 — failed the
422
+ result correlation, was judged an invalid core envelope, and came back as
423
+ `mutation-outcome-unknown` with recovery guidance about a write that had
424
+ provably committed. The ledger's schema version and the number the core
425
+ assigns under its own lock are the only two members of the answer a caller
426
+ could not have known; both engines now read those from the result and
427
+ re-derive the whole candidate from the request bytes, leaving every other
428
+ member pinned by an exact byte comparison against the source the core
429
+ returned. The defect was unreachable while every shipped mutation refused
430
+ before launch, and surfaced the moment a host runtime carried a real approval
431
+ through to the core.
432
+ - Adapter contract section 5's core-request table and section 5.1's approval
433
+ rule named only `create` and `transition`. Both have accepted `patch` since
434
+ adapter contract version 2; the prose now says so.
435
+
436
+ ### Added
437
+
438
+ - **`inspect` answers a bounded per-item lifecycle affordance projection.**
439
+ `inspect --ledger <dir> (--id <id> | --number <n>) --workbench --as-of
440
+ YYYY-MM-DD --json` returns, from one complete validated ledger snapshot,
441
+ `result.workbench`: the projection version, the as-of date, the ledger
442
+ snapshot witness, an `observation` member, a bounded item summary, and one
443
+ `transition_options` entry for every lifecycle target the native edge table
444
+ allows out of that item's kind and status. Each option names its target
445
+ status, its generated decision action or `null`, whether a caller-supplied
446
+ summary and rationale are required, the minimum legal transition date
447
+ `max(created, updated)`, its observed enabled state, and the observed
448
+ precondition issues and multi-item blockers in the exact `transition`
449
+ vocabulary. A workbench can now show a person which transitions an item can
450
+ take without duplicating lifecycle logic and without submitting a mutating
451
+ probe.
452
+
453
+ The read is an observation, not a lease, and says so in the response:
454
+ `observation.authority` is `observed-snapshot` and `observation.rechecked_by`
455
+ names what a later `transition` rechecks under lock — revision, lock, claim
456
+ fence, reconciliation, and candidate validation. It writes no item, lock,
457
+ claim journal, reconciliation log, or Git state, and it takes no lock.
458
+ `transition` and the projection share one lifecycle definition
459
+ (`src/lifecycle.js`), and a differential guard dispatches every projected
460
+ option and every unadvertised target through the real mutation, so an
461
+ advertised affordance cannot drift from what the mutation does.
462
+
463
+ Every variable-size field is bounded and says what it left out: the projected
464
+ title, the relation lists, each option's issues and blockers, and the related
465
+ IDs inside an issue. `--workbench` requires `--as-of`, an unpaired `--as-of`
466
+ is refused rather than ignored, and an `inspect` invocation without
467
+ `--workbench` is byte-identical to before. `capabilities` advertises
468
+ `operations.inspect.workbench` and the three exact bounds
469
+ `max_workbench_title_characters`, `max_workbench_collection_entries`, and
470
+ `max_workbench_response_bytes`; the core contract version stays 5, and the
471
+ projection is negotiated by its own `projection_version`. An invalid ledger is
472
+ `ledger-invalid` at exit 3 with no projection attached, and a projection that
473
+ would exceed its response bound is refused whole with
474
+ `workbench-response-too-large` at exit 2.
475
+
476
+ - **The conformance suite now measures real core outcomes end to end.** A new
477
+ equivalence case, `16-core-outcome-e2e`, carries nine hand-authored scenarios
478
+ that each run the direct real core in one isolated temporary workspace and,
479
+ separately, spawn the real shipped entrypoint over the bootstrap wire against
480
+ the real core in a second workspace materialized from the same before state:
481
+ `inspect` item-not-found, a committed `create`, a committed `transition`, a
482
+ committed `patch` by body replacement and by declared extension member, the
483
+ six-member date refusal, and all three `ledger-mutation` claim-fence refusal
484
+ classes. Success vectors match the exact core exit, stdout bytes, decoded
485
+ adapter streams with their digests and lengths, and the exact ledger
486
+ post-state; refusal vectors match the exact nonzero exit and unchanged ledger
487
+ bytes, and the fence refusals arrive as `ok: true` adapter transport results
488
+ rather than adapter errors. Before this case, no conformance assertion and no
489
+ bootstrap-wire test carried a mutation through a spawned entrypoint into a
490
+ launched core, which is how two real adapter defects shipped. The suite is
491
+ 210 assertions across 16 cases.
492
+
493
+ Determinism comes from fixed inputs and never from normalizing output: a
494
+ caller-supplied ULID, seeded revisions on isolated temporary ledgers, literal
495
+ dates, and — for the two real claim-fence read-backs — a fixed namespace, a
496
+ hand-authored claim journal, and a seeded future clock floor, so the emitted
497
+ `observed_at` is `max(physical_now, floor)` and therefore the floor. That
498
+ makes the refusal bytes fixed without mocking the core clock, and it expires:
499
+ both runners fail loudly and name `2031-01-15T12:01:00.000Z` once wall time
500
+ reaches it. Goldens are authored from the adapter contract, the work-claim
501
+ contract, and the normative `spec/fixtures/mutations/**` bytes; every byte
502
+ reused from those fixtures carries a `derived_from` pin, so drift on either
503
+ side stops the vector and asks for a reviewed golden change instead of
504
+ regenerating one. Only base64, SHA-256, and byte length are derived, and only
505
+ from an already hand-authored byte string.
506
+
507
+ The conformance host gains a granting approval mode, without which no
508
+ mutation can cross the spawned entrypoint at all. **Adapter contract section
509
+ 10 previously said no conformance fixture could manufacture authority; that
510
+ is no longer true, and the claim is withdrawn rather than quietly narrowed.**
511
+ What replaces it is narrower and checkable: the granting mode is reachable
512
+ only from a fixture's own runtime configuration, which no shipped adapter
513
+ package reads and no wire this contract defines carries; every granting
514
+ scenario runs against a throwaway temporary ledger; the approval is minted
515
+ from the binding the engine resolved and canonicalized by the independent
516
+ reference model. The evidence label is the production adapter engine under a
517
+ conformance host approval provider, not a live consumer approval mechanism.
518
+
519
+ - **A host process can wire consumer approval into a shipped adapter
520
+ entrypoint.** `runAdapterEntrypoint` now accepts an optional `hostRuntime`
521
+ carrying the approval source, the current time, the redeemed-nonce store, and
522
+ the core executable identity the host attests. It is a code-level parameter of
523
+ the embedding process and is deliberately absent from every wire: the
524
+ bootstrap request root schema is exact and has no approval member, so an
525
+ approval a model places on the request is an `invalid-invocation` that never
526
+ reaches the gate, exactly as adapter contract section 5.1 requires. The
527
+ approval may be a finished event or a resolver the adapter calls with the
528
+ exact binding it has just resolved — the argument vector, absolute workspace
529
+ paths, and instruction and handoff digests an approval covers do not exist
530
+ until the adapter has built them, so an interactive consumer prompt cannot
531
+ mint the approval any earlier. A resolver that fails produced no approval and
532
+ the mutation refuses; it never proceeds unapproved. The default is unchanged
533
+ and remains no approval. `test/adapter-host-approval-wire.test.js` carries the
534
+ first approved mutation in this repository to cross a spawned entrypoint into
535
+ a launched core and change a ledger, with its binding digest canonicalized by
536
+ the independent reference model rather than by the engine under test. No
537
+ version moved in any domain: `host.trusted_approval` has been optional since
538
+ version 1, the approval object and binding are untouched, and the mechanism is
539
+ invisible on every wire the contract defines. Adapter contract sections 3.2,
540
+ 3.3, 5.1, 10, and 12 state the seam, the honesty rule, the two-runtime
541
+ evidence, and the version argument.
542
+
543
+ - **The cut is one command, and version drift now fails the cut instead of
544
+ shipping.** `npm run release:cut -- <version> --date YYYY-MM-DD` runs on the
545
+ tip of the release branch, proves every version site is accounted for, plans
546
+ the new bytes in memory, runs the full release gate over them, and leaves one
547
+ `Cut <version>` commit and one annotated `v<version>` tag. It stops there:
548
+ push, `npm publish --tag next`, and the registry check stay separate named
549
+ steps, because no local command can undo any of them. Coverage is proved by
550
+ exact-set equality against a hand-maintained
551
+ `scripts/release-version-sites.json`, not by a global grep — the changelog and
552
+ the dated design records must keep naming old versions, so "grep finds
553
+ nothing" would be the wrong test. A release site added next month is
554
+ unmanifested and refuses the cut. `--dry-run` runs the same planner and the
555
+ same gate against a copy of HEAD and then proves the repository unchanged.
556
+ Reruns converge rather than repair: a cut tag at a clean HEAD reports
557
+ `already cut`, a complete cut commit without its tag resumes at tagging, and a
558
+ tag pointing elsewhere refuses.
559
+ - **The changelog can no longer lose its Unreleased section.** A cut opens a
560
+ fresh empty `## Unreleased` and files the released notes beneath it. The two
561
+ previous cuts renamed the heading instead, which left later changes landing
562
+ under an already published release.
563
+ - **The prerelease channel policy is stated and checkable.**
564
+ `npm run release:channels -- check|repair <version>` encodes it: `latest`
565
+ mirroring `next` at the published version and the first published alpha
566
+ deprecated. The first-choice policy — no `latest` at all, so a bare install
567
+ fails loudly — was refused by the registry itself: npm rejects deleting the
568
+ `latest` tag with E400 (verified live), so the current prerelease replaces
569
+ the dead first alpha as the forced default. `check` is read-only and is the
570
+ post-publish verification step; `repair` is idempotent and never unpublishes.
571
+
572
+ - **`--auto-commit` folds the commit-per-mutation ceremony into the mutation.**
573
+ The invariant is correct and the ceremony around it was the consumer's most
574
+ frequent daily cost: mutate, `git add`, `git commit`, `claim-verify`, repeat,
575
+ ten times for ten items. On a provisioned merge-coordinated ledger the new
576
+ opt-in bare flag on `create`, `transition`, `patch`, and `publish-claimed`
577
+ does that loop inside one invocation. It takes a per-working-tree mutex,
578
+ refuses any staged path anywhere and any dirty path under the ledger, checks
579
+ Git identity, runs an internal pre-mutation `claim-verify`, runs the mutation
580
+ unchanged, then commits **exactly** the changed item plus at most one
581
+ `.wowbagger/reconcile-<namespace>.md` under a fixed subject
582
+ (`wowbagger: transition item #7`; the canonical item ID for a schema-1 item
583
+ with no number). It verifies the resulting commit's parent, subject,
584
+ changed-path set, and every blob, then runs `claim-verify` again before it
585
+ answers. Success adds `git_commit`, `commit_paths`, and `claim_verified` to
586
+ `result`.
587
+
588
+ There is no configuration file setting, environment default, or repository
589
+ default, because a hidden default would make existing mutation automation
590
+ create Git commits unexpectedly. An invocation without the flag is
591
+ byte-identical to before, so the core contract stays 3 and the work-claim API
592
+ stays 1. The flag is direct-CLI only in this release; no adapter advertises or
593
+ constructs it.
594
+
595
+ What it will not do: commit anything on `state: "unchanged"` or
596
+ `state: "unknown"`, including the documented reconcile-log residue a refused
597
+ `publish-claimed` leaves behind; stage a path outside the ledger; broad-add,
598
+ amend, squash, reset, clean, stash, or unstage; pass `--no-verify` or disable
599
+ signing; fabricate an author; customize a commit message; or push, fetch,
600
+ pull, merge, or rebase. Hooks through `core.hooksPath`, `commit.gpgSign`, and
601
+ signing programs are honoured, and a hook that rewrites the subject or the
602
+ tree is reported rather than accepted.
603
+
604
+ - **An honest commit-failed contract, and one idempotent recovery verb.** A
605
+ post-publication Git failure that proves the commit is absent is exit 6
606
+ `git-commit-failed` with `state: "committed"` — the state still describes item
607
+ publication, not Git finalization — carrying the published revision, the exact
608
+ ledger-relative commit set with digests, `failure_stage`, `reason`, and a
609
+ bounded `recovery_token`. `create`, `transition`, and `patch` keep the core
610
+ domain; `publish-claimed` keeps `ledger-publication` and its top-level
611
+ `operation_id`. An **ambiguous** Git outcome is `git-commit-outcome-unknown`,
612
+ never `git-commit-failed`, and a commit that stands while reconciliation then
613
+ refuses is `post-commit-reconciliation-failed`. No failure envelope carries
614
+ hook output, signing output, absolute paths, or environment values.
615
+
616
+ New command: `wowbagger mutation-finalize --ledger <dir> --recovery-token
617
+ <token> --json`, answering in the work-claim domain because it changes Git
618
+ reconciliation state and no item byte. It re-derives every path from the
619
+ ledger and the provisioned namespace — the token is a witness, never authority
620
+ to select a path — re-checks the current bytes and the foreign-change rules,
621
+ creates the exact commit if it is absent, then runs `claim-verify`. When
622
+ `HEAD` already holds that exact commit it verifies and returns it without
623
+ creating a second one, so a lost response and a failed commit recover through
624
+ the same command, and repeating it is safe.
625
+
626
+ A failed attempt leaves its own commit set staged, because the design forbids
627
+ unstaging. Recovery tolerates exactly that residue and refuses anything else
628
+ staged; until it runs, the next `--auto-commit` invocation refuses on
629
+ `staged-paths-present`, which is the intended signal.
630
+
631
+ ## 0.1.0-alpha.6 - 2026-08-17
632
+
633
+ ### Added
634
+
635
+ - **`set.extensions` gives consumer-owned extension members a sanctioned patch
636
+ path.** Two field reports in two days: a consumer's own identifier field
637
+ rides a permitted extension member, and a wrong or missing one had no
638
+ ledger-side repair verb at all. On a provisioned ledger the hand-edit that
639
+ filled the gap is a stale write, so the protocol was forcing the edit it then
640
+ punished. `patch` now accepts an `extensions` container whose members name
641
+ extension members and whose values replace each one whole; `null` removes a
642
+ member. The fixed `set` allowlist is unchanged — `extensions` is one more
643
+ name on it, not an opening for arbitrary keys — so a top-level typo is still
644
+ an `unknown-member` refusal. Which members the container may name comes from
645
+ the committed `<ledger>/.wowbagger/extensions.json`, which declares a member
646
+ name and one value type each (`string`, `integer`, `boolean`, `string-list`).
647
+ **A ledger without that file has no patchable extension member at all**, and
648
+ the refusal names the missing declaration. Five new
649
+ `patch-precondition-failed` issue codes carry the refusals —
650
+ `extension-declaration-missing`, `extension-declaration-invalid`,
651
+ `extension-not-declared`, `extension-value-invalid`, `extension-anchored` —
652
+ in the existing four-member issue shape, with the member at fault named in
653
+ `field`. A member the item writes with a YAML anchor or alias is refused
654
+ rather than replaced, because replacing it would change every node bound to
655
+ the anchor; every member the request does not name keeps its exact
656
+ `extensionNodeIdentity` guarantee. The declaration authorizes a write and
657
+ never describes the ledger: `validate` does not read it, so an item whose
658
+ extension member disagrees with it stays valid and stays repairable. Nested
659
+ extension values still have no patch path and stay a reviewable hand-edit.
660
+ Core contract version stays 3 — the patch request schema widens and no
661
+ response envelope member is added, removed, or renamed — but version 3 is
662
+ published, so `contract_version` cannot answer whether a core carries this:
663
+ probe by sending an extension patch and reading the refusal, or pin the
664
+ distribution version. Documented in mutation contract section 9 and pinned by
665
+ `spec/fixtures/mutations/patch-extensions/`.
666
+
667
+ - **`claim-adopt` gives `unauthorized-revision` a non-destructive remedy.** A
668
+ consumer's staging checkout was blocked exit 6 on three items whose bodies
669
+ were hand-edited in a design session and merged. The refusal was correct, but
670
+ the only documented remedy — restore the authorized revision, then
671
+ `claim-verify` — discards reviewed, merged work. `claim-adopt` records that an
672
+ operator ruled the committed bytes legitimate and moves the coordinator's
673
+ authorized revision to them. It writes no item byte, so `updated` and the body
674
+ survive exactly. It is a standalone verb in the work-claim domain, a sibling
675
+ of `claim-verify`, and it is per item and per revision explicit: the request
676
+ names the item, the revision it believes is authorized, the revision being
677
+ adopted, and who is ruling. There is no adopt-all. It refuses
678
+ `adoption-witness-mismatch` on a stale witness (including a replay of a
679
+ successful adoption), `claim-held` while an unexpired claim holds the item,
680
+ `adoption-revision-uncommitted` unless the adopted revision is at Git `HEAD`
681
+ and in the caller's own working tree, and `adoption-ledger-invalid` when the
682
+ complete ledger would not validate. Success appends one `revision-adoption`
683
+ journal entry naming who, when, and both revisions, so the audit trail records
684
+ the ruling instead of losing it. Adoption is not a fence hole: the next
685
+ out-of-protocol edit is `unauthorized-revision` again, measured against the
686
+ adopted revision. Additive at contract version 1 — one new command, one new
687
+ journal entry type, three new error codes, no existing shape changed.
688
+ Documented in work-claim contract section 3.3 and pinned by
689
+ `spec/fixtures/work-claims/revision-adoption/`.
690
+
691
+ ### Changed
692
+
693
+ - **The report's epic-enablement factor now counts done or killed children
694
+ only.** It counted every child carrying a terminal date, which folded
695
+ archived and deferred children into the numerator: an epic with one done,
696
+ one archived, one deferred, and one backlog child reported enablement 0.75
697
+ while the mutation contract's terminal ratio for the same epic was 0.25. Two
698
+ numbers wore one name. A terminal date is not a terminal disposition —
699
+ archived restores and deferred undefers, both documented edges — so a parked
700
+ child is work postponed, not work retired, and counting it reported progress
701
+ that one transition takes back. The factor now reads the same done-or-killed
702
+ set as the contract and the epic complete rollup: one definition, three
703
+ surfaces. This is display-only and recomputed at render time; no ledger byte,
704
+ no wire shape, and no `ready` ordering changes. What does change is the
705
+ report: an epic with parked children reports a lower percentage, and its open
706
+ children rank lower on the epic-enablement step of `work next`.
707
+ - **Every `unauthorized-revision` remediation string now names both remedies.**
708
+ It was one sentence naming only the restore path, which reads as an
709
+ instruction to throw the edit away; the field report above did exactly that.
710
+ It is now two sentences, and each says what happens to the edit: `Restore the
711
+ authorized revision at <path>, then run claim-verify; that discards the edit.
712
+ Or adopt the committed revision of <path> with claim-adopt, then run
713
+ claim-verify; that keeps the edit.` The finding's `code`, `reason`,
714
+ `observed_surface`, `expected_path`, and revisions are unchanged; only the
715
+ human-readable `remediation` prose changed. `revision-regression` keeps its
716
+ restore-only string on purpose: it only fires while an active claim holds the
717
+ item, which is a state adoption refuses.
718
+ - **`patch` corrects an item title.** `set.title` takes a non-empty schema
719
+ string and replaces the title whole, under the same per-ID lock, exact-byte
720
+ compare-and-swap, candidate complete-ledger validation, and atomic
721
+ publication as every other patch; an item with an active claim is refused,
722
+ and `updated` moves to `request.date`. The scalar node is rewritten in place,
723
+ so the quoting style, the comments, the anchors, and every extension node
724
+ survive byte for byte. This closes a protocol contradiction reported twice
725
+ from the field: correcting a title used to require an out-of-protocol edit,
726
+ and on a provisioned ledger that edit is a stale write, so the next mutation
727
+ refused exit 6 `unauthorized-revision` and every later mutation stayed
728
+ blocked. `null` follows the frontmatter removal convention onto
729
+ `candidate-invalid`, because title is required; `""` is refused one step
730
+ earlier, at the request.
731
+ - **The mutation contract states the frontmatter ownership boundary.** Section
732
+ 9 gains a `Frontmatter ownership` table: one row per member, sorted into
733
+ core-owned (`schema_version`, `id`, `number`, `status`, `created`, `updated`,
734
+ the terminal dates, `decisions`), consumer-editable through `patch` (`title`,
735
+ `priority`, `depends_on`, `related`, `body`), and create-once (`kind`,
736
+ `provenance`, `parent`, `snoozed_until`). The boundary was previously
737
+ discoverable only by sending a patch and reading the refusal. A docs test
738
+ pins every row and the skill teaches the same three classes.
739
+
740
+ ### Decided
741
+
742
+ - **`kind` stays unpatchable, and the contract now says why.** A task-to-epic
743
+ flip changes which parent and children rules the item is validated under and
744
+ which lifecycle edges it may take. It needs its own verb with its own
745
+ preconditions, not a wider patch set.
746
+ - **Extension members stay out of `patch`, and the contract records the
747
+ reasons.** Two field reports asked for a sanctioned path for consumer-owned
748
+ identifier fields riding permitted extension members. The widening was
749
+ assessed against title's machinery and is not the same machinery: the
750
+ fail-closed `set` rule has no room for an arbitrary key, candidate validation
751
+ constrains no extension value, nested and anchored values do not survive a
752
+ whole-value replace the way a scalar does, and the oracle has no observable
753
+ surface to correlate an extension patch against. Section 9 names what a real
754
+ path would need — a `set.extensions` container, a declared per-ledger
755
+ extension schema, a stated rule for anchored and nested values, and an
756
+ oracle-visible surface — so the deferral is a design boundary rather than a
757
+ silence. Their status is stated in the ownership table either way.
758
+ - **`patch` gains `set.body_append`.** It takes a JSON string written after the
759
+ item's current body, under the same string rules `set.body` takes: the empty
760
+ string is valid, the bytes are the UTF-8 encoding of the string exactly, and
761
+ `null` is refused at `/set/body_append` because appending nothing is the empty
762
+ string. It is the same byte splice after the closing delimiter, so no
763
+ frontmatter byte moves, `updated` becomes request.date, and every existing
764
+ body byte survives — the request never names them. `body` and `body_append`
765
+ are mutually exclusive in one request: naming both is an `invalid-request`
766
+ issue at `/set`, exit 2, unchanged. This covers the annotation shape a mirror
767
+ consumer needs without making it carry a merge. The core contract stays
768
+ version 3 — it widens the patch request schema and moves no response envelope
769
+ member — but version 3 is already published without it, so a consumer
770
+ **cannot** probe for append support by reading `contract_version`. Send an
771
+ append and read the refusal instead: a core without it answers `unknown-member`
772
+ at `/set/body_append`, exit 2, unchanged.
773
+
774
+ ### Documentation
775
+
776
+ - **The allowed-edges table carries the defer and undefer edges.** `task` and
777
+ `epic` `backlog` to `deferred` and `deferred` to `backlog` have shipped in
778
+ `src/mutation.js` since deferral existed, both requiring a decision, and the
779
+ ownership table already documented `deferred` as a core-owned field that
780
+ `transition` writes on a defer. Section 8's edge table listed neither row, so
781
+ the one place a consumer looks up what it may drive under-reported the
782
+ lifecycle by two edges. Both rows are added with the evidence the code
783
+ generates — `set deferred; append defer decision` and `clear deferred; append
784
+ undefer decision` — and a docs guard pins the kind, the date, and the
785
+ decision on each. No emitted byte changes and the core contract stays version
786
+ 3: this documents shipped edges, it does not add them.
787
+
788
+ - **The epic derivation section cites one shared definition instead of a
789
+ divergence.** It recorded the report's epic-enablement factor as a different,
790
+ wider number than the terminal ratio. The report factor was narrowed to match
791
+ (see Changed above), so the paragraph is replaced: the contract, the epic
792
+ complete rollup, and the report all count done or killed direct children over
793
+ all direct children, and the section now says outright that a terminal date is
794
+ not the test. The docs guard is re-pointed at the new truth rather than
795
+ relaxed.
796
+
797
+ - **The contract states that `set.body` replaces and never merges.** A consumer
798
+ mirroring an external source regenerated an item body from its upstream card
799
+ and destroyed a ledger-only annotation; every check passed, because
800
+ `expected_revision` is a byte-level lost-update guard with no semantic safety.
801
+ Mutation contract section 9 and the skill's body bullet now say it plainly:
802
+ the replacement is total, and a mirroring consumer MUST read-modify-write from
803
+ the current item body and MUST never regenerate from the source alone. Docs
804
+ guards pin both sentences.
805
+
806
+ - **The contract documents the selector an `inspect` `item-not-found` refusal
807
+ echoes.** `inspect --number <n>` on a number no item carries emits
808
+ `details: {"number": <n>}`, and it has done so since `--number` shipped in
809
+ 0.1.0-alpha.5. Mutation contract section 5 claimed these details contain only
810
+ `id`, so the published prose and the published wire disagreed. Section 5 now
811
+ states the rule the runtime follows — the details carry exactly the selector
812
+ the request used, `id` for `--id` and `number` for `--number` — and
813
+ `spec/fixtures/mutations/inspect-number-not-found/` pins it. No emitted byte
814
+ changes and the core contract stays version 3: this documents a shipped
815
+ shape, it does not introduce one. The adapter surfaces still require id-only
816
+ details, deliberately: the adapter's `inspect` request accepts no `number`
817
+ member and always invokes `--id`, so it can never see the number variant.
818
+
819
+ ## 0.1.0-alpha.5 - 2026-08-16
820
+
821
+ ### Breaking
822
+
823
+ - **`number` is no longer caller-settable on schema version 2 ledgers.**
824
+ `create` refuses a request supplying `item.number` and assigns the next
825
+ number itself (`max + 1` under the number-index lock); `patch` refuses
826
+ `set.number` because the number is the immutable item identity. A consumer
827
+ mirroring a legacy backlog cannot carry its legacy numbers into wowbagger
828
+ handles — keep legacy identifiers in a permitted extension member or in the
829
+ item body instead. (Shipped as part of the number-as-identity work; this
830
+ notice was added after the 0.1.0-alpha.5 tarball was cut, so the packaged
831
+ changelog carries it only inside the contract version 3 delta note.)
832
+
833
+ ### Added
834
+
835
+ - **The report draws the whole ledger as a 3D dependency graph.** It sits below
836
+ the evidence layer, under the decision surface. Every item is a node labelled
837
+ `#N`, coloured by readiness or terminal status and sized by the same
838
+ transitive unblocking leverage the recommended order uses; `depends_on` edges
839
+ are straight and arrowed, `parent` edges are curved and unarrowed, and both
840
+ run from the prerequisite to the item it releases. Hovering or clicking a node
841
+ shows its number, title, status, age, leverage, and the same reasons line the
842
+ ranked list prints for it. The renderer is `3d-force-graph` 1.80.0 over
843
+ Three.js r183, vendored at `vendor/3d-force-graph/` with its upstream SHA-256
844
+ recorded beside it and pinned by a test, and inlined at generation time: the
845
+ report stays one self-contained file and fetches nothing at generation or view
846
+ time. It costs roughly 1.3 MB of report size. A browser without WebGL gets the
847
+ section's plain explanation and a per-node roster instead; no
848
+ decision-relevant content exists only in the 3D view.
849
+
850
+ ### Changed
851
+
852
+ - **An invalid ledger can now be diagnosed with the documented commands.** One
853
+ invalid item still refuses every read and every guarded mutation on that
854
+ ledger, but the refusals no longer hide what the operator has to read.
855
+ `inspect` keeps refusing exit 3 `ledger-invalid` — handing back a revision
856
+ from a ledger the core has not judged would read as a mutation precondition,
857
+ and there is no flag that skips validation — and its refusal now carries
858
+ `error.details.item`, the same lossless snapshot the success envelope
859
+ defines, for the item the request selected, whenever no validation error
860
+ names that item's path. A faulted item is withheld; `validate` already names
861
+ its repair. `claim-verify` now reports `result.ledger_validation`, carrying
862
+ `valid` and `errors` exactly as the bare `validate` result does, plus a
863
+ `remediation` when the ledger is invalid. Its claim answer is unchanged:
864
+ `findings`, `state`, and the exit status still describe claim state alone, so
865
+ a consistent journal over an invalid ledger is still exit 0 with
866
+ `findings: []` — it just no longer pretends that is a clear road. The report
867
+ costs no extra ledger read. Fixtures extend item #104's misplaced-item
868
+ scenario.
869
+ - **The adapter forwards `inspect` refusals instead of calling them protocol
870
+ errors.** Both the engine and the independent oracle demanded a canonical
871
+ mutation request before they would accept any error details, which no read
872
+ command has, so every `inspect` `item-not-found` and `ledger-invalid` refusal
873
+ was mapped to `core-protocol-error`. The precondition now applies only to
874
+ mutation commands. The same surfaces accept the `expected_path` and
875
+ `remediation` that item #104 added to a validation error, and the optional
876
+ `details.item` on an `inspect` `ledger-invalid` refusal — on `inspect` only;
877
+ a mutation refusal that carries one is still rejected.
878
+
879
+ - **The report's content security policy now also forbids `connect-src`.** The
880
+ report has never opened a connection; the policy now says so.
881
+
882
+ - **The `item-outside-layout` validation error now names the expected path and
883
+ the relocation that repairs it.** It keeps its stable code and its actual
884
+ `path`, and gains `expected_path` plus a `remediation`; its message names
885
+ both paths. A committed item outside the configured items directory refuses
886
+ every read and every guarded mutation on that ledger, including ones that
887
+ never touch the misplaced item, so the refusal has to say where the item
888
+ belongs. The claim fence is not involved: `claim-verify` reports no finding
889
+ on such a ledger, refuting an earlier consumer report that a
890
+ root-misplaced item makes the fence report `stale-write-detected` with
891
+ `actual_revision: null`. Fixtures pin both configuration orders — layout
892
+ bound first, and layout bound after the item was already committed at the
893
+ root.
894
+ - **The core contract version is now `3`.** Every core command envelope
895
+ (`capabilities`, `inspect`, `create`, `transition`, `patch`, `mint-id`,
896
+ `report`) carries `contract_version: 3`, the shipped adapters require core
897
+ contract version 3, and the installed skill's version check gates on 3. A
898
+ version 1 or version 2 consumer fails closed against this core, which is the
899
+ point of the bump. Version 3 is version 2 plus four deltas against published
900
+ `0.1.0-alpha.4`: the widened `date-before-created` / `date-before-updated`
901
+ issue shape carrying `item_created` and `item_updated` (the delta that forced
902
+ the bump — a version 2 consumer validating issue members exactly refuses the
903
+ six-member shape); the patch field set widening from `number`/`priority` to
904
+ `priority`/`depends_on`/`related`; number as the core-assigned immutable item
905
+ identity on schema version 2, with `create` refusing a supplied number and
906
+ `inspect` accepting `--number`; and `create` deriving its published path from
907
+ a committed `.wowbagger/layout.json`. The mutation contract's "Contract
908
+ versions" section carries the full enumeration. The legacy work-claim,
909
+ ledger-publication, and ledger-mutation envelopes and
910
+ `result.operations.work_claim.api_version` are separate version domains and
911
+ stay at 1; the adapter contract stays at 2.
912
+
913
+ ### Added
914
+
915
+ - The README and the installed skill warn that `git mv` refuses a freshly
916
+ created item, because `create` writes an untracked file and the `git add -A`
917
+ behind it in an unchecked batch commits the item at the ledger root instead.
918
+ Both state the safe sequence: plain `mv`, then `git add`, checking every exit
919
+ code before the commit. The warning sits in the `0.1.0-alpha.4` boundary text
920
+ that already tells consumers that core ignores the layout file.
921
+ - **`patch` can replace an item body.** `set.body` takes a JSON string that
922
+ replaces the whole body under `create`'s body rules, so a consumer whose
923
+ items mirror an external card updates them through the managed path instead
924
+ of hand-editing the Markdown. A body patch rewrites no frontmatter byte —
925
+ anchors, aliases, comments, quoting, styles, member order, and extension
926
+ members all survive, and only `updated` changes, as it does for every patch.
927
+ A body may be set in the same `set` as `priority`, `depends_on`, or
928
+ `related`, in one compare-and-swap write. `null` is refused at `/set/body`:
929
+ the body is a region of the file, so removing it means `""`, not null. A
930
+ claimed item, a stale revision, and a non-string body refuse as before. This
931
+ widens the patch request schema inside core contract version 3 and does not
932
+ move the version.
933
+ - One envelope rule now covers every `--json` response. The mutation contract
934
+ states the response domains (core, work-claim, ledger-publication,
935
+ ledger-mutation, and bare result), the dispatch steps a generic consumer
936
+ follows, which domain each command's success and each refusal class answers
937
+ in, and the exact root members of each shape. Both sanctioned exceptions are
938
+ stated with their reasons: `validate` and `ready` stay bare results because
939
+ scripts and fixtures depend on those bytes, and a claim-fenced refusal to
940
+ `create`, `transition`, or `patch` answers in the `ledger-mutation` domain
941
+ with `command: "<command>-v1"` and `contract_version: 1` because it is the
942
+ work-claim contract refusing, not the core contract. The work-claim contract
943
+ now names all three of its `namespace` values.
944
+ `spec/fixtures/envelope-domains/manifest.json` pins all 37 response classes,
945
+ and `test/envelope-dispatch.test.js` walks every one of them through the
946
+ documented dispatch rule and rejects drift in either direction. No emitted
947
+ byte changed and no contract version moved.
948
+ - `report` now renders a sequencing dashboard instead of a state snapshot. The
949
+ HTML opens with **Work next**, the ready set in a recommended order with the
950
+ factors that placed each entry printed beside it; then **Attention**, naming
951
+ blockers by number, the oldest open work with its age, and started work past
952
+ this ledger's own 85th-percentile cycle time; then an evidence layer with
953
+ aging buckets, weekly arrivals against completions, accept-to-complete cycle
954
+ time, and a Monte Carlo forecast as 50 and 85 percent bands. State counts,
955
+ item cards, and swarm batches remain, below that decision surface. Relations
956
+ and readiness reasons inside the drill-down now name items by number.
957
+ Ordering is a report-layer derivation, recomputed from ledger bytes at render
958
+ time and never persisted: `ready --json`, its four-step order, and the
959
+ mutation contract are unchanged. The report file stays self-contained with no
960
+ external runtime dependency.
961
+ - Report configuration accepts two more `fields` mappings, `class` and `due`.
962
+ `class` is a class of service from `expedite | fixed-date | standard |
963
+ intangible`; `expedite` lifts an item above every other ready item, an absent
964
+ value means `standard`, and an unrecognised value is ranked as standard and
965
+ reported in the report rather than dropped. `due` is an ISO calendar date
966
+ ordered by proximity. Both ride the existing extension-member channel, so no
967
+ core field carries them.
968
+ - The commit-per-mutation invariant is documented. On a provisioned ledger,
969
+ every mutation must be committed to Git before the next mutating command,
970
+ and `claim-verify` is the reconciliation procedure for the exit 6
971
+ `publication-reconciliation-required` refusal. The mutation contract, the
972
+ work-claim contract, the README, and the installed skill's claimed and
973
+ unclaimed loops all state the rule and the loop it implies. The mutation
974
+ contract also records why validating against working-tree bytes was
975
+ rejected.
976
+ - `claim capabilities --ledger <dir> --json` now advertises
977
+ `result.backend.write_serialization`. A provisioned Git-journal backend
978
+ reports `scope: "all-worktrees-of-one-repository"` and
979
+ `blocks_until: "peer-commit-visible-in-this-checkout"`; an unprovisioned
980
+ backend reports `scope: "none"`. This makes the serialization the shared
981
+ Git-common-directory journal already performed discoverable instead of
982
+ implied. The core `capabilities` envelope is unchanged; this change is not
983
+ one of the version 3 deltas.
984
+
985
+ - A `date-before-created` or `date-before-updated` issue now carries
986
+ `item_created` and `item_updated` after `related_ids` — the target item's own
987
+ dates at refusal time, both dates on both codes, on `transition` and `patch`
988
+ alike. One refusal now states the whole acceptable date window, so correcting
989
+ the request no longer costs an `inspect` round-trip. No other issue code
990
+ changes shape; a consumer that validates issue members exactly must accept
991
+ six members for these two codes. This widening is the reason the core
992
+ contract version moves to 3 (see Changed, above).
993
+ The mutation contract and the installed skill now also state that `create`
994
+ derives `created` from the ULID timestamp, which is UTC, with the
995
+ across-midnight example that produces this refusal.
996
+
997
+ ### Documentation
998
+
999
+ - **`create` stays journal-silent, and the work-claim contract now says why.**
1000
+ Section 3.1 already stated that `create` records no claim-journal entry and
1001
+ therefore never blocks a sibling worktree. It now records the decision to
1002
+ keep that asymmetry and the three reasons behind it: create's publication is
1003
+ already atomic, no-clobber, and byte-verified; journaling create would
1004
+ serialize every worktree on the highest-volume mutation; and the remaining
1005
+ exposure window closes at the item's first `transition` or `patch`. The
1006
+ window is stated honestly — until that first journal-visible mutation an
1007
+ out-of-protocol overwrite of a created item is not detected, and a commit
1008
+ alone does not close the window, because reconciliation compares only the
1009
+ revisions the journal expects. `test/create-journal-asymmetry.test.js` pins
1010
+ both halves end to end. No behaviour changed.
1011
+
1012
+ ### Fixed
1013
+
1014
+ - A claim-fence refusal no longer reaches the agent as
1015
+ `mutation-outcome-unknown`. Both adapter engines classified every response
1016
+ with the core envelope validator, so a fenced refusal — `namespace:
1017
+ "ledger-mutation"`, `command: "<command>-v1"`, `contract_version: 1`, `state:
1018
+ "unchanged"` — failed core validation and became "the mutation may have been
1019
+ applied; inspect current state before retrying", on every fenced refusal on a
1020
+ provisioned ledger, about a write that provably never ran. The adapter now
1021
+ dispatches on the response domain first, exactly as the mutation contract's
1022
+ section 2 rule requires, and validates a fenced refusal on the work-claim
1023
+ contract's terms: `claimed-item-write-refused` on `create`,
1024
+ `active-claim-write-refused` on `transition` and `patch`, and
1025
+ `claim-store-unavailable` on any mutation, each with its pinned message, exit,
1026
+ and permitted states, its read-back bound to the item the caller asked to
1027
+ write, and its reason plus findings and remediation forwarded verbatim. A
1028
+ `claim-store-unavailable` refusal that declares `state: "unknown"` stays an
1029
+ unknown outcome, and so does any namespaced envelope the adapter cannot
1030
+ classify. Adapter contract section 6.1 states the rule and the honest-outcome
1031
+ guarantee; five conformance vectors pin it, the differential test replays each
1032
+ through both engines, and no version moved in either domain.
1033
+ - A mutation on a claim-protected ledger now reads the complete ledger twice
1034
+ instead of three times. Journal reconciliation and the mutation engine's
1035
+ pre-lock phase were separate unlocked reads of the same directory inside one
1036
+ claim-lock hold, and reconciliation writes nothing a complete load reads, so
1037
+ the pre-lock phase reuses reconciliation's snapshot. On a 1,500-item
1038
+ provisioned fixture a create fell from about 1.15 s to about 0.89 s and a
1039
+ transition from about 1.17 s to about 0.91 s, matching one full load at about
1040
+ 0.29 s. The read under lock stays: it is what decides the revision
1041
+ compare-and-swap and the lock-closure stability check, and every decision
1042
+ drawn from the shared snapshot is re-made against it. A lock-closure retry
1043
+ still reads fresh. No validation rule changed, and a mutation on a plain
1044
+ directory is unaffected.
1045
+ - A mutation on a large provisioned ledger no longer spends its wall time in
1046
+ process spawns. Git HEAD reconciliation read every committed item with its
1047
+ own `git show`, one subprocess per item, serially; it now reads them with
1048
+ one `git cat-file --batch` subprocess per 16 MiB of tree content. On a
1049
+ 1,500-item fixture a create fell from about 15.4 s to about 1.2 s and a
1050
+ transition from about 15.4 s to about 1.3 s. The reconciliation reads the
1051
+ same bytes for the same items, and no validation is skipped: candidate
1052
+ validation still validates the complete ledger.
1053
+
1054
+ - Every reconciliation finding that blocks a mutation now carries a
1055
+ `remediation` string naming the path to act on and `claim-verify`.
1056
+ `revision-regression`, `legacy-mutation-outcome-unknown`, and
1057
+ `publication-outcome-unknown` previously blocked with no recovery action;
1058
+ they now also carry `expected_path` when it is identifiable.
1059
+
1060
+ - A committed `.wowbagger/layout.json` now binds the ledger's item directory.
1061
+ `create` derives its path from that configuration. Validation rejects parsed
1062
+ items outside it, special or symbolic layout files, and metadata-directory
1063
+ aliases. Malformed configuration fails closed. Ledgers without the file
1064
+ retain the root-level `<id>.md` layout.
1065
+ - A refused legacy mutation and a clean `claim-verify` now leave the ledger
1066
+ working tree byte-identical. The tracked reconciliation log projects only
1067
+ journal entries that record a decision, so per-invocation clock entries no
1068
+ longer dirty it, and a successful legacy mutation now projects its own
1069
+ entries before returning instead of one command later. Batch tooling no
1070
+ longer needs to stage the log after a failure.
1071
+ - `claim-verify` now classifies stale writes as unauthorized revisions, missing
1072
+ Git finalization, worktree synchronization, or pending claimed publication.
1073
+ Working-tree deletions of an authorized Git revision are unauthorized.
1074
+ Findings name the expected item path and give a direct recovery action.
1075
+ - The contracts and the skill now state that a provisioned ledger's claim
1076
+ journal serializes every worktree of one repository, and that a recorded
1077
+ write blocks mutations in the other worktrees until its commit is visible
1078
+ there. `limits.cross_worktree_coordination: false` is documented as "the
1079
+ core never synchronizes checkouts", not as independent worktree writes.
1080
+ Both stale-write remedies, the moving-`expected_revision` trap, and the
1081
+ failed copy-the-item-in workaround are documented and pinned by tests.
1082
+
1083
+ ## 0.1.0-alpha.4 - 2026-08-14
1084
+
1085
+ ### Added
1086
+
1087
+ - `report` validates a ledger and atomically writes a deterministic,
1088
+ self-contained HTML report from `.wowbagger/report.json`. The report includes
1089
+ canonical readiness, semantic-field search, filters, sorting, grouping,
1090
+ three detail levels, terminal history, and optional area-diverse swarm
1091
+ batches. This repository includes a local report configuration and ignores
1092
+ the generated artifact.
1093
+
1094
+ ### Fixed
1095
+
1096
+ - The Claude Code adapter now declares Darwin `supported`. Native Darwin
1097
+ conformance passes all 183 common-vector assertions across all 15 cases, so
1098
+ configured consumer workspaces can invoke the published adapter read path.
1099
+
1100
+ ## 0.1.0-alpha.3 - 2026-08-12
1101
+
1102
+ ### Fixed
1103
+
1104
+ - Published install and upgrade guidance now names this release's immutable Git
1105
+ tag and uses the prerelease `next` npm channel instead of the older `latest`
1106
+ artifact. Item 62.
1107
+ - The installed plugin skill now requires the exact core distribution version
1108
+ that shipped with it, in addition to core contract version 2. This detects an
1109
+ older core that shares the contract number but lacks behavior required by the
1110
+ newer skill. Item 64; item 63 records the rejected capability-schema change.
1111
+
1112
+ ## 0.1.0-alpha.2 - 2026-08-12
1113
+
1114
+ ### Fixed
1115
+
1116
+ - `claim-verify` now reports `git_finalized` and `git_commit` for each successful
1117
+ claimed publication. Reconciliation logs stay inside the configured ledger,
1118
+ including repository-root ledgers. Items 55-57.
1119
+ - A new empty ledger now starts on schema version 2. Existing non-empty
1120
+ schema-version-1 ledgers remain compatible until migration. Item 54.
1121
+ - The installed skill now identifies the active claim as the work-in-flight
1122
+ signal while the item remains in `backlog`. Item 58.
1123
+ - The npm package now ships the mutation and work-claim contract documents that
1124
+ the installed skill references. Item 59.
1125
+ - The npm package now ships the documented schema-version-2 migration
1126
+ entrypoint for package-only consumers. Item 60.
1127
+ - The installed work-claim contract now distinguishes durable reconciliation
1128
+ state from per-publication Git finalization. Item 61.
1129
+
1130
+ - The Claude Code plugin manifest and marketplace metadata now use the same
1131
+ distribution version as `package.json`. The packaging gate rejects release
1132
+ identity drift before npm, Git, or marketplace publication. Item 48.
1133
+ - Capability help now distinguishes the core's unbound default claim profile
1134
+ from one provisioned ledger's work-claim profile. It names
1135
+ `contract_version` as the core version and
1136
+ `operations.work_claim.api_version` as the work-claim API version. Items 47
1137
+ and 51.
1138
+ - `provision --help`, README, and the shipped skill now expose the accessible
1139
+ Git-checkout prerequisite and the pre-provision
1140
+ `claim capabilities --ledger <dir> --json` gate. Item 50.
1141
+ - The isolated consumer dogfood runbook now creates or selects the disposable
1142
+ worktree before agent launch and requires a session-root Git preflight before
1143
+ installation or ledger mutation. Item 52.
1144
+
10
1145
  ## 0.1.0-alpha.1 - 2026-08-11
11
1146
 
12
1147
  ### Behaviour changes