Recover held slots on restart — an empty memory is not an empty pool #40
Labels
No labels
Compat/Breaking
Kind/Bug
Kind/Documentation
Kind/Enhancement
Kind/Feature
Kind/Security
Kind/Testing
Meta/Campaign
Meta/Epic
Meta/Session
Priority/Critical
Priority/High
Priority/Low
Priority/Medium
Reviewed/Confirmed
Reviewed/Curated
Reviewed/Duplicate
Reviewed/Invalid
Reviewed/Won't Fix
Scope/Campaign
Status/Abandoned
Status/Blocked
Status/Conflicted
Status/In Progress
Status/In Review
Status/Need Grooming
Status/Need More Info
Status/Ready
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
Ting/Vedanta#40
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Proposal
Reconstruct which slots are held when the cycle starts, so a restart does not hand out identities that are still in use.
Delivery class
agent-unit— durable occupancy state and its recovery, with capture doubles.Design
Cycle::newcurrently starts every slotAvailable. That is correct for a pool that has never run and wrong for every restart after the first: a process that restarts while drones hold slots comes back believing the pool is empty, and hands the same identities to a second set of occupants.This is the failure #37 exists to prevent, arriving through a door #37 does not watch. #37 guarantees that nothing infers a slot is free — no timeout, no staleness rule, no unreachable runtime. It says nothing about a process that never knew the slot was held, and an empty in-memory map is not an inference. The safety property is stated per-transition and the gap is in the initial state.
Occupancy becomes durable. Checkout writes the holder — session, assignment, grant, generation — before the slot is handed out; completion and release clear it after the slot is safely back. Startup reads it and reconstructs
SlotStatusfrom what is recorded rather than from the absence of a record.Unreadable state denies, it does not default. A recovery that cannot read its own record must refuse to allocate rather than start empty: "I do not know what is held" and "nothing is held" are different answers, and only one of them is safe. This is the same discipline
operations.rsapplies to its journal, and the same one #37 applies to a failed liveness check.Ordering mirrors the cycle's. The occupancy record is written before the stamp and the hand-out, and cleared after revoke and rotate — so a crash at any point leaves the slot held, which is the recoverable side of every window.
Tasks
SlotStatusat startup from the durable record.Specification Delta
Requirement: a restart does not free a held slot
Scenario: the process restarts while a drone holds a slot
Requirement: unknown occupancy is not empty occupancy
Scenario: the durable record cannot be read
Provenance
Found while building #35–#37 and flagged in PR #39 and in
Cycle::new's own doc comment. Filed rather than left in a merged PR body: it is a safety gap, andsrc/checkout.rsmust not run against a real pool until this lands.OpenSpec 30 does not currently specify restart recovery at all — that is a package gap rather than a Session that missed its scope, so the package wants an amendment alongside this record.
OpenSpec
OpenSpec 30
Structural parent
Ting/Ting#2 — Epic: agent identity pool lifecycle.