wowbagger 0.1.0-alpha.13 → 0.1.0-alpha.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +200 -0
- package/README.md +8 -8
- package/docs/mutation-contract.md +104 -20
- package/docs/work-claim-contract.md +291 -64
- package/package.json +1 -1
- package/skills/wowbagger/SKILL.md +116 -21
- package/src/claim-coordinator.js +17 -3
- package/src/claim-journal.js +44 -4
- package/src/claim-publication.js +122 -93
- package/src/cli.js +5 -7
- package/src/git-autocommit.js +26 -21
- package/src/mutation.js +15 -2
- package/src/reconciliation-classifier.js +117 -0
|
@@ -334,57 +334,186 @@ The plain statement: **a recorded private write in one worktree blocks
|
|
|
334
334
|
mutations targeting that item, not unrelated item mutations in sibling
|
|
335
335
|
worktrees.** Own uncommitted work and out-of-protocol revisions remain global
|
|
336
336
|
safety barriers. This is item-scoped availability, not exclusive dispatch.
|
|
337
|
+
`create` reads that same reconciliation with one extra barrier, because it is
|
|
338
|
+
the one mutation whose output depends on an identity no caller supplied.
|
|
339
|
+
|
|
340
|
+
**Superseded: create no longer stays journal-silent.** Through alpha.13 this
|
|
341
|
+
contract decided that create records nothing, and three reasons held that
|
|
342
|
+
decision: create already protects its own instant, because publication is
|
|
343
|
+
atomic, refuses to clobber an existing path, and verifies the published bytes
|
|
344
|
+
exactly; a journaled create would serialize every worktree on the
|
|
345
|
+
highest-volume mutation; and the remaining exposure window was judged narrow
|
|
346
|
+
because it closed at the item's first journal-visible mutation.
|
|
347
|
+
|
|
348
|
+
Item #181 overturns that decision. The first reason answers the wrong
|
|
349
|
+
question. Create's atomic instant protects the path it publishes to, and
|
|
350
|
+
nothing protected the `number` it derives. A schema-version-2 create takes
|
|
351
|
+
`1 + max(existing numbers)` from the items its own checkout holds, so two
|
|
352
|
+
worktrees that have not integrated each other's commits derive the same number
|
|
353
|
+
and both publish. They need not overlap in time: a create today and a create
|
|
354
|
+
tomorrow collide as reliably as two simultaneous ones, because the loser is a
|
|
355
|
+
stale checkout rather than a lost race, and a wider mutex cannot fix a
|
|
356
|
+
sequential failure. Only durable evidence can. `number` is immutable and
|
|
357
|
+
`patch` correctly rejects it, so an undetected collision surfaces at
|
|
358
|
+
integration as a global `duplicate-number` validation failure that stops the
|
|
359
|
+
whole ledger.
|
|
360
|
+
|
|
361
|
+
**The fixed ordering.** On a provisioned ledger a create is a legacy mutation
|
|
362
|
+
like any other, and the shared namespace lock is held across every step:
|
|
363
|
+
|
|
364
|
+
1. Acquire the shared namespace lock.
|
|
365
|
+
2. Reconcile the repository state and apply the allocation fence below.
|
|
366
|
+
3. Load this worktree's ledger.
|
|
367
|
+
4. Acquire the item, relation, and number-index lock closure.
|
|
368
|
+
5. Reload and validate the ledger under those locks.
|
|
369
|
+
6. Derive `1 + max(existing numbers)`.
|
|
370
|
+
7. Serialize the candidate item.
|
|
371
|
+
8. Validate the complete candidate ledger.
|
|
372
|
+
9. Reserve journal capacity, then append the create intent.
|
|
373
|
+
10. Publish with atomic no-clobber semantics.
|
|
374
|
+
11. Verify the final path holds the exact candidate bytes.
|
|
375
|
+
12. Append the committed or aborted terminal.
|
|
376
|
+
|
|
377
|
+
A create appends its `legacy-mutation-intent` with `command: "create-v1"`
|
|
378
|
+
before any byte reaches the item path, and it appends that intent only after
|
|
379
|
+
step 8, so a refusal the command would have returned anyway records no
|
|
380
|
+
attempt. The intent carries `expected_revision: null`, which is valid only for
|
|
381
|
+
`create-v1`; every other command still requires a string revision. The
|
|
382
|
+
committed terminal is `legacy-mutation` with `command: "create-v1"`. The
|
|
383
|
+
create abort carries `command: "create-v1"` and `observed_revision: null`,
|
|
384
|
+
while an abort for any other command remains valid without `command` and still
|
|
385
|
+
requires a string revision. No entry records the assigned number: the
|
|
386
|
+
candidate revision binds the complete item bytes, and the ledger stays the one
|
|
387
|
+
number authority.
|
|
388
|
+
|
|
389
|
+
**The allocation fence.** Create reconciles with its own item as the target,
|
|
390
|
+
exactly like every other mutation, and one extra barrier reads the result.
|
|
391
|
+
Every global finding blocks create because it blocks every write:
|
|
392
|
+
`git-finalization-required` for this worktree's own uncommitted authorized
|
|
393
|
+
bytes, `unauthorized-revision` for out-of-protocol bytes, and an unresolved
|
|
394
|
+
`legacy-mutation-outcome-unknown`. On top of those, any coordinated item this
|
|
395
|
+
checkout does not hold blocks `create`, because the journal records a
|
|
396
|
+
committed terminal for an item whose number this working ledger cannot read,
|
|
397
|
+
so the next number derived here may be one a sibling already published. A
|
|
398
|
+
stale revision of an item this checkout holds does not block `create`: a
|
|
399
|
+
number is immutable, so the local maximum is already correct, and that finding
|
|
400
|
+
keeps its ordinary target scope.
|
|
401
|
+
|
|
402
|
+
**The old exposure window is closed on a provisioned ledger.** The journal used
|
|
403
|
+
to learn of an item only at its first `transition` or `patch`, so an
|
|
404
|
+
out-of-protocol overwrite before that mutation went unreported. A journaled
|
|
405
|
+
create records an authorized revision at the item's birth, so the ordinary
|
|
406
|
+
surfaces cover the item from then on: an out-of-protocol overwrite reports
|
|
407
|
+
`unauthorized-revision` and the next mutation refuses. An unprovisioned or
|
|
408
|
+
non-Git ledger keeps only its local number-index lock and its atomic
|
|
409
|
+
no-clobber publication; nothing there coordinates across checkouts, and nothing
|
|
410
|
+
here defends against an actor that bypasses this tool.
|
|
411
|
+
|
|
412
|
+
**The refusal.** A fenced create refuses before it allocates a number and
|
|
413
|
+
before it writes a byte:
|
|
414
|
+
|
|
415
|
+
```text
|
|
416
|
+
exit: 6
|
|
417
|
+
ok: false
|
|
418
|
+
namespace: "ledger-mutation"
|
|
419
|
+
command: "create-v1"
|
|
420
|
+
contract_version: 1
|
|
421
|
+
state: "unchanged"
|
|
422
|
+
error.code: "claim-store-unavailable"
|
|
423
|
+
error.details.reason: "publication-reconciliation-required"
|
|
424
|
+
```
|
|
337
425
|
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
**
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
426
|
+
`error.details.findings` names the item whose authorized create this checkout
|
|
427
|
+
cannot see, and each finding's own `remediation` string stays authoritative:
|
|
428
|
+
commit this worktree's bytes for `git-finalization-required`; synchronize the
|
|
429
|
+
named sibling revision when `owner_ref` names a live worktree; inspect the
|
|
430
|
+
reachable or dangling history and restore or explicitly adopt reviewed bytes
|
|
431
|
+
for a locally absent or reachable-unowned revision; and restore or adopt for
|
|
432
|
+
unauthorized bytes. The reproduced stale-sibling case is locally absent on both
|
|
433
|
+
surfaces, so it renders the cannot-be-established sentence, which is an
|
|
434
|
+
instruction to inspect and integrate, never an instruction to wait. Once the
|
|
435
|
+
ledger validates and `claim-verify` exits 0, retry the same request ID: the
|
|
436
|
+
refusal published no byte, so the retry is an ordinary no-clobber create and
|
|
437
|
+
takes the next number. The fence does not trap recovery — `claim-verify` stays
|
|
438
|
+
read-only, `claim-adopt` keeps its explicit override, claim release stays
|
|
439
|
+
available under barriers, and Git synchronization was never this tool's to
|
|
440
|
+
block.
|
|
441
|
+
|
|
442
|
+
**Recovery from an interrupted create.** An intent with no terminal resolves on
|
|
443
|
+
the next invocation from the item path alone: the candidate revision present
|
|
444
|
+
appends the committed terminal, the path absent appends the create abort, and
|
|
445
|
+
any third revision reports `legacy-mutation-outcome-unknown` as a global
|
|
446
|
+
barrier. Recovery never guesses a number and never publishes an item. Because
|
|
447
|
+
capacity for the intent, one clock entry, and one terminal is reserved before
|
|
448
|
+
the intent is appended, an exhausted journal refuses unchanged before
|
|
449
|
+
publication rather than stranding an unresolvable attempt.
|
|
450
|
+
|
|
451
|
+
**Create owns its item and its reconciliation log.** A successful create is
|
|
452
|
+
journal-visible from the item's birth, so its `changed_paths` and its
|
|
453
|
+
`--auto-commit` commit set are exactly the created item and
|
|
454
|
+
`<ledger>/.wowbagger/reconcile-<namespace>.md`. Owning the log it writes is not
|
|
455
|
+
permission to absorb residue: a reconciliation log already dirty before the
|
|
456
|
+
invocation is foreign, and create still refuses it.
|
|
457
|
+
|
|
458
|
+
**Commit every create before the next mutation.** A create has no authorized
|
|
459
|
+
predecessor — there is no earlier revision of an item that did not exist — so
|
|
460
|
+
Git `HEAD` is the only surface that can hold its authorized bytes, and an
|
|
461
|
+
uncommitted create reports the global `git-finalization-required` until it is
|
|
462
|
+
committed. A `patch` or `transition` may instead occupy the documented
|
|
463
|
+
authorized predecessor/successor window and run before its predecessor is
|
|
464
|
+
committed, but that window is an accepted overlap, not a licence: commit every
|
|
465
|
+
mutation before the next one. Alpha.14 ships no batch mutation. The supported
|
|
466
|
+
bulk pattern is the create-then-commit loop, one commit per created item, and
|
|
467
|
+
item #186 owns the design of a safe batch create.
|
|
468
|
+
|
|
469
|
+
**Cost.** A provisioned create already took the namespace lock, replayed the
|
|
470
|
+
journal, loaded the ledger, read Git `HEAD`, and reconciled. The fence changes
|
|
471
|
+
which findings refuse; it adds no new Git roster or history traversal to a
|
|
472
|
+
clean create, and the measured cost of the change is two extra fsync'd journal
|
|
473
|
+
appends. The durable cost is journal growth: each successful create adds one
|
|
474
|
+
intent and one committed terminal, so with no other activity the
|
|
475
|
+
65,536-entry limit permits at most 21,845 three-entry create cycles and the
|
|
476
|
+
8 MiB byte limit may bind first. Other claim and mutation activity lowers that
|
|
477
|
+
ceiling. Capacity is checked before publication and fails closed. Journal
|
|
478
|
+
compaction is not part of this change.
|
|
479
|
+
|
|
480
|
+
**A ledger that already carries duplicate numbers is item #182's recovery
|
|
481
|
+
work, not this fix's.** Such a ledger fails validation and refuses every
|
|
482
|
+
mutation before allocation, and item #182 owns the fenced repair. #181 prevents
|
|
483
|
+
new collisions in every valid ledger; it neither renumbers damaged items nor
|
|
484
|
+
makes an invalid ledger worse. Editing `number` in the item source by hand,
|
|
485
|
+
committing it, and then running `claim-adopt` is **not a supported
|
|
486
|
+
workaround**: one field deployment did exactly that as an emergency
|
|
384
487
|
intervention during an outage, and it bypasses the number-collision and
|
|
385
488
|
reference checks every mutation performs, so it can leave dangling
|
|
386
489
|
`depends_on`, `related`, and parent references that nothing reports.
|
|
387
490
|
|
|
491
|
+
**What alpha.14 guarantees, and what it does not.** Alpha.14 closes the
|
|
492
|
+
reported PropertyCompass2 collision: cooperating alpha.14 worktrees of one
|
|
493
|
+
clone that share one Git common directory can no longer commit two items
|
|
494
|
+
carrying the same number, because every such create is visible through the
|
|
495
|
+
shared journal before publication even without branch integration. Separate
|
|
496
|
+
clones, separate machines, alpha.13 writers before the hard cutover, and
|
|
497
|
+
noncooperating writes stay outside that fence and still rely on branch
|
|
498
|
+
integration plus `validate`.
|
|
499
|
+
|
|
500
|
+
**The upgrade is a hard cutover.** Alpha.13 cannot read a `create-v1` legacy
|
|
501
|
+
intent. Executed against a ledger whose shared journal already carries one, its
|
|
502
|
+
create exits 6 with `error.code` `claim-store-unavailable`, message
|
|
503
|
+
`The durable claim store is unavailable.`, and `error.details.reason`
|
|
504
|
+
`claim-store-unreadable`, leaving the state unchanged and writing no item. That
|
|
505
|
+
fail-closed behavior is the compatibility guarantee: an old writer cannot
|
|
506
|
+
commit another duplicate. It is also the operational limit, because alpha.13 is
|
|
507
|
+
immutable and emits a generic unreadable-store message rather than upgrade
|
|
508
|
+
guidance. Read that exact refusal as **this repository was written by a newer
|
|
509
|
+
Wowbagger; upgrade this worktree to continue.** There is no automatic migration
|
|
510
|
+
and no mixed-version grace period: upgrade every writer in one Git coordination
|
|
511
|
+
domain before the first alpha.14 create. If a partial upgrade writes the new
|
|
512
|
+
grammar first, every remaining alpha.13 worktree stops making claim-protected
|
|
513
|
+
mutations until it is upgraded. Item #185 owns the general ability of a running
|
|
514
|
+
old binary to diagnose its own version drift; no change here can retrofit a
|
|
515
|
+
message into an already-published executable.
|
|
516
|
+
|
|
388
517
|
**`unauthorized-revision` has two remedies, and only one of them is
|
|
389
518
|
destructive.** Restoring the authorized revision discards the out-of-protocol
|
|
390
519
|
edit. Adopting the committed revision keeps it and moves the coordinator's
|
|
@@ -401,6 +530,16 @@ Each refusal carries a `reason` that separates the cases:
|
|
|
401
530
|
| `worktree-synchronization-required` | another worktree wrote the item | wait for the owner named by `owner_ref`/`owner_commit`, then synchronize this checkout and run `claim-verify`; if `owner_unavailable` is true, follow the finding's `remediation` (section 3.2 step 4) |
|
|
402
531
|
| `unauthorized-revision` | the item was changed outside the protocol | restore the authorized revision and discard the edit, then `claim-verify`; or adopt the committed revision and keep the edit, then `claim-verify` (section 3.3) |
|
|
403
532
|
|
|
533
|
+
**Scope is the coordinator's judgement, not a published member.** What a
|
|
534
|
+
finding refuses — every write, only a write against the item it names, or
|
|
535
|
+
nothing — is decided from the topology and travels beside the finding, never
|
|
536
|
+
inside it. No response carries it. A consumer MUST NOT infer scope from the
|
|
537
|
+
`reason` string, from the `remediation` sentence, or from whether `owner_ref`
|
|
538
|
+
is present: those are diagnostics for a person, and reading them as a scope
|
|
539
|
+
signal is exactly how alpha.11 treated an out-of-protocol revision as advisory
|
|
540
|
+
synchronization. The only supported scope signal is the refusal itself: a
|
|
541
|
+
mutation that returns exit 6 was blocked, and one that returns exit 0 was not.
|
|
542
|
+
|
|
404
543
|
Ownership on the current symbolic ref is never reported as a foreign worktree
|
|
405
544
|
owner, and a detached `HEAD` applies the same guard through its reachable
|
|
406
545
|
commit history. If the expected revision is already reachable from the current
|
|
@@ -409,16 +548,58 @@ worktree; it remains `unauthorized-revision` and blocks unrelated mutations.
|
|
|
409
548
|
This distinguishes a real sibling's uncommitted successor from history the
|
|
410
549
|
current checkout already owns.
|
|
411
550
|
|
|
551
|
+
**`owner_ref` names an active named worktree, and nothing else.** Owner
|
|
552
|
+
evidence is the worktree roster Git reports live, searched this checkout first,
|
|
553
|
+
then every live worktree that has a branch checked out, ordered by branch ref
|
|
554
|
+
and then by path so one revision carried by several live worktrees always names
|
|
555
|
+
the same owner. A bare record, a record Git marks prunable, and a worktree
|
|
556
|
+
whose `HEAD` names no commit are excluded. `owner_ref` is always the branch of
|
|
557
|
+
one of those live worktrees, and `owner_commit` is the commit in that
|
|
558
|
+
worktree's history whose item bytes are the expected revision.
|
|
559
|
+
|
|
560
|
+
Reachability alone is never ownership. A tag, a remote-tracking ref, a branch
|
|
561
|
+
no worktree has checked out, and a live worktree on a detached `HEAD` can each
|
|
562
|
+
reach the expected revision while naming nobody who could publish it. All of
|
|
563
|
+
them, and a revision no reachable commit carries at all, produce
|
|
564
|
+
`owner_unavailable: true` and no `owner_ref`. So `owner_unavailable` means one
|
|
565
|
+
of three things: the expected revision is not reachable at all, a live sibling
|
|
566
|
+
holds it on a detached `HEAD`, or it is reachable only from refs no active
|
|
567
|
+
worktree has checked out.
|
|
568
|
+
|
|
569
|
+
Three `owner_unavailable` remediation sentences separate those cases. The
|
|
570
|
+
sentence naming ownership that cannot be established from reachable refs is
|
|
571
|
+
emitted only when this checkout has no item path and `HEAD` has none either, so
|
|
572
|
+
the item has never existed here. A revision Git does reach — from a tag, a
|
|
573
|
+
remote-tracking ref, a branch no worktree has checked out, or a live worktree on
|
|
574
|
+
a detached `HEAD` — gets the sentence saying the revision is reachable in Git
|
|
575
|
+
while no active named worktree owner is established. Its remedy is to inspect
|
|
576
|
+
the reachable history and restore or explicitly adopt reviewed bytes, then run
|
|
577
|
+
`claim-verify`; there is no commit left to wait for. Only a revision no
|
|
578
|
+
reachable commit carries at all gets the not-yet-reachable sentence, whose
|
|
579
|
+
remedy is to wait for the owning worktree to commit, then synchronize this
|
|
580
|
+
checkout and run `claim-verify`.
|
|
581
|
+
|
|
582
|
+
Through alpha.13 the reachable cases were given the not-yet-reachable sentence
|
|
583
|
+
too, which told a reader to wait for a commit Git already had. The wait never
|
|
584
|
+
ended, because no named worktree was ever going to publish those bytes. The
|
|
585
|
+
`reason`, the codes, the scope, and the finding members are unchanged; only the
|
|
586
|
+
`remediation` text for the reachable-unowned cases is corrected.
|
|
587
|
+
|
|
412
588
|
**Writer identity.** A legacy mutation and a claimed publication record the
|
|
413
589
|
worktree that authorized them in their journal entries as
|
|
414
590
|
`writer_worktree_id`: `legacy-mutation-intent`, `legacy-mutation`,
|
|
415
591
|
`publish-intent`, and `publish-final`. The field is optional on all of them: an
|
|
416
|
-
entry written before the field existed
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
592
|
+
entry written before the field existed, which is every entry alpha.12 and
|
|
593
|
+
earlier wrote, stays valid, attributes nothing, and leaves the expected writer
|
|
594
|
+
`unknown`. Writer attribution alone therefore cannot turn such a finding into a
|
|
595
|
+
global barrier: it stays advisory `worktree-synchronization-required`. Local
|
|
596
|
+
state and current-checkout ownership are still judged first, and both remain
|
|
597
|
+
global barriers whatever the entry records. A publication resolves its identity
|
|
598
|
+
once under the claim lock, so its intent and its terminal name one writer, and
|
|
599
|
+
a terminal that recovery reconstructs after a lost response carries the
|
|
600
|
+
identity its intent recorded. When the journal names the current worktree as
|
|
601
|
+
the writer of the expected revision and no reachable ref carries that revision,
|
|
602
|
+
no sibling can ever produce it, so the finding is
|
|
422
603
|
`unauthorized-revision` and blocks every mutation instead of
|
|
423
604
|
`worktree-synchronization-required`, which blocks only its own item.
|
|
424
605
|
|
|
@@ -471,13 +652,15 @@ a new UUID, and the removed UUID stays in journal history attributing nothing.
|
|
|
471
652
|
carries `owner_unavailable: true`.
|
|
472
653
|
3. If an owner is named, WAIT for that owner to publish the commit, then
|
|
473
654
|
synchronize this checkout to it. Do not merge unrelated live work.
|
|
474
|
-
4. If ownership is unavailable, the `remediation` string separates
|
|
655
|
+
4. If ownership is unavailable, the `remediation` string separates three cases.
|
|
475
656
|
When it says the expected revision is not yet reachable, the owning
|
|
476
657
|
worktree has not committed it yet: wait as in step 3, then synchronize.
|
|
477
|
-
When it says
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
658
|
+
When it says the revision is reachable in Git while no active named worktree
|
|
659
|
+
owner is established, do not wait: inspect the reachable history that carries
|
|
660
|
+
it, then restore the exact authorized bytes or use explicit `claim-adopt`
|
|
661
|
+
authority after review. When it says ownership cannot be established from
|
|
662
|
+
reachable refs, inspect reachable or dangling commits and remedy it the same
|
|
663
|
+
way. In neither inspection case do you copy peer working-tree bytes.
|
|
481
664
|
5. Run `claim-verify --ledger <dir> --json` in the blocked worktree and require
|
|
482
665
|
exit 0.
|
|
483
666
|
6. Resume the affected item.
|
|
@@ -487,6 +670,27 @@ successful mutation requires a valid ledger and no findings blocking the target
|
|
|
487
670
|
item, not a globally empty findings list. Nonblocking findings remain available
|
|
488
671
|
to `claim-verify`, where a caller that names no target still sees every finding.
|
|
489
672
|
|
|
673
|
+
**A target-scoped success is not a globally clean claim store.** The two scopes
|
|
674
|
+
answer different questions and a caller MUST NOT substitute one for the other.
|
|
675
|
+
A mutation, and auto-commit inside it, requires only that no finding blocks its
|
|
676
|
+
own target item. `claim-verify` names no target, so every unresolved finding
|
|
677
|
+
blocks it: exit 6 there is the repository-wide answer, and it stays exit 6
|
|
678
|
+
while any item in the repository carries a blocking finding, including one
|
|
679
|
+
belonging to work this caller has nothing to do with. A cooperating worker can
|
|
680
|
+
therefore commit every one of its own mutations while `claim-verify` never
|
|
681
|
+
reaches exit 0, because a sibling worktree's unpublished item is visible from
|
|
682
|
+
every checkout that shares the common directory.
|
|
683
|
+
|
|
684
|
+
That is current behavior, not a settled design. Item #184 is open in triage to
|
|
685
|
+
choose the supported surface — item-scoped verification, target-aware input, or
|
|
686
|
+
repository-wide success with structured foreign-work warnings, while keeping a
|
|
687
|
+
strict whole-repository mode. Until it ships, an automated gate that demands a
|
|
688
|
+
globally clean `claim-verify` is unsatisfiable in a repository with live
|
|
689
|
+
sibling work. Gate on the mutation's own refusal, and read a repository-wide
|
|
690
|
+
`claim-verify` as the diagnostic it is. Do not hand-edit an item or run
|
|
691
|
+
`claim-adopt` to reach exit 0: adoption moves the coordinator's authorized
|
|
692
|
+
revision and is not a way to silence a sibling's finding.
|
|
693
|
+
|
|
490
694
|
`claim-verify` in the writing worktree finalizes that worktree's own state; it
|
|
491
695
|
does not make a sibling checkout able to see the item. Synchronization or
|
|
492
696
|
explicit recovery is still required for the affected item.
|
|
@@ -666,6 +870,23 @@ fails or its durability is uncertain, the backend returns exit 6 with
|
|
|
666
870
|
to guess. It cannot report a lease success or semantic rejection whose time
|
|
667
871
|
was not persisted.
|
|
668
872
|
|
|
873
|
+
**A refused reconciliation has already advanced the floor.** Reconciliation
|
|
874
|
+
persists its clock entry before it classifies anything, so a command that then
|
|
875
|
+
refuses with exit 6 `claim-store-unavailable`, reason
|
|
876
|
+
`publication-reconciliation-required`, has still moved the durable floor to
|
|
877
|
+
`max(physical_utc, previous_floor)`. No claim, epoch, or ledger byte changes,
|
|
878
|
+
and the refusal reports none. The floor is monotonic, so the effect is on time
|
|
879
|
+
alone and it is permanent: once a refused reconciliation has published a floor
|
|
880
|
+
at or past a lease's `expires_at`, that lease can never read live again, and a
|
|
881
|
+
later acquire, renew, or fence check on the same tuple observes it expired.
|
|
882
|
+
On a ledger whose floor already runs ahead of this caller's wall clock, every
|
|
883
|
+
refused reconciliation republishes the higher floor, so a holder whose lease
|
|
884
|
+
looks live by its own clock may find it expired the moment it asks. A caller
|
|
885
|
+
that meets a barrier during a long recovery SHOULD expect to reacquire rather
|
|
886
|
+
than assume its lease survived. This is section 5's rule for a rejected
|
|
887
|
+
decision applied one step earlier: an advanced floor is never evidence that
|
|
888
|
+
anything succeeded.
|
|
889
|
+
|
|
669
890
|
When `last_epoch` is `18446744073709551615` (the unsigned 64-bit maximum), a
|
|
670
891
|
new acquire or takeover is impossible. After the authoritative decision time
|
|
671
892
|
has been persisted, the backend returns exit 6 `epoch-exhausted` with message
|
|
@@ -1095,18 +1316,24 @@ create MUST reject any item identity whose tuple has claim history with exit 4
|
|
|
1095
1316
|
high-water mark. Both checks persist authoritative decision time before their
|
|
1096
1317
|
response.
|
|
1097
1318
|
|
|
1098
|
-
Before a merge-coordinated backend permits a legacy transition or
|
|
1099
|
-
write bytes, it MUST fsync a `legacy-mutation-intent`
|
|
1100
|
-
candidate revisions. Under the same namespace lock, it MUST
|
|
1101
|
-
capacity for that intent, one recovery clock entry, and one
|
|
1102
|
-
committed write appends `legacy-mutation`; an unchanged write
|
|
1103
|
-
`legacy-mutation-abort`. Reconciliation resolves a pending intent to
|
|
1104
|
-
committed terminal when the candidate revision is present, or to the abort
|
|
1319
|
+
Before a merge-coordinated backend permits a legacy create, transition, or
|
|
1320
|
+
patch to write bytes, it MUST fsync a `legacy-mutation-intent` carrying the
|
|
1321
|
+
expected and candidate revisions. Under the same namespace lock, it MUST
|
|
1322
|
+
reserve journal capacity for that intent, one recovery clock entry, and one
|
|
1323
|
+
terminal entry. A committed write appends `legacy-mutation`; an unchanged write
|
|
1324
|
+
appends `legacy-mutation-abort`. Reconciliation resolves a pending intent to
|
|
1325
|
+
the committed terminal when the candidate revision is present, or to the abort
|
|
1105
1326
|
terminal when the expected revision remains. Any third revision produces
|
|
1106
1327
|
`legacy-mutation-outcome-unknown`. The latest committed terminal is the
|
|
1107
1328
|
authorized expected revision. A later unrecorded revision remains a stale
|
|
1108
1329
|
write.
|
|
1109
1330
|
|
|
1331
|
+
A create has no predecessor, so its intent carries `expected_revision: null`,
|
|
1332
|
+
which is valid only for `create-v1`, and the item path's absence is what
|
|
1333
|
+
resolves it to the abort terminal. Section 3.1 states the complete create
|
|
1334
|
+
ordering, its allocation fence, and the alpha.13 cutover the widened grammar
|
|
1335
|
+
forces.
|
|
1336
|
+
|
|
1110
1337
|
A merge-coordinated backend MUST reconcile before it authorizes a legacy write,
|
|
1111
1338
|
and section 6 states the same requirement for `publish-claimed`.
|
|
1112
1339
|
When reconciliation produces any blocking finding, the legacy command MUST
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wowbagger",
|
|
3
|
-
"version": "0.1.0-alpha.
|
|
3
|
+
"version": "0.1.0-alpha.14",
|
|
4
4
|
"description": "Git-native work ledger for coding agents: deterministic ready queues, guarded CAS mutations, claims and fencing, and self-contained HTML reports.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|