Skip to content

An erased device stays erased: the lease dies with it, and construction leaves no trace - #117

Merged
lannbot merged 1 commit into
mainfrom
fix/erased-namespace-resurrection
Aug 25, 2026
Merged

An erased device stays erased: the lease dies with it, and construction leaves no trace#117
lannbot merged 1 commit into
mainfrom
fix/erased-namespace-resurrection

Conversation

@lannbot

@lannbot lannbot commented Aug 25, 2026

Copy link
Copy Markdown
Collaborator

Fixes #112 (the class; the CI trigger's identity is discussed honestly in the commit).

Three doors closed on one oracle:

  1. The undying lease heartbeatstartLease's stop handle was deliberately dropped on a lifetime argument that breaks exactly at destroy: the 5s touchLease kept firing after destroyNamespace, and IndexedDB open-on-missing brought the deleted database back. Row 65b pins it (resurrected in 1200 ms unstopped; absent stopped).
  2. Construction as resurrectionbootSeq's module-evaluation write meant new SharedWorker(url, {name: pm-device-<erased>}) alone recreated the database; status() (reachable pre-attach) recreated it with a read; attach's lease start was a third. All three now gate on the index row — the existence oracle anchorIsLive already documents — with creation order verified row-first on every constructing path (boot-counter rows stay green and meaningful). Row 66's negative control: unguarded, the database is back after construction alone, bootSeq=1, no client call, no timer.
  3. The harness's own offencehc-forget's page-side delete of live-hosted devices strewed ~31 resurrected databases per matrix run; cleanup now tears down through the worker (row 66b).

Plus onTokenRefreshed's in-flight write now re-checks destroyed at land time.

Gates: devstore 75 rows green; solo-erase/persistence/ephemeral ×2 green; deno check clean. Full e2e rides CI.

Automerge is armed.

…on leaves no trace

Issue #112's investigation, in three findings. First, the lease
heartbeat's stop() handle was deliberately dropped on a lifetime
argument (lease = lock = global) that holds for every ending except
erasure, where the DEVICE dies before the global: destroy cleared the
checkpoint debounce and the sync timers but the 5s touchLease kept
firing, and ns.put on a deleted database is IndexedDB open-on-missing —
the deleted database is back. The handle is kept and destroy stops it
first (devstore row 65b: resurrected in 1200ms unstopped, absent
stopped).

Second — the negative control for the timer theory did NOT go red,
because serve() closes the global one task after destroy replies, so
the field flake needed another door. It found three: bootSeq's
module-evaluation write meant MERELY CONSTRUCTING a SharedWorker named
for an erased device recreated its database (no client call, no timer
— the construction IS the resurrection); status(), reachable on a raw
port before attach, recreated it with a read (indexedDB.open creates
whatever the transaction mode); attach's takeLock started a lease
whose first act is a put. All three now gate on the INDEX ROW — the
existence oracle anchorIsLive already states in as many words — with
creation order verified row-first on every constructing path, so a
legitimate first boot still counts 1 (rows 11/12/14/15/56 green). Row
66's negative control: unguarded, the database is back after step ONE.

Third, the harness itself committed the offence: hc-forget's page-side
delete left live hosts heartbeating at deleted databases (~31 stray
databases per matrix run). cleanup() now tears down through the worker
(row 66b: 7s of nothing, absent; unguarded control present). Plus one
non-timer hole from the audit: onTokenRefreshed's fire-and-forget
writeOauth re-checks destroyed at land time, not call time.

The field trigger for the CI flake remains unproven — this closes the
CLASS (no constructor, RPC or teardown path can recreate an erased
namespace) rather than naming the one door CI caught. If solo-erase
flakes again, the remaining suspect recreates the index row itself.

Gates: devstore 75 rows green (65, 65b, 66, 66b new); solo-erase,
solo-persistence, solo-ephemeral x2 green; demo deno check clean.
@lannbot
lannbot enabled auto-merge August 25, 2026 12:45
@lannbot
lannbot merged commit 198a892 into main Aug 25, 2026
3 checks passed
@lannbot
lannbot deleted the fix/erased-namespace-resurrection branch August 25, 2026 13:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

solo-erase flakes in full-suite runs: the erased device's IndexedDB database survives the erase

2 participants