Complete tenant offboarding: reaps EVERY tenant-keyed store — snapshot
rows of ANY status (a tenant's suspended-and-abandoned runs are never
eligible for the terminal-only retention purge at any age), the tenant's
agent-memory rows (threads/messages/resources — salted per
docs/agent-memory-tenancy.md, so the same range predicate is exact), the
tenant's approval records (titles, summaries, payloads, decider
identities), and its R2 artifacts.
The snapshot predicate is a range over the tenant-salted runId:
run_id >= '<tid>_' AND run_id < '<tid>' || CHAR(0x60). tenantId is
revalidated against the tenant-ID pattern here — not for injection safety (every query is
parameter-bound) but for RANGE-EXACTNESS: the predicate selects exactly one
tenant only because the charset excludes every character in [0x5F, 0x60]
('_' is 0x5F; its successor 0x60, backtick, closes the range under BINARY
collation). 'abc' can never sweep 'abcdefg'.
PURGE POLICY (the race): this deletes SUSPENDED rows, and a reviewer
approving at that moment would resume against a vanishing row (absorbed:
the runtime's pre-check throws before any step re-executes — pinned by the
purge-race regression test). Only purge tenants whose tokens have already
expired, so no live caller can be mid-resume by construction — or purge at
the tenant's own explicit request (the showcase's self-service /demo/reset).
That caller, uniquely, can race ITSELF: a run started between the artifact
SELECT and the snapshot DELETE is reaped by the DELETE (which re-reads the
range) with its artifacts unenumerated, and the three statements are not one
transaction — an R2 delete can never join a SQL one — so a mid-purge failure
leaves it partially applied. Both are pinned in d1-storage.test.ts. Neither
can escape the requester's own tenant: the range predicate is exact.
Complete tenant offboarding: reaps EVERY tenant-keyed store — snapshot rows of ANY status (a tenant's suspended-and-abandoned runs are never eligible for the terminal-only retention purge at any age), the tenant's agent-memory rows (threads/messages/resources — salted per docs/agent-memory-tenancy.md, so the same range predicate is exact), the tenant's approval records (titles, summaries, payloads, decider identities), and its R2 artifacts.
The snapshot predicate is a range over the tenant-salted runId:
run_id >= '<tid>_' AND run_id < '<tid>' || CHAR(0x60). tenantId is revalidated against the tenant-ID pattern here — not for injection safety (every query is parameter-bound) but for RANGE-EXACTNESS: the predicate selects exactly one tenant only because the charset excludes every character in [0x5F, 0x60] ('_' is 0x5F; its successor 0x60, backtick, closes the range under BINARY collation). 'abc' can never sweep 'abcdefg'.PURGE POLICY (the race): this deletes SUSPENDED rows, and a reviewer approving at that moment would resume against a vanishing row (absorbed: the runtime's pre-check throws before any step re-executes — pinned by the purge-race regression test). Only purge tenants whose tokens have already expired, so no live caller can be mid-resume by construction — or purge at the tenant's own explicit request (the showcase's self-service /demo/reset). That caller, uniquely, can race ITSELF: a run started between the artifact SELECT and the snapshot DELETE is reaped by the DELETE (which re-reads the range) with its artifacts unenumerated, and the three statements are not one transaction — an R2 delete can never join a SQL one — so a mid-purge failure leaves it partially applied. Both are pinned in d1-storage.test.ts. Neither can escape the requester's own tenant: the range predicate is exact.