Repository navigation
[Bug]: archive-completed of a Todo with a released lease record drifts the runtime-shadow qualification (orphaned lease stays in the candidate head) #4315
Description
Activity
- addedbugSomething isn't workingSomething isn't workingcontrol-planeQuota, todo, scheduler, registry, state, goal, or runtime control-plane change.Quota, todo, scheduler, registry, state, goal, or runtime control-plane change.direction/shared-coordinationShared-goal authority and cross-host coordination contracts.Shared-goal authority and cross-host coordination contracts.
on Sep 13, 2026 Thanks for the precise reproduction and for keeping the ladder gap explicit. Confirmed on current
main6b337bc with the real public CLI and production FileAuthorityStore, using only disposable synthetic goals:- Leased completion:
inspect=matchedat cursor 5; archive delivers the Todos entry at cursor 6, theninspect/qualify/read-candidatereport drift and refuse qualification. Source leases = 0, candidate leases = 1 (released); the physical lease inventory still contains 1 record. - Unleased
soft_claimcontrol: archive remainsmatched, and bounded qualification succeeds. Reading the archived Todo correctly returnstodo_missingwith a qualified lineage. - Restoring the pre-archive Markdown bytes does not repair the candidate history.
deliveredis transport/receipt evidence; the writer explicitly reportsparity_verdict=not_evaluated.
The root cause is confirmed: archive captures only
{handoff_mode, todos}, while the source's lease membership depends on the active Todo set. The two capture partitions are physically separate but not semantically independent. #4286 is now merged, but its canonical archive transaction does not change this pre-promotion capture path. This is a D3 capture-qualification blocker; it does not establish a File/SQLite storage-engine failure or failure of the canonical archive command.There is also an adjacent failure the fix needs to cover. I reproduced this sequence on the same revision:
- Complete and archive a leased Todo before bootstrap, retaining its historical lease file.
- Bootstrap:
inspect=matchedbecause the baseline correctly excludes that orphan. - Add another Todo and acquire its lease.
- The lease capture stalls with
source_partition_continuity_unproved; inspection then refuses qualification withoutbox_pending.
beginLeaseOutboxEntrycomputes both its before-image digest and after-image from the full physical lease directory (current code), while the source snapshot excludes archived/orphaned lease records. A one-time archive-side removal alone would therefore leave continuity inconsistent for subsequent lease writers.Direction: prefer option 1's explicit, source-correlated retirement, including that subsequent-writer boundary. Keep historical lease files/receipts for audit, and preserve the existing source rule. Retirement here means removing an edge from the current capture projection, not deleting history or releasing an already released lease again.
Please make the repair and executable ladder row/mutant one cohesive package, with these acceptance conditions:
- Bootstrap, archive capture, and subsequent lease capture use the same current-lease membership rule and compatible before/after digests. Keep the semantic rule in the typed coordination owner; Python should adapt the Markdown source rather than grow another rule implementation.
- If archive produces two partition entries, durably prepare their source correlation before the primary replacement. A crash or bounded drain between entries must remain visibly pending/unqualified until settlement, with deterministic replay and no duplicate effects. Do not manufacture a physical
task_lease_recordmutation merely because the current schema associates that source kind with the leases partition; represent the archive-caused projection change honestly and validate its source proof/lock ordering. - Cover leased archive, unleased control, preview/no-op, retained historical leases at bootstrap, and a subsequent lease acquisition after archive/recovery. Preserve unrelated active leases and archived Todo dependencies. Exercise crashes around preparation, primary replacement, markers and drain, plus interleaving with a lease writer.
- Activate
s2c2.archive_after_leased_completion_parity, add archive to the mixed-writer coverage, and make a matching mutant fail when retirement or membership continuity is omitted. Use the real CLI/File provider; include other real providers if their shared store path changes. Keep the >=10-day soak and promotion holds explicit.
I would not choose option 2: importing all historical lease files changes the live-state contract and growth characteristics. A deterministic derived head (option 3) is not inherently non-replayable, but a filter added only to
composeLocalAuthorityShadowHeadwould not reconcile source digests, partition receipts/cursors and retained-history replay. That would be a larger protocol change, not a local shortcut.Nor should this wait for T4: the current roadmap requires qualified capture for D3 and puts full legacy-writer retirement after approved cutover. Removing the test now would leave that migration window unproved.
One recovery wording clarification: rollback archives the old lineage/outbox (
archive_retained=true); rebootstrap starts a new qualification lineage. Historical audit evidence is retained, but qualification continuity is reset. The additional reproduction above shows why a green bootstrap alone is not sufficient recovery evidence.No runtime fix is included in this comment; keep this issue and the promotion hold open until the repair and regression evidence land.
- Leased completion:
- added 2 commits that reference this issue
on Sep 14, 2026
Summary
Under active
coordination.runtime_shadowcapture,todo archive-completedon a Todo that still holds a released task-lease record makes the bounded qualification drift:coordination-shadow inspectreportsdrifted/shadow_projection_drift, andqualify/read-candidatereject from then on. The same archive of a Todo that never had a lease staysmatched. Restoring bytes does not requalify; the only recovery isrollback --provider-revision … --executeplus a freshbootstrap --execute, which discards the captured lineage.This is the ordinary
hard_leaseflow (task-lease acquire→todo completewith the lease →todo archive-completed), so it blocks the D3 card's "audit sustained mixed-writer coverage against the final command matrix". #4167 declared it as the pending ladder rows2c2.archive_after_leased_completion_parityand mades2c2.parity_equalarchive nothing rather than hide it. Nothing onmainhas changed the path since; #4286 isolates the canonical (post-promotion) archive transaction intodo_archive.tsand does not touch this legacy capture path.Issue origin: observed and reproduced in a disposable environment (real CLI, production
FileAuthorityStore, no live Goal).Reproduced on:
f38847b1c(first seen) and58dbaeaec(today).Reproduction (public CLI only)
Registry: one
hard_leaseGoal,registered_agents: [agent-a, agent-b],coordination.runtime_shadow = {schema_version: loopx_coordination_runtime_shadow_config_v0, enabled: true, provider: file_v0}; disposable--runtime-root.Control: the same sequence without the lease (
soft_claim,todo completewithout lease flags) ends withinspectmatchedafter the archive.Observed difference between the two projections after the archive (todos partition equal,
handoff_modeequal):build_runtime_shadow_source_snapshot):leases: []leases: [{todo_id: T, status: "released", owner: agent-a, version: 1, …}]Root cause
lease_inventorybut is not projected.test_source_snapshot_preserves_inventory_without_projecting_orphan_leasespins that rule.todo archive-completedunder capture prepares and commits onetodos-partition entry. The candidate'sleasespartition still holds the released lease committed by the earlier fence-close transaction (task_lease_fence_close), and no writer ever retires it.qualifySnapshotcompares different head digests.Candidate fixes (owner decision)
archive-completedmoves a Todo that has a lease record, it also prepares/commits aleases-partition entry whose projection drops the archived Todos' leases. Keeps the orphan rule and the existing test; one command then produces two partition entries, so their ordering and a crash between them need the same settle-from-receipts treatment as today's single entry.test_source_snapshot_preserves_inventory_without_projecting_orphan_leases; a bootstrap baseline would then import every historical released lease, which grows the live head.composeLocalAuthorityShadowHeaddrops leases whose Todo left thetodospartition when atodosentry commits. No new writer entry, but the head is then derived rather than captured, which weakens "one receipt per mutation" and replay determinism.If the roadmap retires the legacy archive writer before D3 (T4), closing by retirement is also an answer; the ladder row would then need a canonical-archive twin instead.
Option 1 looks most consistent with "one receipt per mutation", but it is your call. Once decided, I can turn the pending ladder row into an executable regression with a matching mutant within the qualification scope.
Evidence
examples/shared-goal-authority-e2e/README.md(pending rows paragraph),loopx/control_plane/testing/authority_e2e_ladder.py(PENDING_ROWS), RFC §11.2 in both languages.s2c2.parity_equal(mixed writers without archive),s2c2.parity_divergent_detects_foreign_edit(drift detection and rollback recovery).