Skip to content

Commit cd74b35

Browse files
Stvadclaude
andcommitted
chore(sync): runbook to zero/strip legacy system:-authored blocks
One-time §7 cleanup: strip the system: author prefix and re-stamp 787 legacy pre-c0d606f6 mint rows to the updated_at=0 pristine sentinel, so they re-replicate and converge every device (incl. shadows on uninspectable clients) via the gate's stamp-0 exemption. Transactional (clamp+history triggers toggled off so the 0 lands); backup table for rollback. Must run + converge BEFORE PR #151 merges. Held for a drain window. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 2647588 commit cd74b35

1 file changed

Lines changed: 85 additions & 0 deletions

File tree

scripts/system-author-cleanup.sql

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
-- One-time cleanup of legacy `system:`-authored blocks (pre-c0d606f6 mints).
2+
--
3+
-- EXECUTED 2026-06-14 against prod: 787 rows zeroed + de-prefixed (verified
4+
-- remaining=0, zeroed=787); converged on all bridge-connected devices. The e2ee
5+
-- workspace + mobile converge on their next sync. Rollback source:
6+
-- blocks_system_backup_20260614 (drop it once clients confirm sync).
7+
--
8+
-- Strips the `system:` prefix from `updated_by` (restoring a plain user-pair
9+
-- author) and re-stamps the row to the `updated_at = 0` pristine sentinel, so it
10+
-- re-replicates and converges EVERY device — including any shadowed copy on a
11+
-- client we can't inspect (an e2ee workspace / mobile) — via the stamp-0 exemption.
12+
-- `user_updated_at` is captured BEFORE zeroing so display "last edited" survives.
13+
--
14+
-- WHY ZEROING (not a +1 touch): a 0-stamped server row loses to no nonzero local
15+
-- stamp, so under the CURRENT indiscriminate gate every device yields to it
16+
-- regardless of its local stamp — healing even a competing-newer-default shadow.
17+
--
18+
-- ORDERING: this MUST run, and converge on every device, BEFORE PR #151 (the
19+
-- hardened gate) merges. The hardened gate skip-stales a 0-stamped row over a
20+
-- nonzero local (`local >= 0`), so if it ships first the zero never lands.
21+
--
22+
-- PRE-REQS: every device's upload queue drained (`ps_crud → 0`, incl. the e2ee
23+
-- workspace + mobile) — a queued edit at run time would race the zero. `e2ee_plaintext_risk`
24+
-- below must be 0 (the e2ee ciphertext trigger stays ENABLED and would abort the
25+
-- UPDATE on a plaintext-content row in an e2ee workspace).
26+
--
27+
-- RUN: psql "$PS_DATABASE_URI" -f scripts/system-author-cleanup.sql
28+
-- (connects as the blocks table owner — required to toggle triggers; the
29+
-- Supabase Management API can't run this transactional DDL. $PS_DATABASE_URI
30+
-- is sourced from .env.local.)
31+
32+
\set ON_ERROR_STOP on
33+
34+
-- ── Pre-check (read-only) — eyeball before the write ─────────────────────────
35+
\echo '── pre-check: system: census + e2ee-plaintext risk (must be 0) ──'
36+
SELECT count(*) AS total,
37+
count(*) FILTER (WHERE updated_at <> 0) AS nonzero,
38+
(SELECT count(*) FROM blocks b JOIN workspaces w ON w.id = b.workspace_id
39+
WHERE b.updated_by LIKE 'system:%' AND w.encryption_mode = 'e2ee'
40+
AND b.content NOT LIKE 'enc:v1:%') AS e2ee_plaintext_risk
41+
FROM blocks WHERE updated_by LIKE 'system:%';
42+
43+
-- ── Backup snapshot (rollback source; committed, outside the txn) ────────────
44+
DROP TABLE IF EXISTS blocks_system_backup_20260614;
45+
CREATE TABLE blocks_system_backup_20260614 AS
46+
SELECT id, updated_at, updated_by, user_updated_at
47+
FROM blocks WHERE updated_by LIKE 'system:%';
48+
\echo '── backup rows captured ──'
49+
SELECT count(*) AS backed_up FROM blocks_system_backup_20260614;
50+
51+
-- ── Cleanup (one transaction; DDL is transactional, so a failure rolls back
52+
-- the trigger toggles too and leaves them ENABLED) ──────────────────────────
53+
BEGIN;
54+
ALTER TABLE public.blocks DISABLE TRIGGER blocks_clamp_updated_at_trg; -- else the floor pins updated_at back up and 0 never lands
55+
ALTER TABLE public.blocks DISABLE TRIGGER blocks_record_history_trg; -- don't record a stamp "downgrade" in history
56+
57+
UPDATE blocks
58+
SET user_updated_at = COALESCE(user_updated_at, updated_at), -- preserve display stamp (RHS reads OLD values)
59+
updated_at = 0, -- pristine sentinel
60+
updated_by = substring(updated_by FROM length('system:') + 1) -- strip 'system:' prefix
61+
WHERE updated_by LIKE 'system:%';
62+
63+
ALTER TABLE public.blocks ENABLE TRIGGER blocks_record_history_trg;
64+
ALTER TABLE public.blocks ENABLE TRIGGER blocks_clamp_updated_at_trg;
65+
COMMIT;
66+
67+
-- ── Post-commit verify (expect remaining = 0, zeroed = backed_up) ────────────
68+
\echo '── verify ──'
69+
SELECT
70+
(SELECT count(*) FROM blocks WHERE updated_by LIKE 'system:%') AS remaining_system,
71+
(SELECT count(*) FROM blocks b JOIN blocks_system_backup_20260614 z ON z.id = b.id
72+
WHERE b.updated_at = 0) AS zeroed,
73+
(SELECT count(*) FROM blocks_system_backup_20260614) AS expected;
74+
75+
-- ── Rollback (only if verify is wrong; needs the clamp trigger off again since
76+
-- it restores nonzero stamps over the 0s) ─────────────────────────────────
77+
-- BEGIN;
78+
-- ALTER TABLE public.blocks DISABLE TRIGGER blocks_clamp_updated_at_trg;
79+
-- ALTER TABLE public.blocks DISABLE TRIGGER blocks_record_history_trg;
80+
-- UPDATE blocks b SET updated_at = z.updated_at, updated_by = z.updated_by,
81+
-- user_updated_at = z.user_updated_at
82+
-- FROM blocks_system_backup_20260614 z WHERE z.id = b.id;
83+
-- ALTER TABLE public.blocks ENABLE TRIGGER blocks_record_history_trg;
84+
-- ALTER TABLE public.blocks ENABLE TRIGGER blocks_clamp_updated_at_trg;
85+
-- COMMIT;

0 commit comments

Comments
 (0)