pool: recover held slots on restart — an empty memory is not an empty pool #41
No reviewers
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!41
Loading…
Reference in a new issue
No description provided.
Delete branch "pool/40-restart-recovery"
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?
Closes the gap #39 flagged in its own doc comment and PR body. OpenSpec 30 amended to v0.1.2 alongside — the package never specified recovery at all, so this was a package gap rather than a Session that missed its scope.
The bug
Cycle::newstarted every slotAvailable. Correct for a pool that has never run, wrong for every restart after the first: a process that comes back while drones hold slots believed the pool was empty and would hand the same identities to a second set of occupants.This is the failure #37 prevents, arriving through a door #37 does not watch. That rule is stated per transition — nothing infers a slot is free — and a restart takes no transition. An empty map is not an inference; the process simply never knew. The guarantee has to hold over the initial state too, or it does not hold.
The fix
Occupancy is durable.
Cycle::recoverreconstructs each slot from the ledger, andnewis gone rather than deprecated, so no caller can accidentally start empty.Ordering errs toward held, in both directions. The write precedes the stamp, so a process dying anywhere after it leaves a slot recorded as held for an occupancy that may never have gone live — recoverable by an operator's release, costing one slot out of ten. Recording after the hand-out would instead leave a window where a live drone holds a slot nothing remembers, and that cost is unbounded. The record clears only after revoke and rotate have succeeded, for the same reason in reverse.
A refusal is not a crash. When the stamp is refused, nothing was handed out and nothing is live, so the record is compensated immediately rather than waiting for a human. An interrupted process cannot reach that compensation — which is exactly why the write comes first.
Unreadable state denies rather than defaulting. "I do not know what is held" and "nothing is held" are different answers, and only one is safe. A damaged ledger fails the constructor, so a cycle that does not know what it holds cannot be built and therefore cannot allocate — structural, not a check someone can forget to write. Torn tail tolerated, torn middle refused, matching
operations.rs.Verification
Gate green: fmt stable,
clippy --workspace --all-targets -D warningsclean,cargo test --workspacepassing — 103 lib tests, up from 96.ExhaustedOccupancyUnavailable, and the cycle cannot be constructedOne test was honest about its limits rather than faked. A capture double cannot die between two awaits, so the crash-window test writes what the crash would have left and says so in the comment. Pretending otherwise would have made it a test of the double.
Still not here
OccupancyandPoolDirectoryhave no Kanidm or OpenBao implementations; both wait onsa-vedanta(flake-ops#98).held()is what an alert reads; raising it is #11's mandate on flake-ops#470's plane.Closes #40