wowbagger 0.1.0-alpha.9 → 0.5.0
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 +509 -0
- package/README.md +272 -136
- package/docs/adapter-contract.md +1 -1
- package/docs/host-contract.md +7 -1
- package/docs/mutation-contract.md +354 -82
- package/docs/work-claim-contract.md +466 -88
- package/package.json +2 -2
- package/schemas/core-capabilities-response.json +1 -1
- package/schemas/core-envelope.json +4 -3
- package/schemas/index.json +18 -0
- package/schemas/ledger-repair-proposal.json +170 -0
- package/schemas/ledger-repair-request.json +61 -0
- package/schemas/ledger-repair-response.json +90 -0
- package/schemas/report-config-v1.json +4 -0
- package/schemas/report-config-v2.json +5 -0
- package/skills/wowbagger/SKILL.md +242 -59
- package/src/adapter/core-probe.js +3 -4
- package/src/adapter/process-outcome.js +8 -1
- package/src/claim-capabilities.js +3 -3
- package/src/claim-coordinator.js +61 -12
- package/src/claim-journal.js +212 -7
- package/src/claim-prospective.js +1 -28
- package/src/claim-publication.js +412 -78
- package/src/claim-request.js +9 -0
- package/src/claim-store.js +9 -4
- package/src/cli.js +302 -56
- package/src/extensions.js +1 -0
- package/src/git-autocommit.js +106 -43
- package/src/git-reconciliation.js +74 -19
- package/src/git-worktrees.js +73 -0
- package/src/instrumentation.js +1 -0
- package/src/launch.js +2 -2
- package/src/ledger-repair.js +1170 -0
- package/src/mutation.js +73 -15
- package/src/reconciliation-classifier.js +117 -0
- package/src/report-evidence.js +158 -41
- package/src/report-graph.js +201 -73
- package/src/report-html.js +358 -155
- package/src/report-impact.js +106 -0
- package/src/report-selection.js +97 -0
- package/src/report-sequencing.js +4 -4
- package/src/report-svg.js +74 -20
- package/src/report-view.js +14 -1
- package/src/report.js +109 -23
- package/src/version-drift.js +98 -0
- package/src/worktree-identity.js +165 -0
package/README.md
CHANGED
|
@@ -30,16 +30,17 @@ 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:
|
|
34
|
-
>
|
|
35
|
-
>
|
|
36
|
-
> version will move before a stable release.
|
|
33
|
+
> **Status: published and self-hosted.** `0.5.0` is published on npm and has
|
|
34
|
+
> a matching repository tag. It is the version this repository runs its own
|
|
35
|
+
> backlog on. The API is stable at core contract version 5.
|
|
37
36
|
>
|
|
38
|
-
> **
|
|
39
|
-
>
|
|
40
|
-
>
|
|
41
|
-
>
|
|
42
|
-
>
|
|
37
|
+
> **Channels.** A stable release sets both `latest` and `next` to the stable
|
|
38
|
+
> version and publishes with `npm publish --tag latest`. A later prerelease
|
|
39
|
+
> moves only `next` and publishes with `npm publish --tag next`, so `latest`
|
|
40
|
+
> stays on the stable release. While every published release is a prerelease,
|
|
41
|
+
> `latest` mirrors `next`: install the prerelease explicitly with
|
|
42
|
+
> `wowbagger@next`. After the first stable release, a bare install resolves
|
|
43
|
+
> to the stable release.
|
|
43
44
|
>
|
|
44
45
|
> **What is proved.** Contract version **5** validates the complete Markdown
|
|
45
46
|
> ledger, selects a deterministic ready queue, exposes bounded `list` and
|
|
@@ -50,11 +51,12 @@ agent to use those guarantees instead of hand-editing your Markdown.
|
|
|
50
51
|
> verification, and publication finalization coordinate cooperating writers
|
|
51
52
|
> without pretending to be an exclusive dispatch lock.
|
|
52
53
|
>
|
|
53
|
-
> `report` is a self-contained
|
|
54
|
-
>
|
|
55
|
-
>
|
|
56
|
-
>
|
|
57
|
-
>
|
|
54
|
+
> `report` is a self-contained decision workspace: one scoped item browser with
|
|
55
|
+
> **Work next** and other quick views, an area/status matrix, scoped attention
|
|
56
|
+
> actions, dependency impact, interactive Flow charts, and a 3D dependency
|
|
57
|
+
> graph that all share one scope. Version 2 report configurations add named
|
|
58
|
+
> custom views whose statistics, readiness, attention, flow, graph, and impact
|
|
59
|
+
> all describe one filtered subset. Reports remain derived output, not mirrored
|
|
58
60
|
> ledger state.
|
|
59
61
|
>
|
|
60
62
|
> The core ships Claude Code, Codex, and OpenCode adapter packages on one shared
|
|
@@ -77,7 +79,7 @@ Wowbagger is the core authority for a Git-native work ledger. Use it instead
|
|
|
77
79
|
of editing ledger Markdown by hand.
|
|
78
80
|
|
|
79
81
|
```sh
|
|
80
|
-
wowbagger --version # require 0.
|
|
82
|
+
wowbagger --version # require 0.5.0
|
|
81
83
|
wowbagger capabilities --json # require contract_version: 5
|
|
82
84
|
wowbagger validate --ledger ledger --json
|
|
83
85
|
wowbagger ready --ledger ledger --as-of YYYY-MM-DD --json
|
|
@@ -86,10 +88,12 @@ wowbagger inspect --ledger ledger --number N --json
|
|
|
86
88
|
|
|
87
89
|
For a write, inspect immediately before dispatch, send the returned exact-byte
|
|
88
90
|
revision as the compare-and-swap witness, use the explicit core mutation, and
|
|
89
|
-
validate again.
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
91
|
+
validate again. On a provisioned ledger, commit each `create`, `transition`,
|
|
92
|
+
`parent-migrate`, `snooze`, `patch`, or `publish-claimed` mutation before the
|
|
93
|
+
next mutating command. Never replay a lost write: reconnect, re-read current
|
|
94
|
+
state, and treat the outcome as unknown until the core or a human resolves it.
|
|
95
|
+
Numbers are the human-facing item identity; `wb_...` ULIDs are internal
|
|
96
|
+
identities.
|
|
93
97
|
|
|
94
98
|
The core owns validation, ready selection, projections, lifecycle, CAS,
|
|
95
99
|
publication, claims, fencing, and reconciliation. The harness or host owns
|
|
@@ -98,13 +102,15 @@ cooperating writers; they are not exclusive locks.
|
|
|
98
102
|
|
|
99
103
|
## Start here
|
|
100
104
|
|
|
101
|
-
Install the core CLI, then verify it. The
|
|
105
|
+
Install the core CLI, then verify it. The supported runtime is Node.js 24; Node
|
|
106
|
+
26 remains excluded because of the separate Vitest incompatibility:
|
|
102
107
|
|
|
103
108
|
```sh
|
|
104
|
-
npm install -g wowbagger@
|
|
109
|
+
npm install -g wowbagger@latest # stable release
|
|
110
|
+
npm install -g wowbagger@0.5.0 # exact plugin-matched release
|
|
105
111
|
# or, from this release's Git tag:
|
|
106
|
-
# npm install -g github:lstutzman/wowbagger#v0.
|
|
107
|
-
wowbagger --version # 0.
|
|
112
|
+
# npm install -g github:lstutzman/wowbagger#v0.5.0
|
|
113
|
+
wowbagger --version # 0.5.0
|
|
108
114
|
wowbagger capabilities --json # must report contract_version: 5
|
|
109
115
|
```
|
|
110
116
|
|
|
@@ -166,21 +172,45 @@ names — `create` publishes into an existing directory and does not make one.
|
|
|
166
172
|
Nothing is renamed after a create. This repository dogfoods that binding: its
|
|
167
173
|
own items live in [`ledger/items/`](ledger/items/).
|
|
168
174
|
|
|
169
|
-
If your items mirror an external tracker and carry
|
|
170
|
-
declare
|
|
171
|
-
|
|
175
|
+
If your items will mirror an external tracker and carry consumer-owned fields,
|
|
176
|
+
declare those fields **before the first item**. The declaration makes each
|
|
177
|
+
named extension member patchable:
|
|
172
178
|
|
|
173
179
|
```sh
|
|
174
180
|
echo '{"extensions_version":1,"members":{"external_id":"string"}}' \
|
|
175
181
|
> path/to/ledger/.wowbagger/extensions.json
|
|
176
182
|
```
|
|
177
183
|
|
|
178
|
-
Each member declares one value type
|
|
179
|
-
`string-list`.
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
+
Each member declares one value type: `string`, `integer`, `boolean`, or
|
|
185
|
+
`string-list`. A ledger without the file has no patchable extension member,
|
|
186
|
+
and `set.extensions` refuses the missing declaration by name. The declaration
|
|
187
|
+
authorizes writes; it does not define item validity, so `validate` does not
|
|
188
|
+
read it. Commit the declaration with the other ledger setup.
|
|
189
|
+
|
|
190
|
+
If an existing ledger already carries extension values, do not create the
|
|
191
|
+
declaration by hand. Select every member and type explicitly in a request,
|
|
192
|
+
review a dry run, then publish the same proposal:
|
|
193
|
+
|
|
194
|
+
```json
|
|
195
|
+
{"members":{"tags":"string-list"}}
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
```sh
|
|
199
|
+
wowbagger extensions-provision --ledger path/to/ledger \
|
|
200
|
+
--input declaration.json --json --dry-run
|
|
201
|
+
wowbagger extensions-provision --ledger path/to/ledger \
|
|
202
|
+
--input declaration.json --json
|
|
203
|
+
git add path/to/ledger/.wowbagger/extensions.json
|
|
204
|
+
git commit -m "Declare patchable ledger extensions"
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
The command first requires a valid complete ledger. It validates every
|
|
208
|
+
occurrence of each selected member, reports occurrence counts, changes no item
|
|
209
|
+
bytes, and publishes one canonical declaration without overwriting a different
|
|
210
|
+
one. Commit that file before the first corresponding `patch`, inspect the
|
|
211
|
+
target for its current revision, then use `set.extensions.tags`. An item that
|
|
212
|
+
writes the selected member with a YAML anchor or alias remains
|
|
213
|
+
`extension-anchored` and requires a reviewed hand-edit.
|
|
184
214
|
|
|
185
215
|
Cores at `0.1.0-alpha.4` and earlier ignore the layout file and publish every
|
|
186
216
|
item at the ledger root.
|
|
@@ -269,8 +299,9 @@ skill exists because four things break when it does.
|
|
|
269
299
|
- **Validation is whole-ledger and fail-closed.** One malformed item refuses
|
|
270
300
|
every read and every guarded mutation on that ledger, including commands that
|
|
271
301
|
never touch it. A hand-edit finds that out later, and usually in someone
|
|
272
|
-
else's session. `create`, `transition`,
|
|
273
|
-
candidate ledger *before* publishing anything,
|
|
302
|
+
else's session. `create`, `transition`, `parent-migrate`, `snooze`, and
|
|
303
|
+
`patch` validate the complete candidate ledger *before* publishing anything,
|
|
304
|
+
and refuse `unchanged`.
|
|
274
305
|
- **A hand-edit has no lost-update guard.** Every guarded write takes the exact
|
|
275
306
|
SHA-256 revision `inspect` returned and refuses if the bytes moved. An editor
|
|
276
307
|
writes over whatever is there.
|
|
@@ -306,7 +337,7 @@ Four separate things, deliberately:
|
|
|
306
337
|
|
|
307
338
|
The core and the plugin install independently and must carry matching
|
|
308
339
|
distribution versions. The core contract version and the adapter contract
|
|
309
|
-
version are separate domains: the core is at **
|
|
340
|
+
version are separate domains: the core is at **5**, the adapter is at **2**, and
|
|
310
341
|
the legacy work-claim, ledger-publication, and ledger-mutation envelopes stay at
|
|
311
342
|
**1**.
|
|
312
343
|
|
|
@@ -317,12 +348,14 @@ the legacy work-claim, ledger-publication, and ledger-mutation envelopes stay at
|
|
|
317
348
|
Wowbagger ships as an npm package with a single `wowbagger` binary. There are
|
|
318
349
|
two supported install routes:
|
|
319
350
|
|
|
320
|
-
- **npm registry** — `npm install -g wowbagger
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
bytes.
|
|
351
|
+
- **npm registry** — `npm install -g wowbagger` installs the stable release
|
|
352
|
+
once it exists; `npm install -g wowbagger@next` installs the newest
|
|
353
|
+
prerelease. While every published release is a prerelease, `latest` mirrors
|
|
354
|
+
`next`, so a bare install resolves to the same bytes. After the first
|
|
355
|
+
stable release, `latest` stays on stable and only `next` follows later
|
|
356
|
+
prereleases.
|
|
324
357
|
- **git tag** —
|
|
325
|
-
`npm install -g github:lstutzman/wowbagger#v0.
|
|
358
|
+
`npm install -g github:lstutzman/wowbagger#v0.5.0` installs this
|
|
326
359
|
release. Installing at a ref installs the core and every adapter that ref
|
|
327
360
|
carries.
|
|
328
361
|
|
|
@@ -345,8 +378,9 @@ sending the request and reading the refusal — an `unknown-member` issue at
|
|
|
345
378
|
`/set/body_append` means the core predates the append — or pin the distribution
|
|
346
379
|
version.
|
|
347
380
|
|
|
348
|
-
- **Node.js:**
|
|
349
|
-
20
|
|
381
|
+
- **Node.js:** 24 and later. The release gate runs the full suite on Node
|
|
382
|
+
24.20.0 with the strict deprecation gate; Node 26 stays excluded because of
|
|
383
|
+
the separate Vitest incompatibility.
|
|
350
384
|
- **Platforms:** the core runs wherever Node.js runs. The Claude Code adapter
|
|
351
385
|
declares Darwin, Linux, and Windows `supported` from native common-vector
|
|
352
386
|
evidence. Every other shipped adapter target remains `unverified`.
|
|
@@ -358,10 +392,10 @@ version.
|
|
|
358
392
|
|
|
359
393
|
### Security
|
|
360
394
|
|
|
361
|
-
- **Read-only by default.** `validate`, `ready`, `report`, `inspect`,
|
|
362
|
-
`capabilities`, and `mint-id` never modify anything. Every mutation
|
|
363
|
-
(`create`, `transition`, `
|
|
364
|
-
reviewable write.
|
|
395
|
+
- **Read-only by default.** `validate`, `ready`, `report`, `inspect`, `list`,
|
|
396
|
+
`capabilities`, and `mint-id` never modify anything. Every item mutation
|
|
397
|
+
(`create`, `transition`, `parent-migrate`, `snooze`, `patch`, and
|
|
398
|
+
`publish-claimed`) is an explicit, reviewable write.
|
|
365
399
|
- **Lock is not a claim.** A short mutation lock serializes writers during one
|
|
366
400
|
operation. It does not grant a work claim.
|
|
367
401
|
- **Claims are merge-coordinated, not exclusive.** `claim acquire` uses
|
|
@@ -393,8 +427,8 @@ wowbagger core, this is how you move forward safely.
|
|
|
393
427
|
Upgrade the pieces you installed:
|
|
394
428
|
|
|
395
429
|
```sh
|
|
396
|
-
npm install -g wowbagger@
|
|
397
|
-
npm install -g github:lstutzman/wowbagger#v0.
|
|
430
|
+
npm install -g wowbagger@latest # public npm registry
|
|
431
|
+
npm install -g github:lstutzman/wowbagger#v0.5.0 # immutable Git release
|
|
398
432
|
git pull && npm ci # or: a direct checkout
|
|
399
433
|
```
|
|
400
434
|
|
|
@@ -417,7 +451,7 @@ distribution versions equal.
|
|
|
417
451
|
|
|
418
452
|
The shipped adapter selects only adapter contract version 2 and requires core
|
|
419
453
|
contract version 5. The adapter contract and the core contract are separate
|
|
420
|
-
version domains: the adapter stays at 2 while the core
|
|
454
|
+
version domains: the adapter stays at 2 while the core is at 5. A v1-only
|
|
421
455
|
consumer receives `unsupported-adapter-contract-version`; it does not receive v2
|
|
422
456
|
behavior. The schema-2 transport is available. Ledger migration remains a
|
|
423
457
|
separate quiesced maintenance operation. The
|
|
@@ -442,6 +476,16 @@ core, these are the changes most likely to touch you:
|
|
|
442
476
|
`create` and refuses a caller-supplied one; `patch` refuses it because it is
|
|
443
477
|
immutable identity. Keep a legacy identifier in a declared extension member or
|
|
444
478
|
in the item body.
|
|
479
|
+
|
|
480
|
+
- **Repair duplicate numbers through `ledger-repair` version 1.** Generate a
|
|
481
|
+
read-only proposal with `number-repair-proposal`, review every
|
|
482
|
+
`expected_revision` and `replacement_number`, then apply the complete mapping
|
|
483
|
+
with `number-repair`. The command preserves ULID identities and relations and
|
|
484
|
+
does not change core contract version 5.
|
|
485
|
+
|
|
486
|
+
- **Run `version-drift --json` before mutation.** It compares the installed
|
|
487
|
+
skill pin, required core contract, and running core, and names the stale
|
|
488
|
+
package, plugin cache, or linked checkout with remediation.
|
|
445
489
|
- **Delete your local ULID generator.** `wowbagger mint-id --json` prints a
|
|
446
490
|
canonical ID; `--date YYYY-MM-DD` selects the creation date the ID must
|
|
447
491
|
encode.
|
|
@@ -552,7 +596,8 @@ approval never rides the bootstrap request, which the model controls;
|
|
|
552
596
|
|
|
553
597
|
## Core commands
|
|
554
598
|
|
|
555
|
-
The current core requires Node.js
|
|
599
|
+
The current core requires Node.js 24. Node 26 is not in the supported matrix.
|
|
600
|
+
From a Wowbagger checkout,
|
|
556
601
|
`./bin/wowbagger.js --help` prints the full command inventory,
|
|
557
602
|
`./bin/wowbagger.js <command> --help` prints that command's usage, and
|
|
558
603
|
`./bin/wowbagger.js --version` prints the installed package version. The
|
|
@@ -565,21 +610,28 @@ npm ci
|
|
|
565
610
|
./bin/wowbagger.js ready --ledger path/to/ledger --as-of 2030-01-15
|
|
566
611
|
./bin/wowbagger.js report --ledger path/to/ledger --as-of 2030-01-15 --json
|
|
567
612
|
./bin/wowbagger.js capabilities --json
|
|
568
|
-
./bin/wowbagger.js mint-id --json
|
|
569
613
|
./bin/wowbagger.js inspect --ledger path/to/ledger --id wb_... --json
|
|
570
614
|
./bin/wowbagger.js inspect --ledger path/to/ledger --number 30 --json
|
|
615
|
+
./bin/wowbagger.js list --ledger path/to/ledger --input query.json --json
|
|
571
616
|
./bin/wowbagger.js create --ledger path/to/ledger --input request.json --json
|
|
572
617
|
./bin/wowbagger.js transition --ledger path/to/ledger --input request.json --json
|
|
618
|
+
./bin/wowbagger.js parent-migrate --ledger path/to/ledger --input request.json --json
|
|
619
|
+
./bin/wowbagger.js snooze --ledger path/to/ledger --input request.json --json
|
|
573
620
|
./bin/wowbagger.js patch --ledger path/to/ledger --input request.json --json
|
|
621
|
+
./bin/wowbagger.js extensions-provision --ledger path/to/ledger --input declaration.json --json
|
|
622
|
+
./bin/wowbagger.js mint-id --json
|
|
623
|
+
./bin/wowbagger.js publish-claimed --ledger path/to/ledger --input request.json --json
|
|
624
|
+
./bin/wowbagger.js claim-merge-verify --ledger path/to/ledger --base main --head feature --json
|
|
625
|
+
./bin/wowbagger.js claim-sync --ledger path/to/ledger --json
|
|
626
|
+
./bin/wowbagger.js claim-adopt --ledger path/to/ledger --input request.json --json
|
|
627
|
+
./bin/wowbagger.js mutation-finalize --ledger path/to/ledger --recovery-token token --json
|
|
574
628
|
./bin/wowbagger.js provision --ledger path/to/ledger --json
|
|
575
629
|
./bin/wowbagger.js claim capabilities --ledger path/to/ledger --json
|
|
576
630
|
./bin/wowbagger.js claim acquire --ledger path/to/ledger --input request.json --json
|
|
577
631
|
./bin/wowbagger.js claim read --ledger path/to/ledger --input request.json --json
|
|
578
632
|
./bin/wowbagger.js claim renew --ledger path/to/ledger --input request.json --json
|
|
579
633
|
./bin/wowbagger.js claim release --ledger path/to/ledger --input request.json --json
|
|
580
|
-
./bin/wowbagger.js
|
|
581
|
-
./bin/wowbagger.js claim-verify --ledger path/to/ledger --json
|
|
582
|
-
./bin/wowbagger.js claim-adopt --ledger path/to/ledger --input request.json --json
|
|
634
|
+
./bin/wowbagger.js claim-verify --ledger path/to/ledger [--id wb_...] --json
|
|
583
635
|
```
|
|
584
636
|
|
|
585
637
|
`validate` writes exactly one JSON result to standard output. A valid ledger
|
|
@@ -642,10 +694,12 @@ change is an addition. And **extension members are patchable only where the
|
|
|
642
694
|
ledger declares them** — see [Set the ledger up before the first
|
|
643
695
|
item](#set-the-ledger-up-before-the-first-item).
|
|
644
696
|
|
|
645
|
-
Which members you own at all is a
|
|
646
|
-
consumer-editable through `patch`,
|
|
647
|
-
|
|
648
|
-
|
|
697
|
+
Which members you own at all is a four-way split — core-owned,
|
|
698
|
+
consumer-editable through `patch`, mutable through a dedicated command, and
|
|
699
|
+
create-once — stated member by member in the mutation contract's
|
|
700
|
+
**frontmatter ownership** table. Use `parent-migrate` to repoint an existing
|
|
701
|
+
item to or from an epic, and `snooze` to set or clear `snoozed_until`. Read the
|
|
702
|
+
table; do not send a patch and interpret the refusal.
|
|
649
703
|
|
|
650
704
|
### An epic's progress is derived, never stored
|
|
651
705
|
|
|
@@ -670,9 +724,11 @@ command asks you to parse the Markdown by hand:
|
|
|
670
724
|
refusal carries `error.details.item`, the complete snapshot of the item you
|
|
671
725
|
asked for, whenever no validation error names that item's path. A faulted
|
|
672
726
|
item is withheld; `validate` already names its repair.
|
|
673
|
-
- `claim-verify --json` reports `result.ledger_validation`.
|
|
674
|
-
|
|
675
|
-
|
|
727
|
+
- `claim-verify --json` reports `result.ledger_validation`. Bare verification
|
|
728
|
+
is strict repository-wide mode; `--id <item>` keeps all findings visible but
|
|
729
|
+
fails only for that item and global barriers. Exit 0 with an invalid
|
|
730
|
+
`ledger_validation` still means claim state is clean but validation blocks
|
|
731
|
+
mutation.
|
|
676
732
|
|
|
677
733
|
### Work claims
|
|
678
734
|
|
|
@@ -702,22 +758,29 @@ operating rule:
|
|
|
702
758
|
|
|
703
759
|
**Commit each mutation to Git before running the next mutating command.**
|
|
704
760
|
|
|
705
|
-
The durable claim store
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
`
|
|
761
|
+
The durable claim store reconciles every recorded mutation with Git `HEAD` and
|
|
762
|
+
the working tree. The next mutation refuses when that reconciliation finds an
|
|
763
|
+
`unauthorized-revision`, requires Git finalization, or requires synchronization
|
|
764
|
+
for the item the command targets. A synchronization finding on an unrelated
|
|
765
|
+
item remains visible to `claim-verify` but does not block the command.
|
|
766
|
+
|
|
767
|
+
An existing item's latest authorized working-tree bytes and an earlier
|
|
768
|
+
authorized revision at `HEAD` form an authorized predecessor/successor window.
|
|
769
|
+
That window produces no finding, so another mutation can run before the first
|
|
770
|
+
one is committed. Acceptance of the later mutation does not make either change
|
|
771
|
+
durable. Commit each mutation anyway, then run `claim-verify`.
|
|
710
772
|
|
|
711
773
|
The loop that works:
|
|
712
774
|
|
|
713
775
|
```sh
|
|
714
776
|
./bin/wowbagger.js create --ledger path/to/ledger --input request.json --json
|
|
715
777
|
git add path/to/ledger && git commit -m "Record the mutation"
|
|
716
|
-
./bin/wowbagger.js claim-verify --ledger path/to/ledger --json
|
|
778
|
+
./bin/wowbagger.js claim-verify --ledger path/to/ledger --id wb_... --json
|
|
717
779
|
./bin/wowbagger.js transition --ledger path/to/ledger --input next.json --json
|
|
718
780
|
```
|
|
719
781
|
|
|
720
|
-
|
|
782
|
+
For example, an authorized new item that is still absent from `HEAD` makes the
|
|
783
|
+
next command return exit 6:
|
|
721
784
|
|
|
722
785
|
```json
|
|
723
786
|
{"ok":false,"namespace":"ledger-mutation","command":"create-v1","contract_version":1,
|
|
@@ -732,10 +795,16 @@ Skip the commit and the next command returns exit 6:
|
|
|
732
795
|
`state: "unchanged"` is exact — nothing was written. **`claim-verify` is the
|
|
733
796
|
reconciliation procedure.** Read `details.findings`, do what each
|
|
734
797
|
`remediation` string says, run `claim-verify` until it returns exit 0, then
|
|
735
|
-
repeat the refused command.
|
|
798
|
+
repeat the refused command. A `worktree-synchronization-required` finding on an
|
|
799
|
+
unrelated item does not block the requested mutation. The same finding on the
|
|
800
|
+
target item, and every `unauthorized-revision` finding, remains blocking.
|
|
736
801
|
|
|
737
|
-
Batch work is where this bites. Filing ten items means ten
|
|
738
|
-
commit
|
|
802
|
+
Batch work is where this bites. Filing ten items means ten serial
|
|
803
|
+
`create --auto-commit` calls and ten commits, not one batch commit. Wowbagger
|
|
804
|
+
permanently rejects batch create for the direct-Markdown architecture:
|
|
805
|
+
`limits.multi_item_atomicity` remains `false`, request order is the supported
|
|
806
|
+
bulk order, and each create must finish or recover before the next begins. See
|
|
807
|
+
the [batch-create decision](docs/design/2026-08-30-batch-create.md).
|
|
739
808
|
|
|
740
809
|
### Or fold the commit into the mutation
|
|
741
810
|
|
|
@@ -748,16 +817,23 @@ ledger only:
|
|
|
748
817
|
|
|
749
818
|
It is opt-in per invocation. There is no configuration setting or environment
|
|
750
819
|
default, because a hidden default would make existing automation create Git
|
|
751
|
-
commits unexpectedly. The flag is accepted on `create`, `transition`,
|
|
752
|
-
and `publish-claimed`.
|
|
820
|
+
commits unexpectedly. The flag is accepted on `create`, `transition`,
|
|
821
|
+
`parent-migrate`, `snooze`, `patch`, and `publish-claimed`.
|
|
753
822
|
|
|
754
823
|
What one flagged invocation does: refuse if anything is staged anywhere or any
|
|
755
|
-
path under the ledger is dirty; reconcile; run the mutation unchanged;
|
|
756
|
-
exactly the changed item and at most one
|
|
824
|
+
foreign path under the ledger is dirty; reconcile; run the mutation unchanged;
|
|
825
|
+
commit exactly the changed item and at most one
|
|
757
826
|
`.wowbagger/reconcile-<namespace>.md` with a fixed subject such as
|
|
758
827
|
`wowbagger: transition item #7`; verify the commit; then run `claim-verify`
|
|
759
|
-
before it answers.
|
|
760
|
-
`
|
|
828
|
+
before it answers. A command that owns the claim journal may rebuild only its
|
|
829
|
+
derived reconciliation log during preflight. `create` remains strict, and
|
|
830
|
+
every other dirty ledger path still refuses. On success the result gains
|
|
831
|
+
`git_commit`, `commit_paths`, and `claim_verified`.
|
|
832
|
+
|
|
833
|
+
If claim verification refuses, auto-commit preserves its code and reason in
|
|
834
|
+
`claim_verify_code` and `claim_verify_reason`. Only
|
|
835
|
+
`claim_verify_reason: "claim-store-locked"` is retryable; unresolved
|
|
836
|
+
reconciliation is not.
|
|
761
837
|
|
|
762
838
|
Unstaged and untracked files **outside** the ledger are left alone. Hooks and
|
|
763
839
|
signing are honoured; `--no-verify` is never passed. Nothing is pushed.
|
|
@@ -850,62 +926,108 @@ relative `--out` overrides resolve from the caller's working directory.
|
|
|
850
926
|
"complexity": "/complexity",
|
|
851
927
|
"rank": "/priority_rank",
|
|
852
928
|
"class": "/class",
|
|
853
|
-
"due": "/due"
|
|
929
|
+
"due": "/due",
|
|
930
|
+
"tags": "/tags"
|
|
854
931
|
},
|
|
855
932
|
"swarm": { "eligible_complexities": ["small", "medium"] }
|
|
856
933
|
}
|
|
857
934
|
```
|
|
858
|
-
|
|
859
935
|
`repository.logo`, `fields`, and `swarm` are optional. Field values resolve
|
|
860
936
|
from parsed frontmatter with RFC 6901 JSON Pointers. A swarm requires mapped
|
|
861
937
|
`area` and `complexity` fields. The report fetches nothing at view time.
|
|
862
938
|
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
939
|
+
`tags` is the one multi-value mapped field. It accepts a nonempty string or an
|
|
940
|
+
array holding only nonempty strings: a scalar reads as a one-tag set, exact
|
|
941
|
+
duplicates collapse, and values sort deterministically. The mapping never
|
|
942
|
+
splits commas, lowercases values, coerces objects, or partially accepts a
|
|
943
|
+
mixed-type array. An empty array counts as missing; any other rejected value
|
|
944
|
+
is omitted from the item and counted as invalid metadata. `area` stays scalar.
|
|
945
|
+
The model carries `fieldCoverage`, one entry per configured field plus `area`
|
|
946
|
+
and `tags` when unmapped, ordered by field name with `present`, `missing`, and
|
|
947
|
+
`invalid` counts over the retained report population. An unmapped field counts
|
|
948
|
+
every retained item as missing, and a visible missing-mapping notice tells an
|
|
949
|
+
unconfigured mapping apart from missing item values. Missing metadata never
|
|
950
|
+
matches a filter value: there is no `Unclassified` bucket, so a filter for a
|
|
951
|
+
literal `Unclassified` tag matches only items really carrying that tag.
|
|
952
|
+
|
|
953
|
+
The report is a **decision-focused workspace**, not a state snapshot. It has
|
|
954
|
+
three sections behind accessible view navigation: **Items** (the default),
|
|
955
|
+
**Flow**, and **Dependencies**. Only the selected section is visible when
|
|
956
|
+
scripting runs; without scripting, every section stays readable through its
|
|
957
|
+
anchor and native `details` elements, and the artifact states its fixed scope.
|
|
958
|
+
|
|
959
|
+
Items opens with the state counts, then a sticky control strip: search, five
|
|
960
|
+
quick views (**Work next**, **In progress**, **Blocked**, **Needs triage**, and
|
|
961
|
+
**All open**), and the display controls (grouping, sorting, Basic/Standard/
|
|
962
|
+
Detailed, Show history, Expand all, Collapse all). Below 1100px the display
|
|
963
|
+
controls fold behind a **Display** toggle so search and quick views stay in
|
|
964
|
+
reach. Search and the facet groups form the **scope**; the scope narrows the
|
|
965
|
+
summaries, Flow, and Dependencies alike. Quick views and Show history change
|
|
966
|
+
only the list. Work next keeps its recommended order and prints the reasons
|
|
967
|
+
beside each row; any other sort presents itself as that sort.
|
|
968
|
+
|
|
969
|
+
There is one list and one canonical detail per retained item. Desktop widths
|
|
970
|
+
use a list/detail split; narrower widths show the selected detail inline.
|
|
971
|
+
Opening a detail never clears the search, the facets, or the list position, and
|
|
972
|
+
a detail opened from Flow or Dependencies returns to Items with the scope
|
|
973
|
+
intact. Expand all acts on visible rows only.
|
|
974
|
+
|
|
975
|
+
The **filters** are facet groups: Readiness, Status, Kind, Priority, and one
|
|
976
|
+
group for every configured mapped field, each a fieldset of checkbox chips
|
|
977
|
+
behind a collapsed **Filters** control. Values inside a group are alternatives
|
|
978
|
+
and groups narrow each other; the search box is one more condition on the same
|
|
979
|
+
answer. Every chip carries the count it would leave, measured against the
|
|
980
|
+
search and the other groups but never against its own. Missing metadata is its
|
|
981
|
+
own **Missing** chip, distinct from a literal `Unclassified` value. The result
|
|
982
|
+
count states how much of the retained set is showing, and **Clear filters**
|
|
983
|
+
gives every selection back.
|
|
984
|
+
|
|
985
|
+
Above the list sit scoped summaries that open exact contributing items through
|
|
986
|
+
a labelled drilldown pill: attention actions (in progress, blocked, needs
|
|
987
|
+
triage, oldest, and started work past this ledger's own 85th-percentile cycle
|
|
988
|
+
time), an **area/status matrix** with count and blocked count per cell, and
|
|
989
|
+
**Scoped members of existing batches**, which intersects the area-diverse
|
|
990
|
+
batches with the scoped ready set and omits empty batches. Each item detail
|
|
991
|
+
states its **downstream reach** (transitive dependents in the report) and,
|
|
992
|
+
separately, the items that become **ready if done**; neither alters core
|
|
993
|
+
readiness or the recommended order.
|
|
994
|
+
|
|
995
|
+
### Flow
|
|
996
|
+
|
|
997
|
+
Flow recomputes from the scoped open and terminal population in the browser:
|
|
998
|
+
cumulative flow, weekly arrivals against closures with done counted
|
|
999
|
+
separately, throughput with a four-week mean, current aging by status,
|
|
1000
|
+
acceptance-to-completion samples, and the closure forecast. Inclusive **From**
|
|
1001
|
+
and **To** controls default to the twelve-week window; a start after the end,
|
|
1002
|
+
or an end after the report date, is refused with a visible error while the last
|
|
1003
|
+
valid charts stay. Weekly buckets, aging cells, completion samples, and
|
|
1004
|
+
cumulative date/band selections each drill into the exact contributing items,
|
|
1005
|
+
and the accessible tables offer the same actions. The forecast is computed only
|
|
1006
|
+
when Flow first opens and cached by cohort and range. Missing acceptance history
|
|
1007
|
+
is stated as reconstruction uncertainty; an item killed straight from triage is
|
|
1008
|
+
complete history, not a gap. The fixed server-rendered charts remain for
|
|
1009
|
+
readers without scripting, each with `role="img"` and an aria-label that states
|
|
1010
|
+
its finding in words.
|
|
890
1011
|
|
|
891
1012
|
### The ledger graph
|
|
892
1013
|
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
nodes, every link that touched one of them, and their labels
|
|
905
|
-
reheats the layout in place
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
1014
|
+
Dependencies draws the scoped items as a force-directed 3D graph. Every item is
|
|
1015
|
+
a node, labelled `#N`, coloured by readiness for open items and by terminal
|
|
1016
|
+
status for closed ones, and sized by the same transitive unblocking leverage the
|
|
1017
|
+
recommended order uses. Edges run from a prerequisite or a parent to the item
|
|
1018
|
+
it releases: a `depends_on` edge is straight and arrowed, a `parent` edge is
|
|
1019
|
+
curved and unarrowed. Hovering a node opens a card with its number, title,
|
|
1020
|
+
status, age, leverage, and reasons; clicking it, or a roster row, opens the
|
|
1021
|
+
canonical detail in Items. Downstream and ready-if-done actions on the roster
|
|
1022
|
+
drill into the same sets the detail names.
|
|
1023
|
+
|
|
1024
|
+
The graph has no filter of its own: it follows the shared scope, so a scope
|
|
1025
|
+
change drops nodes, every link that touched one of them, and their labels
|
|
1026
|
+
together, then reheats the layout in place. A hidden blocker never turns a
|
|
1027
|
+
retained item ready, because readiness is projected over the complete ledger
|
|
1028
|
+
before any view narrows it. The graph starts only when Dependencies first
|
|
1029
|
+
opens, pauses while another section is shown, and an empty scope draws an
|
|
1030
|
+
empty graph that says so.
|
|
909
1031
|
|
|
910
1032
|
The renderer is [`3d-force-graph`](https://github.com/vasturiano/3d-force-graph)
|
|
911
1033
|
over Three.js, vendored into `vendor/3d-force-graph/` at a pinned version
|
|
@@ -948,9 +1070,10 @@ weight and is shown as written.
|
|
|
948
1070
|
### Named custom report views
|
|
949
1071
|
|
|
950
1072
|
A named custom view is a second self-contained report generated from the same
|
|
951
|
-
complete ledger. Every section of it — statistics, **Work next
|
|
952
|
-
|
|
953
|
-
the
|
|
1073
|
+
complete ledger. Every section of it — statistics, **Work next** and the other
|
|
1074
|
+
quick views, the **Attention** summaries and area/status matrix, dependency
|
|
1075
|
+
impact, Flow, the graph, the drill-down pill, terminal history, and the
|
|
1076
|
+
swarm batches — describes one configured subset, so the file is honest to
|
|
954
1077
|
share as a scoped report. Excluded items are absent from the bytes rather than
|
|
955
1078
|
hidden by a stylesheet. The base report stays available and unchanged.
|
|
956
1079
|
|
|
@@ -1015,9 +1138,10 @@ A `fields` key must also be a configured report field. Each field filter is a
|
|
|
1015
1138
|
non-empty array of unique JSON strings, finite numbers, or booleans, and matching
|
|
1016
1139
|
preserves JSON scalar type and value: stringification is not equality, so a
|
|
1017
1140
|
mapped `2` does not answer a filter for `"2"`. An item carrying no mapped value
|
|
1018
|
-
for a field matches no value selected for that field.
|
|
1019
|
-
|
|
1020
|
-
|
|
1141
|
+
for a field matches no value selected for that field. A `tags` filter uses
|
|
1142
|
+
any-member matching, so one item carrying two tags answers either tag. No
|
|
1143
|
+
title-text inference, regular expression, arbitrary JSON pointer, or body
|
|
1144
|
+
search belongs in a view filter.
|
|
1021
1145
|
|
|
1022
1146
|
Wowbagger validates the complete ledger and computes readiness against the
|
|
1023
1147
|
complete ledger before it filters, so excluding a blocker never makes blocked
|
|
@@ -1081,6 +1205,16 @@ current UTC date:
|
|
|
1081
1205
|
npm run report -- --as-of YYYY-MM-DD
|
|
1082
1206
|
```
|
|
1083
1207
|
|
|
1208
|
+
If you verify the report in a browser from a checkout, generate a deterministic
|
|
1209
|
+
synthetic report through the real pipeline:
|
|
1210
|
+
|
|
1211
|
+
```sh
|
|
1212
|
+
node scripts/report-design-demo.js --out /private/tmp/wowbagger-report-demo.html --items 40
|
|
1213
|
+
```
|
|
1214
|
+
|
|
1215
|
+
That output is synthetic and checkout-only: it describes fixed demo data,
|
|
1216
|
+
never this repository's ledger.
|
|
1217
|
+
|
|
1084
1218
|
## Where the contracts live
|
|
1085
1219
|
|
|
1086
1220
|
The README is the map. These are the territory, and they are normative where
|
|
@@ -1143,18 +1277,20 @@ the finding as a ledger item rather than leaving it in a transcript.
|
|
|
1143
1277
|
|
|
1144
1278
|
### The verification gate
|
|
1145
1279
|
|
|
1146
|
-
Four commands. All four must pass, and the test commands run on **
|
|
1147
|
-
current Node runtime and Node 20:
|
|
1280
|
+
Four commands. All four must pass, and the test commands run on **Node 24.20.0**:
|
|
1148
1281
|
|
|
1149
1282
|
```sh
|
|
1150
|
-
TMPDIR=/tmp node --test test/*.test.js
|
|
1151
|
-
TMPDIR=/tmp /opt/homebrew/opt/node@
|
|
1152
|
-
TMPDIR=/tmp node spec/run-adapter-implementation.js
|
|
1153
|
-
node bin/wowbagger.js validate --ledger ledger --json
|
|
1283
|
+
TMPDIR=/tmp /opt/homebrew/opt/node@24/bin/node --test test/*.test.js
|
|
1284
|
+
TMPDIR=/tmp /opt/homebrew/opt/node@24/bin/node --pending-deprecation --throw-deprecation --test test/*.test.js
|
|
1285
|
+
TMPDIR=/tmp /opt/homebrew/opt/node@24/bin/node spec/run-adapter-implementation.js
|
|
1286
|
+
TMPDIR=/tmp /opt/homebrew/opt/node@24/bin/node bin/wowbagger.js validate --ledger ledger --json
|
|
1154
1287
|
```
|
|
1155
1288
|
|
|
1156
1289
|
`TMPDIR=/tmp` is not optional: the default macOS temporary path makes the claim
|
|
1157
|
-
lock socket path too long.
|
|
1290
|
+
lock socket path too long. Use an explicit Node 24.20.0 binary path.
|
|
1291
|
+
|
|
1292
|
+
The supported runtime matrix is Node 24.20.0. Node 26 remains excluded until
|
|
1293
|
+
the separate Vitest incompatibility reported by Lee is resolved.
|
|
1158
1294
|
|
|
1159
1295
|
`npm test`, `npm audit --omit=dev`, and `git diff --check` are useful alongside
|
|
1160
1296
|
it; they are not a substitute for the four commands above.
|
|
@@ -1258,7 +1394,7 @@ It is the durable work ledger beneath those systems.
|
|
|
1258
1394
|
**Shipped: the policy-input contract and the report's mapped fields keep
|
|
1259
1395
|
consumer vocabulary out of the schema.**
|
|
1260
1396
|
- Stabilize the machine-readable command contract and compatibility evidence.
|
|
1261
|
-
**
|
|
1397
|
+
**Shipped: core contract version 5 is the stable contract.**
|
|
1262
1398
|
- Ship Claude Code and Codex adapters. **Claude Code, Codex, and OpenCode
|
|
1263
1399
|
packages share the version 2 engine; the Claude Code manifest declares Darwin
|
|
1264
1400
|
`supported` after passing all 212 native assertions. Other adapter targets and
|