feat(guardian): recovery dispatcher (shadow) and the pending-rotation recheck exit - #800
Open
WiktorStarczewski wants to merge 12 commits into
Open
Conversation
F-001 P0 recheck passed the Dexie row uuid to getTransactionCommitState, which
matches the on-chain hash — it could only answer 'not-found', so the
budget drained without ever looking and raised a permanent prompt
F-002 P1 a discarded rotation left the vault naming an operator with no
on-chain authority; drift cannot see it, so the recheck reverts
F-003 P1 shadow dispatcher read two hardcoded budgets and no pendingRotation
F-004 P1 claim fence missed destructuring and destructured parameters
F-005 P1 ledger fence missed export const, var, Weak*, LHS annotations
F-006 P1 commit-state read had no post-sync hold re-check
F-007 P1 backfillGuardianEndpoints bypassed the accounts write queue
F-008 P1 dispatcher totality test was true of any total function
F-010 P1 ledger charged against a captured count, losing concurrent charges
F-014 P2 a missing vault record reported 'no-onchain-guardian', a claim about
the chain it had established nothing about
F-018 P2 the 429 log re-derived its own clamp beside the one imposed
…ewer binding
F-032 P0 rollback force-wrote a ≤30min-stale endpoint over whatever was bound;
now a guarded RevertGuardianEndpoint action, conditional on the account
still naming the discarded target and on the guardianEpoch CAS
F-033 P1 rollback ran after the demote, the point of no return; vault first now
F-034 P1 one exhausted pending row condemned its siblings — exhaustion is keyed
per row id, not per account
F-035 P1 a late refund reopened a budget another handle had closed; closure is
a monotonic flag, not a counter value
F-036 P1 a WASM eviction in drift was swallowed and the pass took more locks
F-037 P2 an explicitly-undefined patch field erased a bound endpoint/baseline
F-038 P2 a failed advisory status stamp reported the landed binding as failed
…nces F-031 P1 finalize preflight never re-checked its WASM hold after the sync F-032 P1 asPreflight rewrapped WasmClientPoisonedError, blinding every caller F-033 P1 three arms swallowed an eviction and kept taking locks F-034 P1 URL-equality rollback undid a successful retry to the same operator F-035 P2 claim fence rewritten on the AST; regex missed most destructuring F-036 P2 ledger fence rewritten on the AST, glob-discovered, default-deny F-037 P2 shadow classifier read a pass-start snapshot two writers had moved F-038 P2 pending-rotation listing no longer retains the whole history F-040 P2 exhausted-rotation prompt now retracts once the row stops pending
…ndles F-041 P1 claim fence parsed every file as TSX, unfencing five modules F-042 P1 discarded rotation with no rollback target was demoted anyway F-043 P1 a rollback stuck on 'stale' retried forever and never surfaced F-044 P1 an eviction from the rollback landed in an unclassified catch F-045 P1 four holds read a borrowed Account handle after a parking await F-046 P2 rollback probe used the 20s user timeout on the 3s tick F-047 P2 a missing vault record answered 'superseded', spending the demote F-048 P2 two shadow facts asked per account where the subject is narrower F-049 P2 five JSDoc blocks bound to the wrong declaration
F-060 P1 five eviction-seam tests; every guard was deletable with the suite green F-061 P1 AttemptLedger.write dropped `endpoint`, so anySpentForAccount read high forever F-063 P1 legacy discarded row kept pending so the manual-recovery prompt stays up F-064 P1 rollback probe back to the 20s timeout — 5s condemned slow-but-healthy operators F-062 P2 claim fence misparsed .ts as TSX in the allowlist-minimality check F-065 P2 cap pending-rotation node syncs per pass F-066 P2 shadow classifier prefers an exhausted row over a merely unsettled one F-067 P2 ledger fence: object-literal and numeric-init budget bypasses closed F-068 P2 a watchdog eviction from the rollback now books against the fuse F-069 P2 drift test mocked a probe its subject never calls
…to the frontend F-071 P0 two discarded rotations could strand the account on the middle operator F-072 P0 a cold-re-register eviction was charged to the operator and zeroed the fuse F-073 P1 the intercom dropped the error class, so poison could not be classified F-074 P1 the eviction breaks jumped past the only fuse report on their key F-075 P1 a guardian round-trip eviction did not stop the pass
F-081 P0 the adopt arm swallowed poison, so the pass classified the 401 and zeroed the count F-082 P0 drift ran unfused and broke the loop, starving every account behind it F-083 P1 a realm-error trap was booked as evidence the node had parked us F-084 P1 the internal envelope dropped `reason`, so the watchdog test was always false F-085 P1 the missing-registration snapshot threw past its own "evicted" contract F-086 P1 a per-row fuse note erased what the rest of the recheck had accumulated F-087 P1 an eviction before the /configure was charged to the operator's budget F-088 P1 the new eviction paths had no tests F-089 P2 the closed-budget test could not fail F-090 P2 three presence-test spellings and .tsx walked through the fences F-091 P2 an unreachable arm implied this line fed the eviction count F-092 P2 the register preflight took the five-minute backstop from a 3s loop F-093 P2 a borrowed account handle was read after its hold released F-096 P2 an empty patch spent an epoch and turned concurrent repairs stale F-097 P3 one abandoned attempt retired the hardening check for the session F-098 P3 the bindingChanged comment justified something the code does not do
…bsence F-111 P0 a poisoned hardening call was swallowed, and success booked over the eviction F-102 P1 the legacy object envelope lost `name` and `reason`, so both classifiers read false F-103 P1 an eviction before the /configure was still charged to the operator's budget F-104 P1 guardian-manager read a borrowed account handle after its hold released F-105 P1 the cold service build read signer details with no hold at all F-107 P1 the register preflight derived its payload on a handle it never re-checked F-108 P1 one shared recheck fuse key let a healthy sibling erase a parked account's evidence F-116 P1 seven of the above reverted cleanly with the whole suite still green F-117 P1 the poison break sat behind the recording gate, so a retired pass kept borrowing F-106 P2 passMayRecord checked the generation only on entry, never after its awaits F-112 P2 a realm-error poison fell through all three arms and booked nothing F-114 P2 a 3s-cadence service build took the five-minute backstop F-118 P2 MultisigService.init took an unbounded, unlabelled lock F-115 P3 the recheck's outermost catch reported the eviction and recorded none of it
…first probe F-116 P1 the first mutation pass left five reversions green; these fail under each M1 the register preflight's post-account-read hold re-check M2 guardian-manager's re-check between the account and signer reads M3 onBeforeRegister firing on the POST side, and not at all when evicted M5 the poison re-throw out of ensureGuardianProcedureThresholds M6 the recheck outer catch's fuse booking
…g fences F-121 P0 runSync stage-2 swallowed an eviction and reported it as a canonicalization failure F-122 P1 reRegisterCurrentStateOnGuardian held the client unbounded and unlabelled on the sync cadence F-123 P1 the failure arm's stale-pass continue withheld the evidence the fuse needs to light F-124 P1 service coalescing shared one init across both lock ceilings, defeating boundAtSyncCeiling F-125 P1 the 429 cooldown test advanced Date.now while the code read performance.now F-126 P1 the post-account-read hold guard was deletable with the suite green F-127 P1 the post-sync hold guard was deletable with the suite green F-128 P2 a non-poison hardening failure aborted a guardian sync that had already succeeded F-129 P2 the empty-rows exit left fuse evidence for a subject that no longer exists F-130 P2 syncGeneration only ever moved in test code, so the retirement check was inert F-131 P2 a stale attempt-ledger handle could refund or close the incarnation that replaced it F-133 P2 the claim fence read through parentheses but not through type assertions F-134 P2 the ledger fence missed arrays, static class fields, and signed-literal counters F-135 P3 two guardian log prefixes out of forty were spelled differently F-136 P3 two of this round's fixes landed without their reasoning reachable from the code
F-138 P0 sync coalescing cleared its handle by generation, so a retired pass
wedged guardian sync on a promise nobody settles
F-139 P1 sync fuse had no way to report a probe that never reached the node
F-140 P1 a trap eviction was booked as evidence against the endpoint
F-141 P1 pending-rotation recheck never booked an abandoned pass
F-142 P1 the recheck's outer catch booked nothing for a local failure
F-143 P1 a lit fuse blocked local prompt retirement, not just node work
F-144 P1 registration budget was charged for a parked node read, disabling the
only automatic repair an unregistered account has
F-145 P1 didDirectSwitchLand folded an eviction into "no verdict"
F-146 P1 the commit wait swallowed poison and carried on
F-147 P1 a borrowed account handle was dereferenced past its hold, for a log line
F-148 P1 claim fence failed open for hasOwnProperty.call/.apply
F-149 P1 claim fence failed open for destructuring assignments
F-150 P1 ledger fence failed open for nested stores and nested writes
F-151 P1 drift rollback read the raw endpoint field, condemning legacy-global
accounts to the unrepairable prompt
F-152 P2 self-heal suite leaked the module-scoped fuse between tests
F-153 P2 charge-timing comment contradicted itself after F-144
F-154 P2 fuse-key comment claimed a shared key where it is per-account
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Guardian recovery gains its one trigger table — and the acknowledged no-owner wedge (a guardian switch submitted but never confirmed on chain) gets its exit. This closes the last of the four defect classes from #786's review: states every recovery mechanism declined at once, where rounds 17–20 each moved the wedge because no single predicate could prove the union covered the space.
back/guardian-recovery-dispatcher.ts—classifyGuardianRecovery(facts) → route, one total function over typedGuardianFactswith typed prompt reasons. The totality test enumerates the full 512-row fact product and asserts the invariant the class violated: a blocking status or pending rotation is never left without an exit, and a spent repair budget is never silent even when the unrepairable flag write was missed.noteRecoveryDivergence, logged once per kind). Trigger relocation happens only after a release of divergence data reading ~zero.readDirectSwitchCommitState(the throwing sibling ofdidDirectSwitchLand, so fuse evidence survives), bounded by a Seam-C ledger (15 reads on a flat 120s cadence), labeled at the sync ceiling, gated on and reporting into its own sync-fuse key per the Mobile: the app can freeze permanently while idle-syncing, apparently after RPC rate-limiting #777 discipline.committed→ the row upgrades (title, chip, receipt copy all follow via the Seam-A verdict);discarded→ the row honestly demotes to Failed and drift reconciliation repairs the endpoint on its next tick.606 suites / 10,013 tests, ts, lint, i18n all green.
Part 4 of 4 of the seam plan from #786's review. Stacked on #799 (Seam C).
Reviewers: the recheck's settle rules are the part worth your time — a non-verdict (
pending/not-found) charges the bounded budget, a failed read refunds it, and only a real chain answer clears it; wrong bookkeeping here either prompts a healthy rotation or waits forever on a dead one.