diff --git a/.github/review-media-sql-successor.v1.json b/.github/review-media-sql-successor.v1.json
new file mode 100644
index 0000000000..d60b3b3dcb
--- /dev/null
+++ b/.github/review-media-sql-successor.v1.json
@@ -0,0 +1,348 @@
+{
+ "schemaVersion": 1,
+ "id": "review_media_sql_successor_v1",
+ "purpose": "review-media-current-state-eleven-stage",
+ "projectRef": "aqlcofblfxdrjhhdmarw",
+ "protectedSource": {
+ "remote": "origin",
+ "ref": "refs/heads/main",
+ "repositoryUrl": "https://github.com/twoimo/tzudong.git"
+ },
+ "databaseName": "postgres",
+ "executorRole": "postgres",
+ "serverMajor": 17,
+ "advisoryLockKey": "7311754282260211",
+ "stateRootAlgorithm": "review-media-successor-state/v1",
+ "sourceRoot": "61f9d7637c0714a7b0d9108c6f7959ea820175a7870dba681498b570296a1544",
+ "operatingTransition": {
+ "beforeEleven": 86,
+ "afterEleven": 97
+ },
+ "launchPolicy": {
+ "state": "held",
+ "code": "SUCCESSOR_LAUNCH_HELD",
+ "reason": "protected-release-and-fresh-admission-required"
+ },
+ "terminalContract": {
+ "finishPreimageSha256": "f6c4e71988ed2b789b887484af5fdf2e482bde98f1d1c5b5d4e7ad124c314bd1",
+ "finishPostimageSha256": "a6b4211b2d8486d482f0758ce249eec157249e1a5176be286b76af7e7a775ee9",
+ "requiredFunctionBodySha256": {
+ "public.admin_review_verification_cleanup(uuid,text,text[])": "468c12ee6c3463876cbccb394fcba8f870459a1f6cf3d945d97307f8551f094b",
+ "public.finish_review_media_cleanup()": "a6b4211b2d8486d482f0758ce249eec157249e1a5176be286b76af7e7a775ee9",
+ "public.mutate_review_with_media(uuid,uuid,text,timestamp with time zone,text,text[],text[])": "d5d6646a4294c0e7bd0cd75ea47a996c9c57bac6c32a39ac53fbb7a8b1e7e323",
+ "review_media_private.enqueue_removed()": "cbf6a87e453e8c2e14d67acd73a92e399fbba4b4f24ac27966806e13ef1fd905",
+ "review_media_private.guard_references()": "94113c8ae1231e8eae5a698f35a1370a38367e42c79cc87ac62db76c32e735b1"
+ },
+ "requiredFunctions": [
+ "public.mutate_review_with_media(uuid,uuid,text,timestamp with time zone,text,text[],text[])",
+ "public.queue_review_upload_cleanup(uuid,text[])",
+ "public.read_review_media_commit(uuid,uuid,text)",
+ "public.pending_review_media_cleanup()",
+ "public.finish_review_media_cleanup()",
+ "public.review_media_delete_allowed(text)",
+ "public.review_media_upload_allowed(text,text)",
+ "public.admin_review_verification_cleanup(uuid,text,text[])"
+ ],
+ "requiredPolicies": [
+ "review_media_safe_delete",
+ "review_media_safe_insert",
+ "review_media_safe_update",
+ "review_verifications_owner_read",
+ "review_verifications_owner_insert",
+ "review_verifications_owner_delete",
+ "review_verifications_safe_delete",
+ "review_photos_food_insert",
+ "review_photos_food_update"
+ ]
+ },
+ "migrations": [
+ {
+ "id": "review_media_commit_cleanup",
+ "version": "20261008192455",
+ "name": "review_media_commit_cleanup",
+ "path": "backend/supabase/migrations/20261008192455_review_media_commit_cleanup.sql",
+ "bytes": 17278,
+ "sha256": "1b018884a0ba8bb25e6a9e31fa831e349bd4442118e5d4bf1699464e09e16c3c",
+ "statementCount": 34,
+ "statementVectorSha256": "b90dac7fe72cedb2338c60d85f1852ba429c8395bd6a579797178fb067a887c3",
+ "originalStatementVector": [
+ "-- Atomic review commit receipts and durable, owner-scoped media cleanup.\n-- No scheduler is installed. The authenticated MY/review UI drains this queue.\n-- Tombstones must survive successful cleanup: a delayed write must not revive a\n-- deleted key. No retention period is introduced here.\nBEGIN",
+ "CREATE SCHEMA IF NOT EXISTS review_media_private",
+ "REVOKE ALL ON SCHEMA review_media_private FROM PUBLIC, anon, authenticated",
+ "CREATE TABLE review_media_private.commits (\n operation_id uuid PRIMARY KEY, owner_id uuid NOT NULL, review_id uuid NOT NULL,\n kind text NOT NULL CHECK (kind IN ('edit', 'delete')), request_hash text NOT NULL\n)",
+ "CREATE TABLE review_media_private.cleanup (\n path text PRIMARY KEY, owner_id uuid NOT NULL, review_id uuid NOT NULL,\n purpose text NOT NULL CHECK (purpose IN ('food', 'verification')),\n retired boolean NOT NULL DEFAULT false, complete boolean NOT NULL DEFAULT false\n)",
+ "ALTER TABLE review_media_private.commits ENABLE ROW LEVEL SECURITY",
+ "ALTER TABLE review_media_private.cleanup ENABLE ROW LEVEL SECURITY",
+ "REVOKE ALL ON ALL TABLES IN SCHEMA review_media_private FROM PUBLIC, anon, authenticated",
+ "CREATE FUNCTION review_media_private.canonical(p_path text, p_owner uuid, p_review uuid, p_purpose text)\nRETURNS boolean LANGUAGE sql IMMUTABLE SET search_path = '' AS $$\n SELECT COALESCE(p_purpose IN ('food','verification') AND\n p_path LIKE p_owner::text || '/reviews/' || p_review::text || '/' || p_purpose || '/%' AND\n length(split_part(p_path, '/', 5)) <= 240 AND p_path ~* '^[A-Za-z0-9_-]+/reviews/[A-Za-z0-9_-]+/(food|verification)/[A-Za-z0-9][A-Za-z0-9._-]{0,239}\\.(avif|jpe?g|png|webp)$', false)\n$$",
+ "-- Decode historical public/signed URL values for reference checks and for\n-- cleanup only after the authoritative pre-image passes owner/purpose checks.\nCREATE FUNCTION review_media_private.reference_key(p_value text)\nRETURNS text LANGUAGE plpgsql IMMUTABLE SET search_path = '' AS $$\nDECLARE v text := p_value; b bytea := ''::bytea; i integer := 1;\nBEGIN\n IF v LIKE '%/storage/v1/object/%/review-photos/%' THEN\n v := split_part(split_part(substring(v FROM '/review-photos/(.*)$'), '?', 1), '#', 1);\n WHILE i <= length(v) LOOP\n IF substring(v, i, 1) = '%' THEN\n b := b || decode(substring(v, i + 1, 2), 'hex'); i := i + 3;\n ELSE\n b := b || convert_to(substring(v, i, 1), 'UTF8'); i := i + 1;\n END IF;\n END LOOP;\n RETURN convert_from(b, 'UTF8');\n END IF;\n RETURN v;\nEXCEPTION WHEN OTHERS THEN RETURN p_value;\nEND $$",
+ "-- Historical composer keys are admitted only from the authoritative row\n-- pre-image. Client uploads and compensation still require review-bound keys.\nCREATE FUNCTION review_media_private.owned_legacy(p_path text, p_owner uuid, p_purpose text)\nRETURNS boolean LANGUAGE sql IMMUTABLE SET search_path = '' AS $$\n SELECT COALESCE(split_part(p_path, '/', 1) = p_owner::text AND\n CASE p_purpose\n WHEN 'food' THEN p_path ~ '^[0-9a-f-]{36}/[0-9]{10,16}_food_[0-9]{1,3}_[A-Za-z0-9][A-Za-z0-9._-]{0,200}\\.(avif|jpe?g|png|webp)$'\n WHEN 'verification' THEN p_path ~ '^[0-9a-f-]{36}/[0-9]{10,16}_verification_[A-Za-z0-9][A-Za-z0-9._-]{0,200}\\.(avif|jpe?g|png|webp)$'\n ELSE false END, false)\n$$",
+ "CREATE FUNCTION review_media_private.referenced(p_path text)\nRETURNS boolean LANGUAGE sql VOLATILE SECURITY DEFINER SET search_path = '' AS $$\n SELECT EXISTS (SELECT 1 FROM public.reviews r,\n LATERAL unnest(COALESCE(r.food_photos, ARRAY[]::text[]) || ARRAY[r.verification_photo]) p\n WHERE review_media_private.reference_key(p) = p_path)\n$$",
+ "-- A transaction lock serializes media reference changes and retirement. It is\n-- released BEFORE any Storage HTTP call; retired keys can never be reattached.\n-- Metadata-only moderation/likes do not enter this lock.\nCREATE FUNCTION review_media_private.lock_changes()\nRETURNS trigger LANGUAGE plpgsql SECURITY DEFINER SET search_path = '' AS $$\nBEGIN\n IF current_setting('transaction_isolation') <> 'read committed' THEN\n RAISE EXCEPTION USING MESSAGE = 'REVIEW_RETRY_REQUIRED', ERRCODE = '40001';\n END IF;\n PERFORM pg_advisory_xact_lock(741093, 1);\n RETURN NULL;\nEND $$",
+ "CREATE TRIGGER review_media_serialize BEFORE INSERT OR DELETE OR UPDATE OF food_photos, verification_photo, user_id, id\n ON public.reviews FOR EACH STATEMENT EXECUTE FUNCTION review_media_private.lock_changes()",
+ "CREATE FUNCTION review_media_private.guard_references()\nRETURNS trigger LANGUAGE plpgsql SECURITY DEFINER SET search_path = '' AS $$\nDECLARE p text; old_food text[] := ARRAY[]::text[]; old_verification text; purpose text;\nBEGIN\n IF TG_OP = 'UPDATE' AND NEW.user_id = OLD.user_id AND NEW.id = OLD.id THEN\n old_food := COALESCE(OLD.food_photos, ARRAY[]::text[]);\n old_verification := OLD.verification_photo;\n END IF;\n FOR p, purpose IN SELECT value, 'food' FROM unnest(COALESCE(NEW.food_photos, ARRAY[]::text[])) value\n UNION ALL SELECT NEW.verification_photo, 'verification' LOOP\n IF EXISTS (SELECT 1 FROM review_media_private.cleanup c\n WHERE c.path = review_media_private.reference_key(p) AND c.retired) THEN\n RAISE EXCEPTION USING MESSAGE = 'REVIEW_MEDIA_RETIRED', ERRCODE = '23514';\n END IF;\n -- Historical keys may remain unchanged in their original purpose only.\n IF NOT COALESCE(CASE WHEN purpose = 'food' THEN p = ANY(old_food) ELSE p = old_verification END, false) THEN\n IF NOT review_media_private.canonical(p, NEW.user_id, NEW.id, purpose) THEN\n RAISE EXCEPTION USING MESSAGE = 'REVIEW_MEDIA_INVALID', ERRCODE = '23514';\n END IF;\n END IF;\n END LOOP;\n RETURN NEW;\nEND $$",
+ "CREATE TRIGGER review_media_reference_guard BEFORE INSERT OR UPDATE OF food_photos, verification_photo, user_id, id\n ON public.reviews FOR EACH ROW EXECUTE FUNCTION review_media_private.guard_references()",
+ "-- Every delete path (including legacy and guarded admin) captures OLD, not an\n-- actor-supplied path list. The statement trigger already holds the media lock.\nCREATE FUNCTION review_media_private.enqueue_removed()\nRETURNS trigger LANGUAGE plpgsql SECURITY DEFINER SET search_path = '' AS $$\nDECLARE p text; purpose text; retained text[] := ARRAY[]::text[];\nBEGIN\n IF TG_OP = 'UPDATE' THEN\n retained := COALESCE(NEW.food_photos, ARRAY[]::text[]) || ARRAY[NEW.verification_photo];\n END IF;\n FOR p, purpose IN SELECT review_media_private.reference_key(value), 'food'\n FROM unnest(COALESCE(OLD.food_photos, ARRAY[]::text[])) value\n UNION ALL SELECT review_media_private.reference_key(OLD.verification_photo), 'verification' LOOP\n IF (review_media_private.canonical(p, OLD.user_id, OLD.id, purpose)\n OR review_media_private.owned_legacy(p, OLD.user_id, purpose))\n AND NOT EXISTS (SELECT 1 FROM unnest(retained) value\n WHERE review_media_private.reference_key(value) = p) THEN\n INSERT INTO review_media_private.cleanup(path, owner_id, review_id, purpose, retired)\n VALUES (p, OLD.user_id, OLD.id, purpose, NOT review_media_private.referenced(p))\n ON CONFLICT DO NOTHING;\n END IF;\n END LOOP;\n RETURN NULL;\nEND $$",
+ "CREATE TRIGGER review_media_enqueue_removed AFTER DELETE OR UPDATE OF food_photos, verification_photo, user_id, id\n ON public.reviews FOR EACH ROW EXECUTE FUNCTION review_media_private.enqueue_removed()",
+ "-- Definer is required for the private receipt/queue and global reference check,\n-- not to broaden review access. Owner authorization is checked before any row\n-- mutation. Existing review triggers, cascades and RLS policies are unchanged.\nCREATE FUNCTION public.mutate_review_with_media(\n p_operation_id uuid, p_review_id uuid, p_kind text, p_expected_updated_at timestamptz,\n p_content text DEFAULT NULL, p_categories text[] DEFAULT NULL, p_food_photos text[] DEFAULT NULL\n) RETURNS text LANGUAGE plpgsql SECURITY DEFINER SET search_path = '' AS $$\nDECLARE u uuid := auth.uid(); r public.reviews%ROWTYPE;\n receipt review_media_private.commits%ROWTYPE; h text; p text;\nBEGIN\n IF u IS NULL THEN RETURN 'REVIEW_UNAUTHORIZED'; END IF;\n IF p_operation_id IS NULL OR p_review_id IS NULL OR p_kind IS NULL OR p_kind NOT IN ('edit','delete') THEN\n RETURN 'REVIEW_INVALID';\n END IF;\n -- Bound client-controlled work before hashing or acquiring the shared lock.\n -- These generous transport ceilings do not trim stored or submitted text.\n IF p_kind = 'delete' AND (p_content IS NOT NULL OR p_categories IS NOT NULL OR p_food_photos IS NOT NULL) THEN\n RETURN 'REVIEW_INVALID';\n END IF;\n IF p_kind = 'edit' THEN\n IF p_content IS NULL OR octet_length(p_content) > 262144 OR length(btrim(p_content)) < 20\n OR p_categories IS NULL OR cardinality(p_categories) NOT BETWEEN 1 AND 100\n OR p_food_photos IS NULL OR cardinality(p_food_photos) NOT BETWEEN 1 AND 10 THEN\n RETURN 'REVIEW_INVALID';\n END IF;\n IF EXISTS (SELECT 1 FROM unnest(p_categories) value WHERE value IS NULL OR length(value) NOT BETWEEN 1 AND 200)\n OR EXISTS (SELECT 1 FROM unnest(p_food_photos) value WHERE value IS NULL OR octet_length(value) > 8192) THEN\n RETURN 'REVIEW_INVALID';\n END IF;\n END IF;\n IF current_setting('transaction_isolation') <> 'read committed' THEN RETURN 'REVIEW_RETRY_REQUIRED'; END IF;\n h := encode(sha256(convert_to(jsonb_build_array(p_review_id, p_kind, p_expected_updated_at,\n p_content, p_categories, p_food_photos)::text, 'UTF8')), 'hex');\n PERFORM pg_advisory_xact_lock(741093, 1);\n SELECT * INTO receipt FROM review_media_private.commits WHERE operation_id = p_operation_id;\n IF FOUND THEN\n IF receipt.owner_id = u AND receipt.review_id = p_review_id AND receipt.kind = p_kind AND receipt.request_hash = h THEN\n RETURN 'REVIEW_COMMITTED';\n END IF;\n RETURN 'REVIEW_CONFLICT';\n END IF;\n SELECT * INTO r FROM public.reviews WHERE id = p_review_id AND user_id = u FOR UPDATE;\n IF NOT FOUND THEN RETURN 'REVIEW_NOT_FOUND'; END IF;\n IF p_expected_updated_at IS NULL OR r.updated_at IS DISTINCT FROM p_expected_updated_at THEN RETURN 'REVIEW_CONFLICT'; END IF;\n IF p_kind = 'edit' THEN\n IF p_content IS NULL OR length(btrim(p_content)) < 20 OR p_categories IS NULL OR cardinality(p_categories) = 0\n OR p_food_photos IS NULL OR cardinality(p_food_photos) NOT BETWEEN 1 AND 10 THEN RETURN 'REVIEW_INVALID'; END IF;\n FOREACH p IN ARRAY p_food_photos LOOP\n -- The submitted list is the complete desired state. Historical display\n -- values can only be kept verbatim from this row or omitted, never added.\n IF p = ANY(COALESCE(r.food_photos, ARRAY[]::text[]))\n AND NOT review_media_private.canonical(p, u, r.id, 'food')\n AND NOT review_media_private.owned_legacy(p, u, 'food') THEN CONTINUE; END IF;\n IF NOT review_media_private.canonical(p, u, r.id, 'food')\n AND NOT (review_media_private.owned_legacy(p, u, 'food') AND p = ANY(COALESCE(r.food_photos, ARRAY[]::text[]))) THEN\n RETURN 'REVIEW_INVALID';\n END IF;\n IF NOT EXISTS (SELECT 1 FROM storage.objects WHERE bucket_id = 'review-photos' AND name = p) THEN\n RETURN 'REVIEW_MEDIA_MISSING';\n END IF;\n END LOOP;\n UPDATE public.reviews SET content = btrim(p_content), categories = p_categories,\n food_photos = p_food_photos,\n is_verified = false, admin_note = NULL, updated_at = clock_timestamp()\n WHERE id = r.id AND user_id = u;\n ELSE\n DELETE FROM public.reviews WHERE id = r.id AND user_id = u;\n END IF;\n -- The AFTER trigger queues the authoritative pre-image in this transaction.\n INSERT INTO review_media_private.commits VALUES (p_operation_id, u, r.id, p_kind, h);\n RETURN 'REVIEW_COMMITTED';\nEND $$",
+ "-- Compensation requests are authority-checked and serialized too. A live\n-- reference is never retired, even if the caller incorrectly reports failure.\nCREATE FUNCTION public.queue_review_upload_cleanup(p_review_id uuid, p_paths text[])\nRETURNS text LANGUAGE plpgsql SECURITY DEFINER SET search_path = '' AS $$\nDECLARE u uuid := auth.uid(); p text;\nBEGIN\n IF u IS NULL THEN RETURN 'REVIEW_UNAUTHORIZED'; END IF;\n IF current_setting('transaction_isolation') <> 'read committed' THEN RETURN 'REVIEW_RETRY_REQUIRED'; END IF;\n IF p_review_id IS NULL OR p_paths IS NULL OR cardinality(p_paths) > 10 THEN RETURN 'REVIEW_INVALID'; END IF;\n FOREACH p IN ARRAY p_paths LOOP\n IF NOT review_media_private.canonical(p, u, p_review_id, 'food') THEN RETURN 'REVIEW_INVALID'; END IF;\n END LOOP;\n PERFORM pg_advisory_xact_lock(741093, 1);\n FOREACH p IN ARRAY p_paths LOOP\n IF NOT review_media_private.referenced(p) THEN\n INSERT INTO review_media_private.cleanup(path, owner_id, review_id, purpose, retired)\n VALUES (p, u, p_review_id, 'food', true) ON CONFLICT DO NOTHING;\n END IF;\n END LOOP;\n RETURN 'REVIEW_CLEANUP_QUEUED';\nEND $$",
+ "CREATE FUNCTION public.read_review_media_commit(p_operation_id uuid, p_review_id uuid, p_kind text)\nRETURNS text LANGUAGE sql SECURITY DEFINER SET search_path = '' AS $$\n SELECT CASE WHEN EXISTS (SELECT 1 FROM review_media_private.commits\n WHERE operation_id = p_operation_id AND review_id = p_review_id AND kind = p_kind AND owner_id = auth.uid())\n THEN 'REVIEW_COMMITTED' ELSE 'REVIEW_NOT_CONFIRMED' END\n$$",
+ "CREATE FUNCTION public.pending_review_media_cleanup()\nRETURNS TABLE(path text, owner_id uuid, review_id uuid, purpose text)\nLANGUAGE plpgsql SECURITY DEFINER SET search_path = '' AS $$\nBEGIN\n IF auth.uid() IS NULL OR current_setting('transaction_isolation') <> 'read committed' THEN RETURN; END IF;\n PERFORM pg_advisory_xact_lock(741093, 1);\n UPDATE review_media_private.cleanup c SET retired = true\n WHERE c.owner_id = auth.uid() AND NOT c.retired AND NOT review_media_private.referenced(c.path);\n RETURN QUERY SELECT c.path, c.owner_id, c.review_id, c.purpose FROM review_media_private.cleanup c\n WHERE c.owner_id = auth.uid() AND c.retired AND NOT c.complete\n AND NOT review_media_private.referenced(c.path) ORDER BY c.path LIMIT 20;\nEND $$",
+ "CREATE FUNCTION public.finish_review_media_cleanup()\nRETURNS bigint LANGUAGE plpgsql SECURITY DEFINER SET search_path = '' AS $$\nBEGIN\n IF auth.uid() IS NULL THEN RETURN NULL; END IF;\n UPDATE review_media_private.cleanup c SET complete = true\n WHERE c.owner_id = auth.uid() AND c.retired AND NOT c.complete\n AND NOT EXISTS (SELECT 1 FROM storage.objects o WHERE o.bucket_id = 'review-photos' AND o.name = c.path);\n RETURN (SELECT count(*) FROM review_media_private.cleanup c WHERE c.owner_id = auth.uid() AND NOT c.complete);\nEND $$",
+ "-- A restrictive policy also protects direct browser compensation by older\n-- clients: successful HTTP delete responses alone cannot bypass live references.\nCREATE FUNCTION public.review_media_delete_allowed(p_path text)\nRETURNS boolean LANGUAGE plpgsql VOLATILE SECURITY DEFINER SET search_path = '' AS $$\nDECLARE u uuid := auth.uid(); review_id uuid; purpose text;\nBEGIN\n IF u IS NULL OR current_setting('transaction_isolation') <> 'read committed' THEN RETURN false; END IF;\n IF octet_length(p_path) > 512 THEN RETURN false; END IF;\n -- Existing composer compensation also uses Storage.remove directly. Admit\n -- only canonical orphan keys, retiring them atomically before that delete;\n -- a delayed insert/update then fails rather than referring to deleted media.\n PERFORM pg_advisory_xact_lock(741093, 1);\n IF EXISTS (SELECT 1 FROM review_media_private.cleanup c WHERE c.path = p_path\n AND c.owner_id = u AND c.retired AND NOT c.complete\n AND review_media_private.owned_legacy(p_path, u, c.purpose)) THEN\n RETURN NOT review_media_private.referenced(p_path);\n END IF;\n BEGIN review_id := split_part(p_path, '/', 3)::uuid;\n EXCEPTION WHEN invalid_text_representation THEN RETURN false; END;\n purpose := split_part(p_path, '/', 4);\n IF NOT review_media_private.canonical(p_path, u, review_id, purpose) THEN RETURN false; END IF;\n IF review_media_private.referenced(p_path) THEN RETURN false; END IF;\n INSERT INTO review_media_private.cleanup(path, owner_id, review_id, purpose, retired)\n VALUES (p_path, u, review_id, purpose, true)\n ON CONFLICT (path) DO UPDATE SET retired = true;\n RETURN true;\nEND $$",
+ "CREATE POLICY review_media_safe_delete ON storage.objects AS RESTRICTIVE FOR DELETE TO authenticated\n USING (bucket_id <> 'review-photos' OR public.review_media_delete_allowed(name))",
+ "REVOKE ALL ON ALL FUNCTIONS IN SCHEMA review_media_private FROM PUBLIC, anon, authenticated",
+ "REVOKE ALL ON FUNCTION public.mutate_review_with_media(uuid,uuid,text,timestamptz,text,text[],text[]) FROM PUBLIC, anon",
+ "REVOKE ALL ON FUNCTION public.queue_review_upload_cleanup(uuid,text[]) FROM PUBLIC, anon",
+ "REVOKE ALL ON FUNCTION public.read_review_media_commit(uuid,uuid,text) FROM PUBLIC, anon",
+ "REVOKE ALL ON FUNCTION public.pending_review_media_cleanup() FROM PUBLIC, anon",
+ "REVOKE ALL ON FUNCTION public.finish_review_media_cleanup() FROM PUBLIC, anon",
+ "REVOKE ALL ON FUNCTION public.review_media_delete_allowed(text) FROM PUBLIC, anon",
+ "GRANT EXECUTE ON FUNCTION public.mutate_review_with_media(uuid,uuid,text,timestamptz,text,text[],text[]),\n public.queue_review_upload_cleanup(uuid,text[]), public.read_review_media_commit(uuid,uuid,text), public.pending_review_media_cleanup(),\n public.finish_review_media_cleanup(), public.review_media_delete_allowed(text) TO authenticated",
+ "COMMIT"
+ ]
+ },
+ {
+ "id": "review_verification_private",
+ "version": "20261008200719",
+ "name": "review_verification_private",
+ "path": "backend/supabase/migrations/20261008200719_review_verification_private.sql",
+ "bytes": 5281,
+ "sha256": "92b469ab0ed1a23bf91c1ab5a3f8e92d45cf0f4e06e5fa602d6a34ad0497a51b",
+ "statementCount": 17,
+ "statementVectorSha256": "3d2e3409b2a271362fb8543b2357bcacece7a990f7960b74ae0807773854f664",
+ "originalStatementVector": [
+ "-- Verification images are private; food photos retain their public contract.\n-- Historical verification objects must be moved with the Storage API, followed\n-- by metadata and public-denial readback. This DDL does not claim that transfer.\nBEGIN",
+ "INSERT INTO storage.buckets(id, name, public, file_size_limit, allowed_mime_types)\nVALUES ('review-verifications', 'review-verifications', false, 5242880,\n ARRAY['image/jpeg','image/png','image/webp','image/avif']::text[])\nON CONFLICT (id) DO UPDATE SET public = false, file_size_limit = EXCLUDED.file_size_limit,\n allowed_mime_types = EXCLUDED.allowed_mime_types",
+ "-- Serialize Storage writes with review references and retirement. A private\n-- object must not shadow a referenced public receipt, and a completed tombstone\n-- must continue to deny delayed uploads. Storage upsert/update uses both checks.\nCREATE FUNCTION public.review_media_upload_allowed(p_bucket text, p_path text)\nRETURNS boolean LANGUAGE plpgsql VOLATILE SECURITY DEFINER SET search_path = '' AS $$\nDECLARE u uuid := auth.uid(); review_id uuid; purpose text;\nBEGIN\n IF u IS NULL OR current_setting('transaction_isolation') <> 'read committed'\n OR p_bucket IS NULL OR p_bucket NOT IN ('review-photos','review-verifications')\n OR p_path IS NULL OR octet_length(p_path) > 512 THEN RETURN false; END IF;\n BEGIN review_id := split_part(p_path, '/', 3)::uuid;\n EXCEPTION WHEN invalid_text_representation THEN RETURN false; END;\n purpose := CASE p_bucket WHEN 'review-photos' THEN 'food' ELSE 'verification' END;\n IF NOT review_media_private.canonical(p_path, u, review_id, purpose) THEN RETURN false; END IF;\n PERFORM pg_advisory_xact_lock(741093, 1);\n RETURN NOT EXISTS (SELECT 1 FROM review_media_private.cleanup c WHERE c.path = p_path AND c.retired)\n AND NOT review_media_private.referenced(p_path);\nEND $$",
+ "REVOKE ALL ON FUNCTION public.review_media_upload_allowed(text,text) FROM PUBLIC, anon",
+ "GRANT EXECUTE ON FUNCTION public.review_media_upload_allowed(text,text) TO authenticated",
+ "CREATE POLICY review_media_safe_insert ON storage.objects AS RESTRICTIVE FOR INSERT TO authenticated\n WITH CHECK (bucket_id NOT IN ('review-photos','review-verifications') OR public.review_media_upload_allowed(bucket_id,name))",
+ "CREATE POLICY review_media_safe_update ON storage.objects AS RESTRICTIVE FOR UPDATE TO authenticated\n USING (bucket_id NOT IN ('review-photos','review-verifications') OR public.review_media_upload_allowed(bucket_id,name))\n WITH CHECK (bucket_id NOT IN ('review-photos','review-verifications') OR public.review_media_upload_allowed(bucket_id,name))",
+ "CREATE POLICY review_verifications_owner_read ON storage.objects FOR SELECT TO authenticated\n USING (bucket_id = 'review-verifications' AND\n ((storage.foldername(name))[1] = (SELECT auth.uid()::text)\n OR public.has_role((SELECT auth.uid()), 'admin'::public.app_role)))",
+ "CREATE POLICY review_verifications_owner_insert ON storage.objects FOR INSERT TO authenticated\n WITH CHECK (bucket_id = 'review-verifications' AND\n (storage.foldername(name))[1] = (SELECT auth.uid()::text)\n AND split_part(name, '/', 4) = 'verification')",
+ "CREATE POLICY review_verifications_owner_delete ON storage.objects FOR DELETE TO authenticated\n USING (bucket_id = 'review-verifications' AND\n (storage.foldername(name))[1] = (SELECT auth.uid()::text))",
+ "CREATE POLICY review_verifications_safe_delete ON storage.objects AS RESTRICTIVE FOR DELETE TO authenticated\n USING (bucket_id <> 'review-verifications' OR public.review_media_delete_allowed(name))",
+ "-- Old browser composers must fail before publishing a verification image into\n-- the public bucket. Public food uploads keep the current policy unchanged.\nCREATE POLICY review_photos_food_insert ON storage.objects AS RESTRICTIVE FOR INSERT TO authenticated\n WITH CHECK (bucket_id <> 'review-photos' OR (split_part(name, '/', 4) = 'food'\n AND (storage.foldername(name))[1] = (SELECT auth.uid()::text)))",
+ "CREATE POLICY review_photos_food_update ON storage.objects AS RESTRICTIVE FOR UPDATE TO authenticated\n USING (bucket_id <> 'review-photos' OR (split_part(name, '/', 4) = 'food'\n AND (storage.foldername(name))[1] = (SELECT auth.uid()::text)))\n WITH CHECK (bucket_id <> 'review-photos' OR (split_part(name, '/', 4) = 'food'\n AND (storage.foldername(name))[1] = (SELECT auth.uid()::text)))",
+ "CREATE OR REPLACE FUNCTION public.finish_review_media_cleanup()\nRETURNS bigint LANGUAGE plpgsql SECURITY DEFINER SET search_path = '' AS $$\nBEGIN\n IF auth.uid() IS NULL THEN RETURN NULL; END IF;\n UPDATE review_media_private.cleanup c SET complete = true\n WHERE c.owner_id = auth.uid() AND c.retired AND NOT c.complete\n AND NOT EXISTS (SELECT 1 FROM storage.objects o\n WHERE o.bucket_id = CASE c.purpose WHEN 'verification' THEN 'review-verifications' ELSE 'review-photos' END\n AND o.name = c.path)\n AND NOT EXISTS (SELECT 1 FROM storage.objects o WHERE o.bucket_id = 'review-photos' AND o.name = c.path);\n RETURN (SELECT count(*) FROM review_media_private.cleanup c WHERE c.owner_id = auth.uid() AND NOT c.complete);\nEND $$",
+ "REVOKE ALL ON FUNCTION public.finish_review_media_cleanup() FROM PUBLIC, anon",
+ "GRANT EXECUTE ON FUNCTION public.finish_review_media_cleanup() TO authenticated",
+ "COMMIT"
+ ]
+ },
+ {
+ "id": "review_media_catalog_integration",
+ "version": "20261008201635",
+ "name": "review_media_catalog_integration",
+ "path": "backend/supabase/migrations/20261008201635_review_media_catalog_integration.sql",
+ "bytes": 47177,
+ "sha256": "6e820eb535bc514e78f258110e36dff00537bcaefcffd8b354356107ca9c09fe",
+ "statementCount": 11,
+ "statementVectorSha256": "e69060e11abab0564f63decd2ba601630d13724c0a71b2f6cb2c616c0bb03e9c",
+ "originalStatementVector": [
+ "-- Integrate exactly the two preceding review-media sources into G014.\n-- CLI 2.117.0 generated this new migration. No assertion body or immutable\n-- manifest row is changed. Execute the complete transaction; do not split it.\nBEGIN",
+ "SET LOCAL lock_timeout = '2s'",
+ "SELECT pg_catalog.pg_advisory_xact_lock(pg_catalog.hashtextextended('tzudong:review-media-catalog:v1', 0))",
+ "CREATE TEMP TABLE review_media_expected (\n signature text PRIMARY KEY, body_sha256 text NOT NULL, final_body_sha256 text NOT NULL, definer boolean NOT NULL,\n volatility text NOT NULL, language text NOT NULL, result text NOT NULL, argument_names text[] NOT NULL\n) ON COMMIT DROP",
+ "INSERT INTO review_media_expected VALUES\n ('public.finish_review_media_cleanup()','f7e925a25dedb987a3166c5ca641ba7db1568a7583bdeca3cee04f27d9189f20','f6c4e71988ed2b789b887484af5fdf2e482bde98f1d1c5b5d4e7ad124c314bd1',true,'v','plpgsql','bigint',ARRAY[]::text[]),\n ('public.mutate_review_with_media(uuid,uuid,text,timestamp with time zone,text,text[],text[])','6f1450d4afaa7d3e9dcba3abb231de62aeb7c0a687cc2a9ceafbbe6dc2c2e413','50bfad7bc4fc7ddbc9d9379e8eb13facd938a15f66553576cc69fbcf69aa98b1',true,'v','plpgsql','text',ARRAY['p_operation_id','p_review_id','p_kind','p_expected_updated_at','p_content','p_categories','p_food_photos']::text[]),\n ('public.pending_review_media_cleanup()','21fa71446354fe855508550a3df78ddbe0ad4c1f7007ea905d04bdb98f1ac766','5a92c8f282c895eabaa77f8cf3f452fb659bac33bd519ed8cf439dc1f2905c7b',true,'v','plpgsql','TABLE(path text, owner_id uuid, review_id uuid, purpose text)',ARRAY[]::text[]),\n ('public.queue_review_upload_cleanup(uuid,text[])','00784e9daa9b6b707432200e4df6592010e9f22d2bc9db831bf8329e14bdb453','89c3b573891f598cac5df6ce9a3217639092801a7439ec0404f338f2097114a8',true,'v','plpgsql','text',ARRAY['p_review_id','p_paths']::text[]),\n ('public.read_review_media_commit(uuid,uuid,text)','c7d2e4e111223150ceeda5893310df0e9656e5930823db053135812a69222081','2f14abfc04864f369467f5ac5f0603defa149b8023d7e6b04e2bb8535f8a268e',true,'v','sql','text',ARRAY['p_operation_id','p_review_id','p_kind']::text[]),\n ('public.review_media_delete_allowed(text)','45fd396f240fb1d9fc084f31bd4feea7ee097017f2fd2b8d12df9c2c59152f78','a75dbd787dd388b0761bee11c82a56d5f410688e4cfd953181bce4d613062ae7',true,'v','plpgsql','boolean',ARRAY['p_path']::text[]),\n ('public.review_media_upload_allowed(text,text)','0a3d375685c5e3fd82721bd8033345c93031704c838a0a7f1b61ff248d9b6636','298c6a127f3a44a193b78be8c1f2577ab58aa34c772b66ccd53f774d807cc90c',true,'v','plpgsql','boolean',ARRAY['p_bucket','p_path']::text[]),\n ('review_media_private.canonical(text,uuid,uuid,text)','87d8397c36cf4c7b986f75cbc2788979333a76be2cc26dc5522cd362f62414a9','87d8397c36cf4c7b986f75cbc2788979333a76be2cc26dc5522cd362f62414a9',false,'i','sql','boolean',ARRAY['p_path','p_owner','p_review','p_purpose']::text[]),\n ('review_media_private.enqueue_removed()','7edb21d70ea3ff81ab90e13617e8c1c2749b54b1506f1ff42aaa6c5dc33f9e38','7edb21d70ea3ff81ab90e13617e8c1c2749b54b1506f1ff42aaa6c5dc33f9e38',true,'v','plpgsql','trigger',ARRAY[]::text[]),\n ('review_media_private.guard_references()','846030547ffd76e17596755d7eee4279dcb9b563361435260826071d2f250d59','846030547ffd76e17596755d7eee4279dcb9b563361435260826071d2f250d59',true,'v','plpgsql','trigger',ARRAY[]::text[]),\n ('review_media_private.lock_changes()','381d89aa6bbf5a59c2ee0fef2a7392ef705147252b324527201b8b6f74376250','381d89aa6bbf5a59c2ee0fef2a7392ef705147252b324527201b8b6f74376250',true,'v','plpgsql','trigger',ARRAY[]::text[]),\n ('review_media_private.owned_legacy(text,uuid,text)','a2d55f09f03fcaeedd77461198511673da7342a6a4873d37227f9cf612090980','a2d55f09f03fcaeedd77461198511673da7342a6a4873d37227f9cf612090980',false,'i','sql','boolean',ARRAY['p_path','p_owner','p_purpose']::text[]),\n ('review_media_private.reference_key(text)','a8910cb09bfd959951045e8b0b8233b1e2a8bdc45073abeebdfc26b552b1dae7','a8910cb09bfd959951045e8b0b8233b1e2a8bdc45073abeebdfc26b552b1dae7',false,'i','plpgsql','text',ARRAY['p_value']::text[]),\n ('review_media_private.referenced(text)','cc295115d85a3a11b380e1378e2ee91b0d9f7f587c68839653f54ed826c398ce','cc295115d85a3a11b380e1378e2ee91b0d9f7f587c68839653f54ed826c398ce',true,'v','sql','boolean',ARRAY['p_path']::text[])",
+ "REVOKE ALL ON pg_temp.review_media_expected FROM PUBLIC, anon, authenticated, service_role",
+ "GRANT SELECT ON pg_temp.review_media_expected TO privacy_workflow_owner",
+ "DO $integration$\nDECLARE\n v_expected record; v_function record; v_oid oid;\n v_owner oid := 'privacy_workflow_owner'::regrole;\n v_runner oid := 'postgres'::regrole;\n v_members jsonb; v_assertions jsonb; v_manifest jsonb; v_functions jsonb;\n v_known oid := (SELECT p.oid FROM pg_proc p JOIN pg_namespace n ON n.oid=p.pronamespace\n WHERE n.nspname='privacy_retention' AND p.proname='g014_catalog_protected_relations' AND p.pronargs=0);\n v_definition text;\n v_public jsonb;\n v_self jsonb; v_major integer := current_setting('server_version_num')::integer / 10000;\n v_pg15_lease boolean := false;\nBEGIN\n IF current_user <> 'postgres' OR session_user <> 'postgres' OR v_major NOT IN (15,17)\n OR current_setting('transaction_read_only') <> 'off'\n OR NOT EXISTS (SELECT 1 FROM pg_roles WHERE oid=v_owner AND NOT rolsuper\n AND NOT rolbypassrls AND NOT rolcanlogin AND NOT rolinherit) THEN\n RAISE EXCEPTION 'review_media_catalog_executor_denied';\n END IF;\n SELECT coalesce(jsonb_agg(to_jsonb(m) ORDER BY roleid,member,grantor),'[]')\n INTO v_members FROM pg_auth_members m;\n SELECT to_jsonb(n) INTO v_public FROM pg_namespace n WHERE nspname='public';\n IF NOT has_schema_privilege(v_owner,'public','CREATE') THEN\n RAISE EXCEPTION 'review_media_catalog_existing_public_create_missing';\n END IF;\n SELECT to_jsonb(m) INTO v_self FROM pg_auth_members m\n WHERE roleid=v_owner AND member=v_runner AND grantor=v_runner;\n IF v_self IS NOT NULL AND (v_self->>'admin_option')::boolean THEN\n RAISE EXCEPTION 'review_media_catalog_membership_denied';\n END IF;\n SELECT jsonb_agg(CASE WHEN p.oid=v_known THEN to_jsonb(p)-'prosrc' ELSE to_jsonb(p) END ORDER BY p.oid) INTO v_assertions FROM pg_proc p\n WHERE p.pronamespace='privacy_retention'::regnamespace;\n -- Pre-existing RPC/helper bodies and complete type/default metadata are pinned\n -- before any ownership or grant change. No unknown overload is admitted.\n FOR v_expected IN SELECT * FROM pg_temp.review_media_expected LOOP\n v_oid := to_regprocedure(v_expected.signature);\n SELECT * INTO v_function FROM pg_proc WHERE oid=v_oid;\n IF v_oid IS NULL OR v_function.proowner<>v_runner OR v_function.prokind<>'f'\n OR v_function.prosecdef IS DISTINCT FROM v_expected.definer OR v_function.provolatile::text<>v_expected.volatility\n OR v_function.proconfig IS DISTINCT FROM ARRAY['search_path=\"\"']::text[]\n OR (SELECT lanname FROM pg_language WHERE oid=v_function.prolang)<>v_expected.language\n OR pg_get_function_result(v_oid) IS DISTINCT FROM v_expected.result\n OR coalesce(v_function.proargnames[1:v_function.pronargs],ARRAY[]::text[]) IS DISTINCT FROM v_expected.argument_names\n OR encode(sha256(convert_to(v_function.prosrc,'UTF8')),'hex')<>v_expected.body_sha256\n OR v_function.pronargdefaults<>(CASE WHEN v_function.proname='mutate_review_with_media' THEN 3 ELSE 0 END)\n OR (v_function.pronargdefaults=3 AND pg_get_expr(v_function.proargdefaults,0)<>'NULL::text, NULL::text[], NULL::text[]')\n OR (SELECT count(*) FROM pg_proc x WHERE x.pronamespace=v_function.pronamespace AND x.proname=v_function.proname)<>1 THEN\n RAISE EXCEPTION 'review_media_catalog_source_drift';\n END IF;\n END LOOP;\n IF (SELECT count(*) FROM pg_proc WHERE pronamespace='review_media_private'::regnamespace)<>7 THEN\n RAISE EXCEPTION 'review_media_catalog_identity_conflict';\n END IF;\n -- Preserve semantic defaults. PG15 pg_node_tree contains parser location\n -- offsets that change when the identical definition is recreated. The initial\n -- exact source/default checks above remain mandatory; retain every other field.\n SELECT jsonb_agg(((to_jsonb(p)-'proowner'-'proacl'-'prosrc'-'proargdefaults') || jsonb_build_object('proargdefaults',pg_get_expr(p.proargdefaults,0))) ORDER BY p.oid) INTO v_functions\n FROM pg_proc p JOIN pg_temp.review_media_expected e ON p.oid=to_regprocedure(e.signature);\n\n -- Use only the existing G014 owner-management authority. This transaction's\n -- noninheriting SET lease is restored exactly before unchanged assertions run.\n -- Never grant a Storage/service role or alter supautils/global configuration.\n IF v_major=17 AND NOT EXISTS (SELECT 1 FROM pg_auth_members\n WHERE roleid=v_owner AND member=v_runner AND admin_option AND grantor<>v_runner) THEN\n RAISE EXCEPTION 'review_media_catalog_existing_owner_admin_missing';\n END IF;\n IF v_major=17 THEN\n EXECUTE 'GRANT privacy_workflow_owner TO postgres WITH ADMIN FALSE, INHERIT FALSE, SET TRUE GRANTED BY postgres';\n ELSE\n -- PG15 has one row per role/member pair, even when another grantor owns it.\n -- Lease only absent membership; never revoke a pre-existing grantor's row.\n v_pg15_lease := NOT pg_has_role(v_runner,v_owner,'MEMBER');\n IF v_pg15_lease THEN GRANT privacy_workflow_owner TO postgres; END IF;\n END IF;\n SET LOCAL ROLE privacy_workflow_owner;\n -- Existing G041 claim boundary: never restore auth-schema access for the\n -- workflow owner. Pin the unchanged helper before substituting auth.uid().\n IF has_schema_privilege(v_owner,'auth','USAGE,CREATE') OR NOT EXISTS (\n SELECT 1 FROM pg_proc p JOIN pg_namespace n ON n.oid=p.pronamespace\n WHERE n.nspname='privacy_retention' AND p.proname='g041_current_claim_user_id'\n AND p.pronargs=0 AND p.proowner=v_owner AND p.prosecdef AND p.prokind='f'\n AND NOT p.proretset AND p.prorettype='uuid'::regtype AND p.provolatile='s'\n AND p.prolang=(SELECT oid FROM pg_language WHERE lanname='plpgsql')\n AND p.proconfig=ARRAY['search_path=\"\"']::text[]\n AND encode(sha256(convert_to(p.prosrc,'UTF8')),'hex')='43f3ff2ed914944802555c4ffe3ff8ecece213e82288e2c1c47ae22efdf81585'\n AND has_function_privilege(v_owner,p.oid,'EXECUTE')\n AND NOT has_function_privilege('anon',p.oid,'EXECUTE')\n AND NOT has_function_privilege('authenticated',p.oid,'EXECUTE')\n AND NOT has_function_privilege('service_role',p.oid,'EXECUTE')\n ) THEN RAISE EXCEPTION 'review_media_catalog_claim_boundary_drift'; END IF;\n IF EXISTS (SELECT 1 FROM privacy_retention.g014_public_rpc_allowlist a\n JOIN pg_temp.review_media_expected e ON a.function_name=split_part(split_part(e.signature,'(',1),'.',2)\n WHERE a.function_schema='public') THEN\n RAISE EXCEPTION 'review_media_catalog_identity_conflict';\n END IF;\n SELECT jsonb_agg(to_jsonb(m) ORDER BY manifest_kind,manifest_key) INTO v_manifest\n FROM privacy_retention.g014_catalog_contract_manifest m;\n PERFORM privacy_retention.assert_g014_catalog_manifest();\n RESET ROLE;\n GRANT USAGE, CREATE ON SCHEMA review_media_private TO privacy_workflow_owner;\n FOR v_expected IN SELECT * FROM pg_temp.review_media_expected LOOP\n EXECUTE format('ALTER FUNCTION %s OWNER TO privacy_workflow_owner',to_regprocedure(v_expected.signature));\n SET LOCAL ROLE privacy_workflow_owner;\n IF v_expected.signature LIKE 'public.%' THEN\n SELECT pg_get_functiondef(oid) INTO v_definition FROM pg_proc WHERE oid=to_regprocedure(v_expected.signature);\n EXECUTE replace(v_definition,'auth.uid()','privacy_retention.g041_current_claim_user_id()');\n END IF;\n IF (SELECT encode(sha256(convert_to(prosrc,'UTF8')),'hex') FROM pg_proc\n WHERE oid=to_regprocedure(v_expected.signature)) IS DISTINCT FROM v_expected.final_body_sha256 THEN\n RAISE EXCEPTION 'review_media_catalog_claim_transition_drift';\n END IF;\n EXECUTE format('REVOKE ALL ON FUNCTION %s FROM PUBLIC, anon, authenticated, service_role',to_regprocedure(v_expected.signature));\n IF v_expected.signature LIKE 'public.%' THEN\n EXECUTE format('GRANT EXECUTE ON FUNCTION %s TO authenticated',to_regprocedure(v_expected.signature));\n END IF;\n RESET ROLE;\n END LOOP;\n REVOKE CREATE ON SCHEMA review_media_private FROM privacy_workflow_owner;\n REVOKE ALL ON SCHEMA review_media_private FROM PUBLIC, anon, authenticated, service_role;\n REVOKE ALL ON TABLE review_media_private.commits, review_media_private.cleanup FROM PUBLIC, anon, authenticated, service_role;\n -- Private tables keep their migration owner. The implementation role receives\n -- only operations used by the reviewed functions, with explicit RLS policies.\n GRANT SELECT, INSERT ON review_media_private.commits TO privacy_workflow_owner;\n GRANT SELECT, INSERT, UPDATE ON review_media_private.cleanup TO privacy_workflow_owner;\n ALTER TABLE review_media_private.commits FORCE ROW LEVEL SECURITY;\n ALTER TABLE review_media_private.cleanup FORCE ROW LEVEL SECURITY;\n CREATE POLICY review_media_commits_select ON review_media_private.commits FOR SELECT TO privacy_workflow_owner USING (true);\n CREATE POLICY review_media_commits_insert ON review_media_private.commits FOR INSERT TO privacy_workflow_owner WITH CHECK (true);\n CREATE POLICY review_media_cleanup_select ON review_media_private.cleanup FOR SELECT TO privacy_workflow_owner USING (true);\n CREATE POLICY review_media_cleanup_insert ON review_media_private.cleanup FOR INSERT TO privacy_workflow_owner WITH CHECK (true);\n CREATE POLICY review_media_cleanup_update ON review_media_private.cleanup FOR UPDATE TO privacy_workflow_owner USING (true) WITH CHECK (true);\n -- Existing account-deletion contracts already require reviews SELECT/DELETE\n -- and storage.objects SELECT. Add only the six columns this RPC updates.\n GRANT UPDATE(content,categories,food_photos,is_verified,admin_note,updated_at)\n ON public.reviews TO privacy_workflow_owner;\n -- Existing supautils.policy_grants may authorize this DDL without table-owner\n -- USAGE/SET membership. Let the actual policy command decide; no fallback grant.\n CREATE POLICY review_media_workflow_read ON storage.objects FOR SELECT TO privacy_workflow_owner\n USING (bucket_id IN ('review-photos','review-verifications'));\n\n SET LOCAL ROLE privacy_workflow_owner;\n INSERT INTO privacy_retention.g014_public_rpc_allowlist\n (function_schema,function_name,identity_arguments,grantee,source_signature)\n SELECT 'public',p.proname,p.proargtypes::text,'authenticated',e.signature\n FROM pg_temp.review_media_expected e JOIN pg_proc p ON p.oid=to_regprocedure(e.signature)\n WHERE e.signature LIKE 'public.%';\n IF NOT FOUND THEN RAISE EXCEPTION 'review_media_catalog_allowlist_missing'; END IF;\n\n PERFORM privacy_retention.assert_g014_catalog_manifest();\n IF EXISTS (\n WITH expected(relation,ordinal,name,type,not_null,default_expr) AS (VALUES\n ('commits',1,'operation_id','uuid',true,NULL::text),\n ('commits',2,'owner_id','uuid',true,NULL::text),\n ('commits',3,'review_id','uuid',true,NULL::text),\n ('commits',4,'kind','text',true,NULL::text),\n ('commits',5,'request_hash','text',true,NULL::text),\n ('cleanup',1,'path','text',true,NULL::text),\n ('cleanup',2,'owner_id','uuid',true,NULL::text),\n ('cleanup',3,'review_id','uuid',true,NULL::text),\n ('cleanup',4,'purpose','text',true,NULL::text),\n ('cleanup',5,'retired','boolean',true,'false'),\n ('cleanup',6,'complete','boolean',true,'false')\n ), actual AS (\n SELECT c.relname::text,a.attnum::integer,a.attname::text,pg_catalog.format_type(a.atttypid,a.atttypmod),a.attnotnull,\n pg_catalog.pg_get_expr(d.adbin,d.adrelid)\n FROM pg_catalog.pg_class c JOIN pg_catalog.pg_attribute a ON a.attrelid=c.oid AND a.attnum>0 AND NOT a.attisdropped\n LEFT JOIN pg_catalog.pg_attrdef d ON d.adrelid=a.attrelid AND d.adnum=a.attnum\n WHERE c.relnamespace='review_media_private'::regnamespace AND c.relkind IN ('r','p')\n )\n (SELECT * FROM expected EXCEPT SELECT * FROM actual) UNION ALL (SELECT * FROM actual EXCEPT SELECT * FROM expected)\n ) THEN RAISE EXCEPTION 'review_media_catalog_column_shape_drift'; END IF;\n IF EXISTS (\n WITH expected(relation,name,definition) AS (VALUES\n ('commits','commits_pkey','PRIMARY KEY (operation_id)'),\n ('commits','commits_kind_check','CHECK ((kind = ANY (ARRAY[''edit''::text, ''delete''::text])))'),\n ('cleanup','cleanup_pkey','PRIMARY KEY (path)'),\n ('cleanup','cleanup_purpose_check','CHECK ((purpose = ANY (ARRAY[''food''::text, ''verification''::text])))')\n ), actual AS (\n SELECT c.relname::text,k.conname::text,pg_catalog.pg_get_constraintdef(k.oid)\n FROM pg_catalog.pg_class c JOIN pg_catalog.pg_constraint k ON k.conrelid=c.oid\n WHERE c.relnamespace='review_media_private'::regnamespace\n )\n (SELECT * FROM expected EXCEPT SELECT * FROM actual) UNION ALL (SELECT * FROM actual EXCEPT SELECT * FROM expected)\n ) OR EXISTS (SELECT 1 FROM pg_catalog.pg_trigger t JOIN pg_catalog.pg_class c ON c.oid=t.tgrelid\n WHERE c.relnamespace='review_media_private'::regnamespace AND NOT t.tgisinternal)\n OR EXISTS (SELECT 1 FROM pg_catalog.pg_constraint k JOIN pg_catalog.pg_class c ON c.oid=k.conrelid\n WHERE c.relnamespace='review_media_private'::regnamespace AND (NOT k.convalidated OR k.condeferrable OR k.condeferred))\n OR (SELECT count(*) FROM pg_catalog.pg_index i JOIN pg_catalog.pg_class c ON c.oid=i.indrelid\n WHERE c.relnamespace='review_media_private'::regnamespace)<>2\n OR EXISTS (SELECT 1 FROM pg_catalog.pg_index i JOIN pg_catalog.pg_class c ON c.oid=i.indrelid\n WHERE c.relnamespace='review_media_private'::regnamespace AND (NOT i.indisprimary OR NOT i.indisvalid OR NOT i.indisready)) THEN\n RAISE EXCEPTION 'review_media_catalog_constraint_shape_drift';\n END IF;\n -- Extend only the declarative protected-relation list, never an assertion.\n IF NOT EXISTS (SELECT 1 FROM pg_catalog.pg_proc WHERE oid=v_known AND proowner=v_owner\n AND prosecdef AND prokind='f' AND proretset AND provolatile='s'\n AND proconfig=ARRAY['search_path=\"\"']::text[]\n AND prolang=(SELECT oid FROM pg_catalog.pg_language WHERE lanname='sql')\n AND pg_catalog.pg_get_function_result(oid)='TABLE(schema_name name, relation_name name)') THEN\n RAISE EXCEPTION 'review_media_catalog_known_identity_metadata_drift';\n END IF;\n SELECT pg_catalog.pg_get_functiondef(v_known) INTO v_definition;\n IF (SELECT pg_catalog.encode(pg_catalog.sha256(pg_catalog.convert_to(prosrc,'UTF8')),'hex') FROM pg_catalog.pg_proc WHERE oid=v_known)<>'43bf370fe74864708e9efbc6559d899a6afa5b353e6cbc930306e31c9ecdc499'\n OR EXISTS (SELECT 1 FROM privacy_retention.g014_catalog_contract_manifest\n WHERE manifest_key->>'schema'='review_media_private') THEN\n RAISE EXCEPTION 'review_media_catalog_known_identity_source_drift';\n END IF;\n EXECUTE pg_catalog.replace(v_definition,\n $known_anchor$ ('public', 'notifications'),$known_anchor$,\n $known_replacement$ ('review_media_private', 'commits'),\n ('review_media_private', 'cleanup'),\n ('public', 'notifications'),$known_replacement$);\n IF (SELECT pg_catalog.encode(pg_catalog.sha256(pg_catalog.convert_to(prosrc,'UTF8')),'hex') FROM pg_catalog.pg_proc WHERE oid=v_known)<>'20acd5742d891cf8a33b265a3a1298c65dd9646402c58391cd17829951cf92f2' THEN\n RAISE EXCEPTION 'review_media_catalog_known_identity_post_drift';\n END IF;\n INSERT INTO privacy_retention.g014_catalog_contract_manifest(manifest_kind,manifest_key,manifest_value)\n SELECT manifest_kind,manifest_key,manifest_value FROM privacy_retention.g014_catalog_manifest_rows()\n WHERE manifest_key->>'schema'='review_media_private'\n AND manifest_key->>'relation' IN ('commits','cleanup');\n IF NOT FOUND THEN RAISE EXCEPTION 'review_media_catalog_manifest_append_missing'; END IF;\n PERFORM privacy_retention.assert_g014_catalog_manifest();\n CREATE FUNCTION pg_temp.review_media_g014_assert() RETURNS void\n LANGUAGE plpgsql SECURITY DEFINER SET search_path='' AS $bridge$\n BEGIN\n IF EXISTS (SELECT 1 FROM pg_temp.review_media_expected e\n JOIN pg_proc p ON p.oid=to_regprocedure(e.signature)\n WHERE e.signature LIKE 'public.%' AND (\n (SELECT count(*) FROM privacy_retention.g014_public_rpc_allowlist a\n WHERE a.function_schema='public' AND a.function_name=p.proname)<>1\n OR NOT EXISTS (SELECT 1 FROM privacy_retention.g014_public_rpc_allowlist a\n WHERE a.source_signature=e.signature AND a.function_schema='public' AND a.function_name=p.proname\n AND a.identity_arguments=p.proargtypes::text AND a.grantee='authenticated'))) THEN\n RAISE EXCEPTION 'review_media_catalog_allowlist_drift';\n END IF;\n PERFORM privacy_retention.assert_g014_public_rpc_allowlist();\n PERFORM privacy_retention.assert_g014_definer_contract();\n PERFORM privacy_retention.assert_g014_catalog_contract();\n DROP FUNCTION pg_temp.review_media_g014_assert();\n END $bridge$;\n REVOKE ALL ON FUNCTION pg_temp.review_media_g014_assert() FROM PUBLIC,anon,authenticated,service_role;\n GRANT EXECUTE ON FUNCTION pg_temp.review_media_g014_assert() TO postgres;\n IF (SELECT jsonb_agg(to_jsonb(m) ORDER BY manifest_kind,manifest_key)\n FROM privacy_retention.g014_catalog_contract_manifest m\n WHERE m.manifest_key->>'schema' IS DISTINCT FROM 'review_media_private') IS DISTINCT FROM v_manifest THEN\n RAISE EXCEPTION 'review_media_catalog_manifest_drift';\n END IF;\n RESET ROLE;\n IF v_major=15 THEN\n IF v_pg15_lease THEN REVOKE privacy_workflow_owner FROM postgres; END IF;\n ELSIF v_self IS NULL THEN\n REVOKE privacy_workflow_owner FROM postgres GRANTED BY postgres;\n ELSE\n EXECUTE format('GRANT privacy_workflow_owner TO postgres WITH ADMIN FALSE, INHERIT %s, SET %s GRANTED BY postgres',\n upper(v_self->>'inherit_option'),upper(v_self->>'set_option'));\n END IF;\n IF (SELECT coalesce(jsonb_agg(to_jsonb(m) ORDER BY roleid,member,grantor),'[]') FROM pg_auth_members m) IS DISTINCT FROM v_members\n OR (SELECT to_jsonb(n) FROM pg_namespace n WHERE nspname='public') IS DISTINCT FROM v_public\n OR (SELECT jsonb_agg(CASE WHEN p.oid=v_known THEN to_jsonb(p)-'prosrc' ELSE to_jsonb(p) END ORDER BY p.oid) FROM pg_proc p\n WHERE p.pronamespace='privacy_retention'::regnamespace) IS DISTINCT FROM v_assertions\n OR (SELECT jsonb_agg(((to_jsonb(p)-'proowner'-'proacl'-'prosrc'-'proargdefaults') || jsonb_build_object('proargdefaults',pg_get_expr(p.proargdefaults,0))) ORDER BY p.oid) FROM pg_proc p\n JOIN pg_temp.review_media_expected e ON p.oid=to_regprocedure(e.signature)) IS DISTINCT FROM v_functions THEN\n RAISE EXCEPTION 'review_media_catalog_preservation_drift' USING DETAIL =\n jsonb_build_object(\n 'membershipPreserved',(SELECT coalesce(jsonb_agg(to_jsonb(m) ORDER BY roleid,member,grantor),'[]') FROM pg_auth_members m) IS NOT DISTINCT FROM v_members,\n 'publicNamespacePreserved',(SELECT to_jsonb(n) FROM pg_namespace n WHERE nspname='public') IS NOT DISTINCT FROM v_public,\n 'assertionsPreserved',(SELECT jsonb_agg(CASE WHEN p.oid=v_known THEN to_jsonb(p)-'prosrc' ELSE to_jsonb(p) END ORDER BY p.oid) FROM pg_proc p\n WHERE p.pronamespace='privacy_retention'::regnamespace) IS NOT DISTINCT FROM v_assertions,\n 'functionMetadataPreserved',(SELECT jsonb_agg(((to_jsonb(p)-'proowner'-'proacl'-'prosrc'-'proargdefaults') || jsonb_build_object('proargdefaults',pg_get_expr(p.proargdefaults,0))) ORDER BY p.oid) FROM pg_proc p\n JOIN pg_temp.review_media_expected e ON p.oid=to_regprocedure(e.signature)) IS NOT DISTINCT FROM v_functions,\n 'changedMetadataFields',(SELECT jsonb_agg(DISTINCT old_field.key ORDER BY old_field.key)\n FROM jsonb_array_elements(v_functions) AS old_function(value)\n CROSS JOIN LATERAL jsonb_each(old_function.value) AS old_field(key,value)\n JOIN pg_proc p ON p.oid=(old_function.value->>'oid')::oid\n WHERE old_field.value IS DISTINCT FROM (((to_jsonb(p)-'proowner'-'proacl'-'prosrc'-'proargdefaults') || jsonb_build_object('proargdefaults',pg_get_expr(p.proargdefaults,0))))->old_field.key)\n )::text;\n END IF;\n PERFORM pg_temp.review_media_g014_assert();\n IF to_regprocedure('pg_temp.review_media_g014_assert()') IS NOT NULL THEN\n RAISE EXCEPTION 'review_media_catalog_bridge_remaining';\n END IF;\nEND $integration$",
+ "-- REVIEW_MEDIA_READBACK_BEGIN\n-- The same independent catalog-only checks are embedded in canonical readback.\nDO $review_media_catalog_readback$\nDECLARE\n v_expected record; v_function record; v_relation record;\n v_owner oid := 'privacy_workflow_owner'::regrole;\n v_path text := current_setting('search_path');\nBEGIN\n -- Existing G041 claim boundary: never restore auth-schema access for the\n -- workflow owner. Pin the unchanged helper before substituting auth.uid().\n IF has_schema_privilege(v_owner,'auth','USAGE,CREATE') OR NOT EXISTS (\n SELECT 1 FROM pg_proc p JOIN pg_namespace n ON n.oid=p.pronamespace\n WHERE n.nspname='privacy_retention' AND p.proname='g041_current_claim_user_id'\n AND p.pronargs=0 AND p.proowner=v_owner AND p.prosecdef AND p.prokind='f'\n AND NOT p.proretset AND p.prorettype='uuid'::regtype AND p.provolatile='s'\n AND p.prolang=(SELECT oid FROM pg_language WHERE lanname='plpgsql')\n AND p.proconfig=ARRAY['search_path=\"\"']::text[]\n AND encode(sha256(convert_to(p.prosrc,'UTF8')),'hex')='43f3ff2ed914944802555c4ffe3ff8ecece213e82288e2c1c47ae22efdf81585'\n AND has_function_privilege(v_owner,p.oid,'EXECUTE')\n AND NOT has_function_privilege('anon',p.oid,'EXECUTE')\n AND NOT has_function_privilege('authenticated',p.oid,'EXECUTE')\n AND NOT has_function_privilege('service_role',p.oid,'EXECUTE')\n ) THEN RAISE EXCEPTION 'review_media_catalog_claim_boundary_drift'; END IF;\n -- Fix pg_get_expr qualification before comparing the exact parsed predicates.\n PERFORM set_config('search_path','public, pg_catalog',true);\n FOR v_expected IN SELECT * FROM (VALUES\n ('public.finish_review_media_cleanup()','f6c4e71988ed2b789b887484af5fdf2e482bde98f1d1c5b5d4e7ad124c314bd1',true,'v','plpgsql','bigint',ARRAY[]::text[]),\n ('public.mutate_review_with_media(uuid,uuid,text,timestamp with time zone,text,text[],text[])','50bfad7bc4fc7ddbc9d9379e8eb13facd938a15f66553576cc69fbcf69aa98b1',true,'v','plpgsql','text',ARRAY['p_operation_id','p_review_id','p_kind','p_expected_updated_at','p_content','p_categories','p_food_photos']::text[]),\n ('public.pending_review_media_cleanup()','5a92c8f282c895eabaa77f8cf3f452fb659bac33bd519ed8cf439dc1f2905c7b',true,'v','plpgsql','TABLE(path text, owner_id uuid, review_id uuid, purpose text)',ARRAY[]::text[]),\n ('public.queue_review_upload_cleanup(uuid,text[])','89c3b573891f598cac5df6ce9a3217639092801a7439ec0404f338f2097114a8',true,'v','plpgsql','text',ARRAY['p_review_id','p_paths']::text[]),\n ('public.read_review_media_commit(uuid,uuid,text)','2f14abfc04864f369467f5ac5f0603defa149b8023d7e6b04e2bb8535f8a268e',true,'v','sql','text',ARRAY['p_operation_id','p_review_id','p_kind']::text[]),\n ('public.review_media_delete_allowed(text)','a75dbd787dd388b0761bee11c82a56d5f410688e4cfd953181bce4d613062ae7',true,'v','plpgsql','boolean',ARRAY['p_path']::text[]),\n ('public.review_media_upload_allowed(text,text)','298c6a127f3a44a193b78be8c1f2577ab58aa34c772b66ccd53f774d807cc90c',true,'v','plpgsql','boolean',ARRAY['p_bucket','p_path']::text[]),\n ('review_media_private.canonical(text,uuid,uuid,text)','87d8397c36cf4c7b986f75cbc2788979333a76be2cc26dc5522cd362f62414a9',false,'i','sql','boolean',ARRAY['p_path','p_owner','p_review','p_purpose']::text[]),\n ('review_media_private.enqueue_removed()','7edb21d70ea3ff81ab90e13617e8c1c2749b54b1506f1ff42aaa6c5dc33f9e38',true,'v','plpgsql','trigger',ARRAY[]::text[]),\n ('review_media_private.guard_references()','846030547ffd76e17596755d7eee4279dcb9b563361435260826071d2f250d59',true,'v','plpgsql','trigger',ARRAY[]::text[]),\n ('review_media_private.lock_changes()','381d89aa6bbf5a59c2ee0fef2a7392ef705147252b324527201b8b6f74376250',true,'v','plpgsql','trigger',ARRAY[]::text[]),\n ('review_media_private.owned_legacy(text,uuid,text)','a2d55f09f03fcaeedd77461198511673da7342a6a4873d37227f9cf612090980',false,'i','sql','boolean',ARRAY['p_path','p_owner','p_purpose']::text[]),\n ('review_media_private.reference_key(text)','a8910cb09bfd959951045e8b0b8233b1e2a8bdc45073abeebdfc26b552b1dae7',false,'i','plpgsql','text',ARRAY['p_value']::text[]),\n ('review_media_private.referenced(text)','cc295115d85a3a11b380e1378e2ee91b0d9f7f587c68839653f54ed826c398ce',true,'v','sql','boolean',ARRAY['p_path']::text[])\n ) AS expected(signature,body_sha256,definer,volatility,language,result,argument_names) LOOP\n SELECT * INTO v_function FROM pg_proc WHERE oid=to_regprocedure(v_expected.signature);\n IF v_function.oid IS NULL OR v_function.proowner<>v_owner OR v_function.prokind<>'f'\n OR v_function.prosecdef IS DISTINCT FROM v_expected.definer OR v_function.provolatile::text<>v_expected.volatility\n OR v_function.proconfig IS DISTINCT FROM ARRAY['search_path=\"\"']::text[]\n OR (SELECT lanname FROM pg_language WHERE oid=v_function.prolang)<>v_expected.language\n OR pg_get_function_result(v_function.oid) IS DISTINCT FROM v_expected.result\n OR coalesce(v_function.proargnames[1:v_function.pronargs],ARRAY[]::text[]) IS DISTINCT FROM v_expected.argument_names\n OR v_function.pronargdefaults<>(CASE WHEN v_function.proname='mutate_review_with_media' THEN 3 ELSE 0 END)\n OR (v_function.pronargdefaults=3 AND pg_get_expr(v_function.proargdefaults,0)<>'NULL::text, NULL::text[], NULL::text[]')\n OR encode(sha256(convert_to(v_function.prosrc,'UTF8')),'hex')<>v_expected.body_sha256\n OR (SELECT count(*) FROM pg_proc x WHERE x.pronamespace=v_function.pronamespace AND x.proname=v_function.proname)<>1\n OR NOT has_function_privilege(v_owner,v_function.oid,'EXECUTE')\n OR has_function_privilege('anon',v_function.oid,'EXECUTE')\n OR has_function_privilege('service_role',v_function.oid,'EXECUTE')\n OR has_function_privilege('authenticated',v_function.oid,'EXECUTE') IS DISTINCT FROM (v_expected.signature LIKE 'public.%')\n OR EXISTS (SELECT 1 FROM aclexplode(coalesce(v_function.proacl,acldefault('f',v_function.proowner))) a\n WHERE a.is_grantable OR a.privilege_type<>'EXECUTE'\n OR (a.grantee<>v_owner AND NOT (v_expected.signature LIKE 'public.%' AND a.grantee='authenticated'::regrole))) THEN\n RAISE EXCEPTION 'review_media_catalog_function_drift';\n END IF;\n END LOOP;\n IF (SELECT count(*) FROM pg_proc WHERE pronamespace='review_media_private'::regnamespace)<>7\n OR (SELECT count(*) FROM pg_class WHERE relnamespace='review_media_private'::regnamespace AND relkind IN ('r','p','v','m','f','S'))<>2\n OR NOT EXISTS (SELECT 1 FROM pg_namespace WHERE nspname='review_media_private' AND nspowner='postgres'::regrole)\n OR NOT has_schema_privilege(v_owner,'review_media_private','USAGE')\n OR has_schema_privilege(v_owner,'review_media_private','CREATE')\n OR EXISTS (SELECT 1 FROM pg_namespace n CROSS JOIN LATERAL aclexplode(coalesce(n.nspacl,acldefault('n',n.nspowner))) a\n WHERE n.nspname='review_media_private' AND a.grantee<>n.nspowner\n AND (a.grantee<>v_owner OR a.privilege_type<>'USAGE' OR a.is_grantable)) THEN\n RAISE EXCEPTION 'review_media_catalog_private_schema_drift';\n END IF;\n FOR v_expected IN SELECT * FROM (VALUES\n ('commits',ARRAY['INSERT','SELECT']::text[]),\n ('cleanup',ARRAY['INSERT','SELECT','UPDATE']::text[])\n ) AS expected(name,privileges) LOOP\n SELECT * INTO v_relation FROM pg_class WHERE oid=to_regclass('review_media_private.'||v_expected.name);\n IF v_relation.relkind<>'r' OR v_relation.relowner<>'postgres'::regrole OR NOT v_relation.relrowsecurity OR NOT v_relation.relforcerowsecurity\n OR (SELECT array_agg(a.privilege_type ORDER BY a.privilege_type) FROM aclexplode(v_relation.relacl) a\n WHERE a.grantee=v_owner) IS DISTINCT FROM v_expected.privileges\n OR EXISTS (SELECT 1 FROM aclexplode(v_relation.relacl) a WHERE a.grantee<>v_relation.relowner AND (a.grantee<>v_owner OR a.is_grantable))\n OR EXISTS (SELECT 1 FROM pg_attribute WHERE attrelid=v_relation.oid AND attacl IS NOT NULL) THEN\n RAISE EXCEPTION 'review_media_catalog_private_table_drift';\n END IF;\n END LOOP;\n IF EXISTS (\n WITH expected(relation, name, cmd, using_expr, check_expr) AS (VALUES\n ('commits','review_media_commits_select','r','true'::text,NULL::text),\n ('commits','review_media_commits_insert','a',NULL::text,'true'),\n ('cleanup','review_media_cleanup_select','r','true',NULL::text),\n ('cleanup','review_media_cleanup_insert','a',NULL::text,'true'),\n ('cleanup','review_media_cleanup_update','w','true','true')\n ), actual AS (\n SELECT c.relname::text,p.polname::text,p.polcmd::text,pg_get_expr(p.polqual,p.polrelid),pg_get_expr(p.polwithcheck,p.polrelid),p.polpermissive,p.polroles\n FROM pg_policy p JOIN pg_class c ON c.oid=p.polrelid WHERE c.relnamespace='review_media_private'::regnamespace\n ), wanted AS (SELECT *,true,ARRAY[v_owner] FROM expected)\n (SELECT * FROM wanted EXCEPT SELECT * FROM actual) UNION ALL (SELECT * FROM actual EXCEPT SELECT * FROM wanted)\n ) THEN RAISE EXCEPTION 'review_media_catalog_private_policy_drift'; END IF;\n IF EXISTS (\n SELECT 1 FROM (VALUES ('anon'),('authenticated'),('service_role')) r(name)\n WHERE has_schema_privilege(r.name,'review_media_private','USAGE,CREATE')\n OR has_table_privilege(r.name,'review_media_private.commits','SELECT,INSERT,UPDATE,DELETE,TRUNCATE,REFERENCES,TRIGGER')\n OR has_table_privilege(r.name,'review_media_private.cleanup','SELECT,INSERT,UPDATE,DELETE,TRUNCATE,REFERENCES,TRIGGER')\n ) OR NOT has_table_privilege(v_owner,'public.reviews','SELECT')\n OR NOT has_table_privilege(v_owner,'public.reviews','DELETE')\n OR EXISTS (SELECT 1 FROM unnest(ARRAY['content','categories','food_photos','is_verified','admin_note','updated_at']) c(name)\n WHERE NOT has_column_privilege(v_owner,'public.reviews',c.name,'UPDATE'))\n OR has_table_privilege(v_owner,'public.reviews','INSERT,UPDATE,TRUNCATE,REFERENCES,TRIGGER')\n OR EXISTS (SELECT 1 FROM pg_attribute a WHERE a.attrelid='public.reviews'::regclass AND a.attnum>0 AND NOT a.attisdropped\n AND a.attname<>ALL(ARRAY['content','categories','food_photos','is_verified','admin_note','updated_at'])\n AND has_column_privilege(v_owner,a.attrelid,a.attnum,'UPDATE'))\n OR NOT has_table_privilege(v_owner,'storage.objects','SELECT')\n OR has_table_privilege(v_owner,'storage.objects','INSERT,UPDATE,DELETE,TRUNCATE,REFERENCES,TRIGGER') THEN\n RAISE EXCEPTION 'review_media_catalog_minimum_grant_drift';\n END IF;\n IF EXISTS (\n WITH expected(relation,ordinal,name,type,not_null,default_expr) AS (VALUES\n ('commits',1,'operation_id','uuid',true,NULL::text),\n ('commits',2,'owner_id','uuid',true,NULL::text),\n ('commits',3,'review_id','uuid',true,NULL::text),\n ('commits',4,'kind','text',true,NULL::text),\n ('commits',5,'request_hash','text',true,NULL::text),\n ('cleanup',1,'path','text',true,NULL::text),\n ('cleanup',2,'owner_id','uuid',true,NULL::text),\n ('cleanup',3,'review_id','uuid',true,NULL::text),\n ('cleanup',4,'purpose','text',true,NULL::text),\n ('cleanup',5,'retired','boolean',true,'false'),\n ('cleanup',6,'complete','boolean',true,'false')\n ), actual AS (\n SELECT c.relname::text,a.attnum::integer,a.attname::text,pg_catalog.format_type(a.atttypid,a.atttypmod),a.attnotnull,\n pg_catalog.pg_get_expr(d.adbin,d.adrelid)\n FROM pg_catalog.pg_class c JOIN pg_catalog.pg_attribute a ON a.attrelid=c.oid AND a.attnum>0 AND NOT a.attisdropped\n LEFT JOIN pg_catalog.pg_attrdef d ON d.adrelid=a.attrelid AND d.adnum=a.attnum\n WHERE c.relnamespace='review_media_private'::regnamespace AND c.relkind IN ('r','p')\n )\n (SELECT * FROM expected EXCEPT SELECT * FROM actual) UNION ALL (SELECT * FROM actual EXCEPT SELECT * FROM expected)\n ) THEN RAISE EXCEPTION 'review_media_catalog_column_shape_drift'; END IF;\n IF EXISTS (\n WITH expected(relation,name,definition) AS (VALUES\n ('commits','commits_pkey','PRIMARY KEY (operation_id)'),\n ('commits','commits_kind_check','CHECK ((kind = ANY (ARRAY[''edit''::text, ''delete''::text])))'),\n ('cleanup','cleanup_pkey','PRIMARY KEY (path)'),\n ('cleanup','cleanup_purpose_check','CHECK ((purpose = ANY (ARRAY[''food''::text, ''verification''::text])))')\n ), actual AS (\n SELECT c.relname::text,k.conname::text,pg_catalog.pg_get_constraintdef(k.oid)\n FROM pg_catalog.pg_class c JOIN pg_catalog.pg_constraint k ON k.conrelid=c.oid\n WHERE c.relnamespace='review_media_private'::regnamespace\n )\n (SELECT * FROM expected EXCEPT SELECT * FROM actual) UNION ALL (SELECT * FROM actual EXCEPT SELECT * FROM expected)\n ) OR EXISTS (SELECT 1 FROM pg_catalog.pg_trigger t JOIN pg_catalog.pg_class c ON c.oid=t.tgrelid\n WHERE c.relnamespace='review_media_private'::regnamespace AND NOT t.tgisinternal)\n OR EXISTS (SELECT 1 FROM pg_catalog.pg_constraint k JOIN pg_catalog.pg_class c ON c.oid=k.conrelid\n WHERE c.relnamespace='review_media_private'::regnamespace AND (NOT k.convalidated OR k.condeferrable OR k.condeferred))\n OR (SELECT count(*) FROM pg_catalog.pg_index i JOIN pg_catalog.pg_class c ON c.oid=i.indrelid\n WHERE c.relnamespace='review_media_private'::regnamespace)<>2\n OR EXISTS (SELECT 1 FROM pg_catalog.pg_index i JOIN pg_catalog.pg_class c ON c.oid=i.indrelid\n WHERE c.relnamespace='review_media_private'::regnamespace AND (NOT i.indisprimary OR NOT i.indisvalid OR NOT i.indisready)) THEN\n RAISE EXCEPTION 'review_media_catalog_constraint_shape_drift';\n END IF;\n IF (SELECT pg_catalog.encode(pg_catalog.sha256(pg_catalog.convert_to(prosrc,'UTF8')),'hex') FROM pg_catalog.pg_proc\n WHERE pronamespace=(SELECT oid FROM pg_namespace WHERE nspname='privacy_retention')\n AND proname='g014_catalog_protected_relations' AND pronargs=0) IS DISTINCT FROM '20acd5742d891cf8a33b265a3a1298c65dd9646402c58391cd17829951cf92f2' THEN\n RAISE EXCEPTION 'review_media_catalog_known_identity_post_drift';\n END IF;\n -- Exact allowlist and unchanged G014 assertions run through the owner bridge\n -- above; canonical readback also runs them before this catalog-only block.\n -- REVIEW_MEDIA_TRIGGER_READBACK_BEGIN\n IF EXISTS (\n WITH expected(name,definition) AS (VALUES\n ('review_media_enqueue_removed','CREATE TRIGGER review_media_enqueue_removed AFTER DELETE OR UPDATE OF food_photos, verification_photo, user_id, id ON public.reviews FOR EACH ROW EXECUTE FUNCTION review_media_private.enqueue_removed()'),\n ('review_media_reference_guard','CREATE TRIGGER review_media_reference_guard BEFORE INSERT OR UPDATE OF food_photos, verification_photo, user_id, id ON public.reviews FOR EACH ROW EXECUTE FUNCTION review_media_private.guard_references()'),\n ('review_media_serialize','CREATE TRIGGER review_media_serialize BEFORE INSERT OR DELETE OR UPDATE OF food_photos, verification_photo, user_id, id ON public.reviews FOR EACH STATEMENT EXECUTE FUNCTION review_media_private.lock_changes()')\n ), actual AS (\n SELECT tgname::text,pg_get_triggerdef(oid),tgenabled::text FROM pg_trigger\n WHERE tgrelid='public.reviews'::regclass AND tgname LIKE 'review_media_%' AND NOT tgisinternal\n ), wanted AS (SELECT name,definition,'O'::text FROM expected)\n (SELECT * FROM wanted EXCEPT SELECT * FROM actual) UNION ALL (SELECT * FROM actual EXCEPT SELECT * FROM wanted)\n ) THEN RAISE EXCEPTION 'review_media_catalog_trigger_drift'; END IF;\n -- REVIEW_MEDIA_TRIGGER_READBACK_END\n -- Compare command, role, permissive/restrictive bit and BOTH complete predicates.\n IF EXISTS (\n WITH expected(name,cmd,permissive,role_name,using_expr,check_expr) AS (VALUES\n ('review_media_safe_delete','d',false,'authenticated','((bucket_id <> ''review-photos''::text) OR review_media_delete_allowed(name))',NULL::text),\n ('review_media_safe_insert','a',false,'authenticated',NULL::text,'((bucket_id <> ALL (ARRAY[''review-photos''::text, ''review-verifications''::text])) OR review_media_upload_allowed(bucket_id, name))'),\n ('review_media_safe_update','w',false,'authenticated','((bucket_id <> ALL (ARRAY[''review-photos''::text, ''review-verifications''::text])) OR review_media_upload_allowed(bucket_id, name))','((bucket_id <> ALL (ARRAY[''review-photos''::text, ''review-verifications''::text])) OR review_media_upload_allowed(bucket_id, name))'),\n ('review_media_workflow_read','r',true,'privacy_workflow_owner','(bucket_id = ANY (ARRAY[''review-photos''::text, ''review-verifications''::text]))',NULL::text),\n ('review_photos_food_insert','a',false,'authenticated',NULL::text,'((bucket_id <> ''review-photos''::text) OR ((split_part(name, ''/''::text, 4) = ''food''::text) AND ((storage.foldername(name))[1] = ( SELECT (auth.uid())::text AS uid))))'),\n ('review_photos_food_update','w',false,'authenticated','((bucket_id <> ''review-photos''::text) OR ((split_part(name, ''/''::text, 4) = ''food''::text) AND ((storage.foldername(name))[1] = ( SELECT (auth.uid())::text AS uid))))','((bucket_id <> ''review-photos''::text) OR ((split_part(name, ''/''::text, 4) = ''food''::text) AND ((storage.foldername(name))[1] = ( SELECT (auth.uid())::text AS uid))))'),\n ('review_verifications_owner_delete','d',true,'authenticated','((bucket_id = ''review-verifications''::text) AND ((storage.foldername(name))[1] = ( SELECT (auth.uid())::text AS uid)))',NULL::text),\n ('review_verifications_owner_insert','a',true,'authenticated',NULL::text,'((bucket_id = ''review-verifications''::text) AND ((storage.foldername(name))[1] = ( SELECT (auth.uid())::text AS uid)) AND (split_part(name, ''/''::text, 4) = ''verification''::text))'),\n ('review_verifications_owner_read','r',true,'authenticated','((bucket_id = ''review-verifications''::text) AND (((storage.foldername(name))[1] = ( SELECT (auth.uid())::text AS uid)) OR has_role(( SELECT auth.uid() AS uid), ''admin''::app_role)))',NULL::text),\n ('review_verifications_safe_delete','d',false,'authenticated','((bucket_id <> ''review-verifications''::text) OR review_media_delete_allowed(name))',NULL::text)\n ), actual AS (\n SELECT p.polname::text,p.polcmd::text,p.polpermissive,p.polroles,\n regexp_replace(pg_get_expr(p.polqual,p.polrelid),'[[:space:]]+',' ','g'),\n regexp_replace(pg_get_expr(p.polwithcheck,p.polrelid),'[[:space:]]+',' ','g')\n FROM pg_policy p WHERE p.polrelid='storage.objects'::regclass\n AND (p.polname LIKE 'review_media_%' OR p.polname LIKE 'review_photos_%' OR p.polname LIKE 'review_verifications_%')\n ), wanted AS (SELECT name,cmd,permissive,ARRAY[to_regrole(role_name)::oid],using_expr,check_expr FROM expected)\n (SELECT * FROM wanted EXCEPT SELECT * FROM actual) UNION ALL (SELECT * FROM actual EXCEPT SELECT * FROM wanted)\n ) THEN RAISE EXCEPTION 'review_media_catalog_storage_policy_drift'; END IF;\n IF NOT EXISTS (SELECT 1 FROM storage.buckets WHERE id='review-verifications' AND name='review-verifications'\n AND public=false AND file_size_limit=5242880\n AND allowed_mime_types=ARRAY['image/jpeg','image/png','image/webp','image/avif']::text[]) THEN\n RAISE EXCEPTION 'review_media_catalog_private_bucket_drift';\n END IF;\n -- Admit the strict canonical local read policy or the three exact deployed\n -- bucket read identities. All existing API-role reads must match a known\n -- predicate; the private verification bucket is never publicly readable.\n IF EXISTS (SELECT 1 FROM pg_policy p WHERE p.polrelid='storage.objects'::regclass\n AND p.polcmd='r' AND p.polpermissive AND p.polroles && ARRAY[0::oid,'anon'::regrole::oid,'authenticated'::regrole::oid]\n AND p.polname NOT IN ('tzudong_public_media_read','Anyone can view review photos','Public read access',\n 'Public read access for profile-avatars','local_nightly_avatar_read','review_verifications_owner_read'))\n OR EXISTS (SELECT 1 FROM pg_policy p JOIN (VALUES\n ('Anyone can view review photos','(bucket_id = ''review-photos''::text)'),\n ('Public read access','(bucket_id = ''ad-banner-images''::text)'),\n ('Public read access for profile-avatars','(bucket_id = ''profile-avatars''::text)')\n ) e(name,predicate) ON p.polname=e.name\n WHERE p.polrelid='storage.objects'::regclass AND (p.polcmd<>'r' OR NOT p.polpermissive\n OR p.polroles<>ARRAY[0::oid] OR p.polwithcheck IS NOT NULL OR pg_get_expr(p.polqual,p.polrelid)<>e.predicate))\n OR EXISTS (SELECT 1 FROM pg_policy p WHERE p.polrelid='storage.objects'::regclass\n AND p.polname='tzudong_public_media_read' AND (p.polcmd<>'r' OR NOT p.polpermissive\n OR NOT p.polroles @> ARRAY['anon'::regrole::oid,'authenticated'::regrole::oid] OR cardinality(p.polroles)<>2\n OR p.polwithcheck IS NOT NULL OR pg_get_expr(p.polqual,p.polrelid)<>'(bucket_id = ANY (ARRAY[''profile-avatars''::text, ''review-photos''::text, ''ad-banner-images''::text]))'))\n OR EXISTS (SELECT 1 FROM pg_policy p WHERE p.polrelid='storage.objects'::regclass AND p.polname='local_nightly_avatar_read'\n AND (p.polcmd<>'r' OR NOT p.polpermissive OR NOT p.polroles @> ARRAY['anon'::regrole::oid,'authenticated'::regrole::oid]\n OR cardinality(p.polroles)<>2 OR p.polwithcheck IS NOT NULL OR pg_get_expr(p.polqual,p.polrelid)<>'(bucket_id = ''avatars''::text)')) THEN\n RAISE EXCEPTION 'review_media_catalog_public_read_drift';\n END IF;\n IF NOT EXISTS (SELECT 1 FROM pg_policy WHERE polrelid='storage.objects'::regclass\n AND polname='tzudong_public_media_read' AND polcmd='r' AND polpermissive\n AND polroles @> ARRAY['anon'::regrole::oid,'authenticated'::regrole::oid]\n AND cardinality(polroles)=2 AND polwithcheck IS NULL\n AND pg_get_expr(polqual,polrelid)='(bucket_id = ANY (ARRAY[''profile-avatars''::text, ''review-photos''::text, ''ad-banner-images''::text]))')\n AND (SELECT count(*) FROM pg_policy WHERE polrelid='storage.objects'::regclass\n AND polname IN ('Anyone can view review photos','Public read access','Public read access for profile-avatars'))<>3 THEN\n RAISE EXCEPTION 'review_media_catalog_public_read_drift';\n END IF;\n -- The global reference scan must see all reviews under the trusted role.\n IF NOT EXISTS (SELECT 1 FROM pg_policy WHERE polrelid='public.reviews'::regclass\n AND polname='g014_account_deletion_source_access' AND polcmd='*' AND polpermissive\n AND polroles=ARRAY[v_owner] AND pg_get_expr(polqual,polrelid)='true' AND pg_get_expr(polwithcheck,polrelid)='true') THEN\n RAISE EXCEPTION 'review_media_catalog_reference_visibility_drift';\n END IF;\n PERFORM set_config('search_path',v_path,true);\nEND $review_media_catalog_readback$",
+ "-- REVIEW_MEDIA_READBACK_END\nNOTIFY pgrst, 'reload schema'",
+ "COMMIT"
+ ]
+ },
+ {
+ "id": "admin_review_media_completion",
+ "version": "20261011072824",
+ "name": "admin_review_media_completion",
+ "path": "backend/supabase/migrations/20261011072824_admin_review_media_completion.sql",
+ "bytes": 13429,
+ "sha256": "32626259ca77ea913f068ee804815ff439abbff1ce310da4525e4f0bb86d8338",
+ "statementCount": 7,
+ "statementVectorSha256": "3a47d7e182bfd764ae70401e4e8462b08eaa5576baf4d051503e80534fc8fb8e",
+ "originalStatementVector": [
+ "-- Forward-only correction. Immutable applied SQL and G014 assertions stay intact.\n-- Relation locks precede the media global lock; global precedes object locks.\n-- Admin locks reviews before restaurants, matching review-trigger counter work.\nBEGIN",
+ "SET LOCAL lock_timeout='2s'",
+ "SELECT pg_advisory_xact_lock(hashtextextended('tzudong:review-media-catalog:v2',0))",
+ "DO $forward$\nDECLARE target oid; definition text; source text; patched text; metadata jsonb; defaults text;\n before_members jsonb; before_assertions jsonb; before_manifest jsonb; self_membership jsonb;\n workflow_owner oid:='privacy_workflow_owner'::regrole; runner oid:='postgres'::regrole;\n major integer:=current_setting('server_version_num')::integer/10000; pg15_lease boolean:=false;\nBEGIN\n IF current_user<>'postgres' OR session_user<>'postgres' OR major NOT IN (15,17)\n OR current_setting('transaction_read_only')<>'off' OR NOT EXISTS(SELECT 1 FROM pg_roles\n WHERE oid=workflow_owner AND NOT rolsuper AND NOT rolbypassrls AND NOT rolcanlogin AND NOT rolinherit)\n OR to_regprocedure('public.admin_review_verification_cleanup(uuid,text,text[])') IS NOT NULL\n THEN RAISE EXCEPTION 'REVIEW_MEDIA_FORWARD_EXECUTOR_DENIED'; END IF;\n SELECT coalesce(jsonb_agg(to_jsonb(m) ORDER BY roleid,member,grantor),'[]') INTO before_members FROM pg_auth_members m;\n SELECT jsonb_agg(to_jsonb(p) ORDER BY oid) INTO before_assertions FROM pg_proc p WHERE pronamespace='privacy_retention'::regnamespace;\n\n target:='public.admin_record_action(uuid,text,uuid,text,uuid[],jsonb,text)'::regprocedure;\n SELECT pg_get_functiondef(p.oid),p.prosrc,to_jsonb(p)-ARRAY['prosrc','proargdefaults'],pg_get_expr(p.proargdefaults,0)\n INTO definition,source,metadata,defaults FROM pg_proc p WHERE p.oid=target;\n IF encode(sha256(convert_to(source,'UTF8')),'hex')<>'a18fad1f748d736371a9ab549a1ca483a5b2f321674fabe71b2411b7e58b3fc9' THEN RAISE EXCEPTION 'REVIEW_MEDIA_LOCK_SOURCE_DRIFT'; END IF;\n patched:=source;\n IF (length(patched)-length(replace(patched,$old$LOCK TABLE public.restaurants,public.restaurant_submissions,public.restaurant_submission_items,public.reviews,public.restaurant_requests IN SHARE ROW EXCLUSIVE MODE;$old$,'')))/length($old$LOCK TABLE public.restaurants,public.restaurant_submissions,public.restaurant_submission_items,public.reviews,public.restaurant_requests IN SHARE ROW EXCLUSIVE MODE;$old$)<>1 THEN RAISE EXCEPTION 'REVIEW_MEDIA_LOCK_ANCHOR_DRIFT'; END IF;\n patched:=replace(patched,$old$LOCK TABLE public.restaurants,public.restaurant_submissions,public.restaurant_submission_items,public.reviews,public.restaurant_requests IN SHARE ROW EXCLUSIVE MODE;$old$,$new$LOCK TABLE public.reviews,public.restaurants,public.restaurant_submissions,public.restaurant_submission_items,public.restaurant_requests IN SHARE ROW EXCLUSIVE MODE;$new$);\n IF (length(patched)-length(replace(patched,$old$IF p_phase LIKE 'cleanup_%' THEN$old$,'')))/length($old$IF p_phase LIKE 'cleanup_%' THEN$old$)<>1 THEN RAISE EXCEPTION 'REVIEW_MEDIA_LOCK_ANCHOR_DRIFT'; END IF;\n patched:=replace(patched,$old$IF p_phase LIKE 'cleanup_%' THEN$old$,$new$IF p_phase LIKE 'cleanup_%' THEN\n PERFORM pg_advisory_xact_lock(741093, 1);$new$);\n IF (length(patched)-length(replace(patched,$old$IF p_action='review.delete' THEN\n FOR media$old$,'')))/length($old$IF p_action='review.delete' THEN\n FOR media$old$)<>1 THEN RAISE EXCEPTION 'REVIEW_MEDIA_LOCK_ANCHOR_DRIFT'; END IF;\n patched:=replace(patched,$old$IF p_action='review.delete' THEN\n FOR media$old$,$new$IF p_action='review.delete' THEN\n PERFORM pg_advisory_xact_lock(741093, 1);\n FOR media$new$);\n EXECUTE replace(definition,source,patched);\n IF (SELECT to_jsonb(p)-ARRAY['prosrc','proargdefaults'] FROM pg_proc p WHERE p.oid=target) IS DISTINCT FROM metadata\n OR (SELECT pg_get_expr(p.proargdefaults,0) FROM pg_proc p WHERE p.oid=target) IS DISTINCT FROM defaults\n OR (SELECT encode(sha256(convert_to(p.prosrc,'UTF8')),'hex') FROM pg_proc p WHERE p.oid=target)<>'2353de06b4d93f45fd603380e678f4edbcc0b16a8bd9860b5967683bea5f1157'\n THEN RAISE EXCEPTION 'REVIEW_MEDIA_LOCK_POST_DRIFT'; END IF;\n\n SELECT to_jsonb(m) INTO self_membership FROM pg_auth_members m WHERE roleid=workflow_owner AND member=runner AND grantor=runner;\n IF self_membership IS NOT NULL AND (self_membership->>'admin_option')::boolean THEN RAISE EXCEPTION 'REVIEW_MEDIA_FORWARD_MEMBERSHIP_DENIED'; END IF;\n IF major=17 THEN\n IF NOT EXISTS(SELECT 1 FROM pg_auth_members WHERE roleid=workflow_owner AND member=runner AND admin_option AND grantor<>runner)\n THEN RAISE EXCEPTION 'REVIEW_MEDIA_FORWARD_OWNER_AUTHORITY_MISSING'; END IF;\n EXECUTE 'GRANT privacy_workflow_owner TO postgres WITH ADMIN FALSE,INHERIT FALSE,SET TRUE GRANTED BY postgres';\n ELSE\n pg15_lease:=NOT pg_has_role(runner,workflow_owner,'MEMBER');\n IF pg15_lease THEN GRANT privacy_workflow_owner TO postgres; END IF;\n END IF;\n SET LOCAL ROLE privacy_workflow_owner;\n SELECT jsonb_agg(to_jsonb(m) ORDER BY manifest_kind,manifest_key) INTO before_manifest FROM privacy_retention.g014_catalog_contract_manifest m;\n\n target:='public.mutate_review_with_media(uuid,uuid,text,timestamp with time zone,text,text[],text[])'::regprocedure;\n SELECT pg_get_functiondef(p.oid),p.prosrc,to_jsonb(p)-ARRAY['prosrc','proargdefaults'],pg_get_expr(p.proargdefaults,0)\n INTO definition,source,metadata,defaults FROM pg_proc p WHERE p.oid=target;\n IF encode(sha256(convert_to(source,'UTF8')),'hex')<>'50bfad7bc4fc7ddbc9d9379e8eb13facd938a15f66553576cc69fbcf69aa98b1' THEN RAISE EXCEPTION 'REVIEW_MEDIA_LOCK_SOURCE_DRIFT'; END IF;\n patched:=source;\n IF (length(patched)-length(replace(patched,$old$PERFORM pg_advisory_xact_lock(741093, 1);$old$,'')))/length($old$PERFORM pg_advisory_xact_lock(741093, 1);$old$)<>1 THEN RAISE EXCEPTION 'REVIEW_MEDIA_LOCK_ANCHOR_DRIFT'; END IF;\n patched:=replace(patched,$old$PERFORM pg_advisory_xact_lock(741093, 1);$old$,$new$LOCK TABLE public.reviews IN ROW EXCLUSIVE MODE;\n PERFORM pg_advisory_xact_lock(741093, 1);$new$);\n EXECUTE replace(definition,source,patched);\n IF (SELECT to_jsonb(p)-ARRAY['prosrc','proargdefaults'] FROM pg_proc p WHERE p.oid=target) IS DISTINCT FROM metadata\n OR (SELECT pg_get_expr(p.proargdefaults,0) FROM pg_proc p WHERE p.oid=target) IS DISTINCT FROM defaults\n OR (SELECT encode(sha256(convert_to(p.prosrc,'UTF8')),'hex') FROM pg_proc p WHERE p.oid=target)<>'d5d6646a4294c0e7bd0cd75ea47a996c9c57bac6c32a39ac53fbb7a8b1e7e323'\n THEN RAISE EXCEPTION 'REVIEW_MEDIA_LOCK_POST_DRIFT'; END IF;\n\n EXECUTE $rpc$CREATE FUNCTION public.admin_review_verification_cleanup(p_review_id uuid,p_phase text,p_paths text[] DEFAULT NULL)\n RETURNS jsonb LANGUAGE plpgsql SECURITY DEFINER SET search_path='' AS $cleanup$\nDECLARE items jsonb;\nBEGIN\n IF p_review_id IS NULL OR p_phase IS NULL OR p_phase NOT IN ('prepare','finish')\n OR current_setting('transaction_isolation') <> 'read committed'\n OR (p_phase = 'prepare' AND p_paths IS NOT NULL)\n OR (p_phase = 'finish' AND (p_paths IS NULL OR cardinality(p_paths) > 20\n OR EXISTS (SELECT 1 FROM unnest(p_paths) x WHERE x IS NULL OR octet_length(x) > 8192)\n OR cardinality(p_paths) <> (SELECT count(DISTINCT x) FROM unnest(p_paths) x))) THEN\n RAISE EXCEPTION 'REVIEW_ADMIN_CLEANUP_INVALID';\n END IF;\n PERFORM pg_advisory_xact_lock(741093, 1);\n IF p_phase = 'prepare' THEN\n UPDATE review_media_private.cleanup c SET retired = true\n WHERE c.review_id = p_review_id AND c.purpose = 'verification'\n AND NOT c.retired AND NOT c.complete AND NOT review_media_private.referenced(c.path);\n SELECT coalesce(jsonb_agg(to_jsonb(x) ORDER BY x.path),'[]'::jsonb) INTO items\n FROM (SELECT c.path,c.owner_id,c.review_id,c.purpose FROM review_media_private.cleanup c\n WHERE c.review_id = p_review_id AND c.purpose = 'verification' AND c.retired AND NOT c.complete\n AND NOT review_media_private.referenced(c.path)\n AND (review_media_private.canonical(c.path,c.owner_id,c.review_id,c.purpose)\n OR review_media_private.owned_legacy(c.path,c.owner_id,c.purpose))\n ORDER BY c.path LIMIT 20) x;\n RETURN jsonb_build_object('status','READY','items',items);\n END IF;\n UPDATE review_media_private.cleanup c SET complete = true\n WHERE c.review_id = p_review_id AND c.purpose = 'verification' AND c.retired AND NOT c.complete\n AND c.path = ANY(p_paths)\n AND (review_media_private.canonical(c.path,c.owner_id,c.review_id,c.purpose)\n OR review_media_private.owned_legacy(c.path,c.owner_id,c.purpose))\n AND NOT review_media_private.referenced(c.path)\n AND NOT EXISTS (SELECT 1 FROM storage.objects o\n WHERE o.bucket_id IN ('review-photos','review-verifications') AND o.name = c.path);\n RETURN jsonb_build_object('remaining',(SELECT count(*) FROM review_media_private.cleanup c\n WHERE c.review_id = p_review_id AND c.purpose = 'verification' AND NOT c.complete));\nEND\n$cleanup$;$rpc$;\n REVOKE ALL ON FUNCTION public.admin_review_verification_cleanup(uuid,text,text[]) FROM PUBLIC,anon,authenticated,service_role;\n GRANT EXECUTE ON FUNCTION public.admin_review_verification_cleanup(uuid,text,text[]) TO service_role;\n INSERT INTO privacy_retention.g014_public_rpc_allowlist(function_schema,function_name,identity_arguments,grantee,source_signature)\n SELECT 'public',p.proname,p.proargtypes::text,'service_role','public.admin_review_verification_cleanup(uuid,text,text[])'\n FROM pg_proc p WHERE p.oid='public.admin_review_verification_cleanup(uuid,text,text[])'::regprocedure;\n IF to_regprocedure('pg_temp.admin_media_forward_assert()') IS NOT NULL THEN RAISE EXCEPTION 'REVIEW_MEDIA_FORWARD_BRIDGE_EXISTS'; END IF;\n CREATE FUNCTION pg_temp.admin_media_forward_assert() RETURNS void LANGUAGE plpgsql SECURITY DEFINER SET search_path='' AS $bridge$\n BEGIN\n PERFORM privacy_retention.assert_g014_public_rpc_allowlist();\n PERFORM privacy_retention.assert_g014_definer_contract();\n PERFORM privacy_retention.assert_g014_catalog_contract();\n DROP FUNCTION pg_temp.admin_media_forward_assert();\n END $bridge$;\n REVOKE ALL ON FUNCTION pg_temp.admin_media_forward_assert() FROM PUBLIC,anon,authenticated,service_role;\n GRANT EXECUTE ON FUNCTION pg_temp.admin_media_forward_assert() TO postgres;\n IF (SELECT jsonb_agg(to_jsonb(m) ORDER BY manifest_kind,manifest_key) FROM privacy_retention.g014_catalog_contract_manifest m) IS DISTINCT FROM before_manifest THEN RAISE EXCEPTION 'REVIEW_MEDIA_FORWARD_MANIFEST_DRIFT'; END IF;\n RESET ROLE;\n IF major=15 THEN\n IF pg15_lease THEN REVOKE privacy_workflow_owner FROM postgres; END IF;\n ELSIF self_membership IS NULL THEN\n REVOKE privacy_workflow_owner FROM postgres GRANTED BY postgres;\n ELSE\n EXECUTE format('GRANT privacy_workflow_owner TO postgres WITH ADMIN FALSE,INHERIT %s,SET %s GRANTED BY postgres',\n upper(self_membership->>'inherit_option'),upper(self_membership->>'set_option'));\n END IF;\n IF (SELECT coalesce(jsonb_agg(to_jsonb(m) ORDER BY roleid,member,grantor),'[]') FROM pg_auth_members m) IS DISTINCT FROM before_members\n OR (SELECT jsonb_agg(to_jsonb(p) ORDER BY oid) FROM pg_proc p WHERE pronamespace='privacy_retention'::regnamespace) IS DISTINCT FROM before_assertions\n THEN RAISE EXCEPTION 'REVIEW_MEDIA_FORWARD_PRESERVATION_DRIFT'; END IF;\n PERFORM pg_temp.admin_media_forward_assert();\n IF to_regprocedure('pg_temp.admin_media_forward_assert()') IS NOT NULL THEN RAISE EXCEPTION 'REVIEW_MEDIA_FORWARD_BRIDGE_REMAINING'; END IF;\nEND $forward$",
+ "-- ADMIN_REVIEW_MEDIA_READBACK_BEGIN\nDO $readback$\nBEGIN\n IF NOT EXISTS(SELECT 1 FROM pg_proc p WHERE p.oid='public.admin_review_verification_cleanup(uuid,text,text[])'::regprocedure\n AND p.proowner='privacy_workflow_owner'::regrole AND p.prosecdef AND p.proconfig=ARRAY['search_path=\"\"']::text[]\n AND p.pronargs=3 AND p.pronargdefaults=1 AND p.prorettype='jsonb'::regtype AND NOT p.proretset\n AND p.provolatile='v' AND p.prokind='f' AND NOT p.proisstrict\n AND p.proargnames=ARRAY['p_review_id','p_phase','p_paths']::text[]\n AND pg_get_expr(p.proargdefaults,0)='NULL::text[]'\n AND p.prolang=(SELECT oid FROM pg_language WHERE lanname='plpgsql')\n AND NOT EXISTS(SELECT 1 FROM aclexplode(coalesce(p.proacl,acldefault('f',p.proowner))) a\n WHERE a.is_grantable OR a.privilege_type<>'EXECUTE'\n OR a.grantee NOT IN ('privacy_workflow_owner'::regrole,'service_role'::regrole))\n AND encode(sha256(convert_to(p.prosrc,'UTF8')),'hex')='f4d19bc5b41fe7808635240cae80f796d12f6cad8b13802577a88afbb1f024a7'\n AND NOT has_function_privilege('anon',p.oid,'EXECUTE') AND NOT has_function_privilege('authenticated',p.oid,'EXECUTE')\n AND has_function_privilege('service_role',p.oid,'EXECUTE'))\n OR (SELECT encode(sha256(convert_to(prosrc,'UTF8')),'hex') FROM pg_proc WHERE oid='public.admin_record_action(uuid,text,uuid,text,uuid[],jsonb,text)'::regprocedure)<>'2353de06b4d93f45fd603380e678f4edbcc0b16a8bd9860b5967683bea5f1157'\n OR (SELECT encode(sha256(convert_to(prosrc,'UTF8')),'hex') FROM pg_proc WHERE oid='public.mutate_review_with_media(uuid,uuid,text,timestamp with time zone,text,text[],text[])'::regprocedure)<>'d5d6646a4294c0e7bd0cd75ea47a996c9c57bac6c32a39ac53fbb7a8b1e7e323'\n THEN RAISE EXCEPTION 'REVIEW_MEDIA_FORWARD_READBACK_DRIFT'; END IF;\nEND $readback$",
+ "-- ADMIN_REVIEW_MEDIA_READBACK_END\nNOTIFY pgrst,'reload schema'",
+ "COMMIT"
+ ]
+ },
+ {
+ "id": "review_legacy_shared_cleanup",
+ "version": "20261011085342",
+ "name": "review_legacy_shared_cleanup",
+ "path": "backend/supabase/migrations/20261011085342_review_legacy_shared_cleanup.sql",
+ "bytes": 7661,
+ "sha256": "623236ddbd87059b94c84486f9e14822dba0190d2448656259991bd1345a434f",
+ "statementCount": 6,
+ "statementVectorSha256": "b7b7cc3636ba9496aea569ec18ddeeeb18dbd97e20228e3490a41dd37f6f3956",
+ "originalStatementVector": [
+ "-- Unreferenced shared legacy receipts follow their last authoritative review.\n-- Applied SQL, grants, actor authorization and canonical key ownership stay intact.\nBEGIN",
+ "SET LOCAL lock_timeout='2s'",
+ "SELECT pg_advisory_xact_lock(hashtextextended('tzudong:review-media-catalog:v2',0))",
+ "DO $forward$\nDECLARE target oid:='review_media_private.enqueue_removed()'::regprocedure;\n definition text; source text; patched text; metadata jsonb;\n before_members jsonb; before_assertions jsonb; before_manifest jsonb; self_membership jsonb;\n before_schema jsonb; schema_lease boolean;\n workflow_owner oid:='privacy_workflow_owner'::regrole; runner oid:='postgres'::regrole;\n major integer:=current_setting('server_version_num')::integer/10000; pg15_lease boolean:=false;\nBEGIN\n IF current_user<>'postgres' OR session_user<>'postgres' OR major NOT IN (15,17)\n OR current_setting('transaction_read_only')<>'off' OR NOT EXISTS(SELECT 1 FROM pg_roles\n WHERE oid=workflow_owner AND NOT rolsuper AND NOT rolbypassrls AND NOT rolcanlogin AND NOT rolinherit)\n THEN RAISE EXCEPTION 'REVIEW_SHARED_EXECUTOR_DENIED'; END IF;\n SELECT coalesce(jsonb_agg(to_jsonb(m) ORDER BY roleid,member,grantor),'[]') INTO before_members FROM pg_auth_members m;\n SELECT jsonb_agg(to_jsonb(p) ORDER BY oid) INTO before_assertions FROM pg_proc p WHERE pronamespace='privacy_retention'::regnamespace;\n SELECT pg_get_functiondef(p.oid),p.prosrc,to_jsonb(p)-'prosrc' INTO definition,source,metadata FROM pg_proc p WHERE p.oid=target;\n IF metadata->>'proowner'<>workflow_owner::text OR encode(sha256(convert_to(source,'UTF8')),'hex')<>'7edb21d70ea3ff81ab90e13617e8c1c2749b54b1506f1ff42aaa6c5dc33f9e38'\n THEN RAISE EXCEPTION 'REVIEW_SHARED_SOURCE_DRIFT'; END IF;\n SELECT to_jsonb(n) INTO before_schema FROM pg_namespace n WHERE n.oid='review_media_private'::regnamespace;\n IF before_schema->>'nspowner'<>runner::text THEN RAISE EXCEPTION 'REVIEW_SHARED_SCHEMA_OWNER_DRIFT'; END IF;\n schema_lease:=NOT has_schema_privilege(workflow_owner,'review_media_private','CREATE');\n IF schema_lease THEN GRANT CREATE ON SCHEMA review_media_private TO privacy_workflow_owner; END IF;\n SELECT to_jsonb(m) INTO self_membership FROM pg_auth_members m WHERE roleid=workflow_owner AND member=runner AND grantor=runner;\n IF self_membership IS NOT NULL AND (self_membership->>'admin_option')::boolean THEN RAISE EXCEPTION 'REVIEW_MEDIA_FORWARD_MEMBERSHIP_DENIED'; END IF;\n IF major=17 THEN\n IF NOT EXISTS(SELECT 1 FROM pg_auth_members WHERE roleid=workflow_owner AND member=runner AND admin_option AND grantor<>runner)\n THEN RAISE EXCEPTION 'REVIEW_MEDIA_FORWARD_OWNER_AUTHORITY_MISSING'; END IF;\n EXECUTE 'GRANT privacy_workflow_owner TO postgres WITH ADMIN FALSE,INHERIT FALSE,SET TRUE GRANTED BY postgres';\n ELSE\n pg15_lease:=NOT pg_has_role(runner,workflow_owner,'MEMBER');\n IF pg15_lease THEN GRANT privacy_workflow_owner TO postgres; END IF;\n END IF;\n SET LOCAL ROLE privacy_workflow_owner;\n SELECT jsonb_agg(to_jsonb(m) ORDER BY manifest_kind,manifest_key) INTO before_manifest FROM privacy_retention.g014_catalog_contract_manifest m;\n\n IF (length(source)-length(replace(source,$old$INSERT INTO review_media_private.cleanup(path, owner_id, review_id, purpose, retired)\n VALUES (p, OLD.user_id, OLD.id, purpose, NOT review_media_private.referenced(p))\n ON CONFLICT DO NOTHING;$old$,'')))/length($old$INSERT INTO review_media_private.cleanup(path, owner_id, review_id, purpose, retired)\n VALUES (p, OLD.user_id, OLD.id, purpose, NOT review_media_private.referenced(p))\n ON CONFLICT DO NOTHING;$old$)<>1\n THEN RAISE EXCEPTION 'REVIEW_SHARED_ANCHOR_DRIFT'; END IF;\n patched:=replace(source,$old$INSERT INTO review_media_private.cleanup(path, owner_id, review_id, purpose, retired)\n VALUES (p, OLD.user_id, OLD.id, purpose, NOT review_media_private.referenced(p))\n ON CONFLICT DO NOTHING;$old$,$new$INSERT INTO review_media_private.cleanup AS queued(path, owner_id, review_id, purpose, retired)\n VALUES (p, OLD.user_id, OLD.id, purpose, NOT review_media_private.referenced(p))\n ON CONFLICT (path) DO UPDATE SET review_id = EXCLUDED.review_id,\n retired = queued.retired OR EXCLUDED.retired\n WHERE queued.owner_id = EXCLUDED.owner_id AND queued.purpose = EXCLUDED.purpose\n AND queued.purpose = 'verification' AND NOT queued.retired AND NOT queued.complete\n AND review_media_private.owned_legacy(queued.path, queued.owner_id, queued.purpose);$new$);\n EXECUTE replace(definition,source,patched);\n IF (SELECT to_jsonb(p)-'prosrc' FROM pg_proc p WHERE p.oid=target) IS DISTINCT FROM metadata\n OR (SELECT encode(sha256(convert_to(p.prosrc,'UTF8')),'hex') FROM pg_proc p WHERE p.oid=target)<>'cbf6a87e453e8c2e14d67acd73a92e399fbba4b4f24ac27966806e13ef1fd905'\n THEN RAISE EXCEPTION 'REVIEW_SHARED_POST_DRIFT'; END IF;\n CREATE FUNCTION pg_temp.review_shared_assert() RETURNS void LANGUAGE plpgsql SECURITY DEFINER SET search_path='' AS $bridge$\n BEGIN\n PERFORM privacy_retention.assert_g014_public_rpc_allowlist();\n PERFORM privacy_retention.assert_g014_definer_contract();\n PERFORM privacy_retention.assert_g014_catalog_contract();\n DROP FUNCTION pg_temp.review_shared_assert();\n END $bridge$;\n REVOKE ALL ON FUNCTION pg_temp.review_shared_assert() FROM PUBLIC,anon,authenticated,service_role;\n GRANT EXECUTE ON FUNCTION pg_temp.review_shared_assert() TO postgres;\n IF (SELECT jsonb_agg(to_jsonb(m) ORDER BY manifest_kind,manifest_key) FROM privacy_retention.g014_catalog_contract_manifest m) IS DISTINCT FROM before_manifest\n THEN RAISE EXCEPTION 'REVIEW_SHARED_MANIFEST_DRIFT'; END IF;\n RESET ROLE;\n IF schema_lease THEN REVOKE CREATE ON SCHEMA review_media_private FROM privacy_workflow_owner; END IF;\n IF major=15 THEN\n IF pg15_lease THEN REVOKE privacy_workflow_owner FROM postgres; END IF;\n ELSIF self_membership IS NULL THEN\n REVOKE privacy_workflow_owner FROM postgres GRANTED BY postgres;\n ELSE\n EXECUTE format('GRANT privacy_workflow_owner TO postgres WITH ADMIN FALSE,INHERIT %s,SET %s GRANTED BY postgres',\n upper(self_membership->>'inherit_option'),upper(self_membership->>'set_option'));\n END IF;\n\n IF (SELECT coalesce(jsonb_agg(to_jsonb(m) ORDER BY roleid,member,grantor),'[]') FROM pg_auth_members m) IS DISTINCT FROM before_members\n OR (SELECT jsonb_agg(to_jsonb(p) ORDER BY oid) FROM pg_proc p WHERE pronamespace='privacy_retention'::regnamespace) IS DISTINCT FROM before_assertions\n OR (SELECT to_jsonb(n) FROM pg_namespace n WHERE n.oid='review_media_private'::regnamespace) IS DISTINCT FROM before_schema\n THEN RAISE EXCEPTION 'REVIEW_SHARED_PRESERVATION_DRIFT'; END IF;\n PERFORM pg_temp.review_shared_assert();\nEND $forward$",
+ "-- REVIEW_SHARED_MEDIA_READBACK_BEGIN\nDO $readback$\nBEGIN\n IF NOT EXISTS(SELECT 1 FROM pg_proc p WHERE p.oid='review_media_private.enqueue_removed()'::regprocedure\n AND p.proowner='privacy_workflow_owner'::regrole AND p.prosecdef AND p.proconfig=ARRAY['search_path=\"\"']::text[]\n AND p.prorettype='trigger'::regtype AND p.provolatile='v' AND p.pronargs=0\n AND p.prolang=(SELECT oid FROM pg_language WHERE lanname='plpgsql')\n AND encode(sha256(convert_to(p.prosrc,'UTF8')),'hex')='cbf6a87e453e8c2e14d67acd73a92e399fbba4b4f24ac27966806e13ef1fd905'\n AND NOT has_function_privilege('anon',p.oid,'EXECUTE')\n AND NOT has_function_privilege('authenticated',p.oid,'EXECUTE')\n AND NOT has_function_privilege('service_role',p.oid,'EXECUTE'))\n THEN RAISE EXCEPTION 'REVIEW_SHARED_READBACK_DRIFT'; END IF;\nEND $readback$",
+ "-- REVIEW_SHARED_MEDIA_READBACK_END\nCOMMIT"
+ ]
+ },
+ {
+ "id": "admin_evaluation_revision_safeupdate",
+ "version": "20261011094346",
+ "name": "admin_evaluation_revision_safeupdate",
+ "path": "backend/supabase/migrations/20261011094346_admin_evaluation_revision_safeupdate.sql",
+ "bytes": 3666,
+ "sha256": "b7061015ea07192dccfcf0774d5548a9c2a584c7daa140d77fccd7c75cb8bf88",
+ "statementCount": 6,
+ "statementVectorSha256": "8cfd8d94d5419712b767b1360361ed905b151e45c8ba3d9cca8d7fe3f1adbd69",
+ "originalStatementVector": [
+ "-- Keep Supabase safeupdate enabled while advancing the single catalog revision.\n-- This is a forward, source-bound replacement; applied migrations are immutable.\nBEGIN",
+ "SET LOCAL lock_timeout='2s'",
+ "SELECT pg_advisory_xact_lock(hashtextextended('tzudong:admin-revision-safeupdate:v1',0))",
+ "DO $forward$\nDECLARE target oid:='pipeline_control.bump_admin_evaluation_revision()'::regprocedure;\n definition text; source text; patched text; metadata jsonb;\n before_members jsonb; before_manifest jsonb; before_schema jsonb;\nBEGIN\n IF current_user<>'postgres' OR session_user<>'postgres'\n OR current_setting('transaction_read_only')<>'off'\n OR current_setting('server_version_num')::integer/10000 NOT IN (15,17)\n THEN RAISE EXCEPTION 'ADMIN_REVISION_EXECUTOR_DENIED'; END IF;\n SELECT coalesce(jsonb_agg(to_jsonb(m) ORDER BY roleid,member,grantor),'[]') INTO before_members FROM pg_auth_members m;\n SELECT jsonb_agg(to_jsonb(m) ORDER BY manifest_kind,manifest_key) INTO before_manifest FROM privacy_retention.g014_catalog_contract_manifest m;\n SELECT to_jsonb(n) INTO before_schema FROM pg_namespace n WHERE n.oid='pipeline_control'::regnamespace;\n SELECT pg_get_functiondef(p.oid),p.prosrc,to_jsonb(p)-'prosrc' INTO definition,source,metadata FROM pg_proc p WHERE p.oid=target;\n IF metadata->>'proowner'<>'postgres'::regrole::oid::text\n OR encode(sha256(convert_to(source,'UTF8')),'hex')<>'ed16226885c03b05cb459381cc5ef7c8352506f7ea1f88ae526a6511e4cc1b20'\n OR (SELECT count(*) FROM pipeline_control.admin_evaluation_catalog_revision WHERE singleton)<>1\n THEN RAISE EXCEPTION 'ADMIN_REVISION_SOURCE_DRIFT'; END IF;\n IF (length(source)-length(replace(source,$old$UPDATE pipeline_control.admin_evaluation_catalog_revision SET revision = revision + 1;$old$,'')))/length($old$UPDATE pipeline_control.admin_evaluation_catalog_revision SET revision = revision + 1;$old$)<>1\n THEN RAISE EXCEPTION 'ADMIN_REVISION_ANCHOR_DRIFT'; END IF;\n patched:=replace(source,$old$UPDATE pipeline_control.admin_evaluation_catalog_revision SET revision = revision + 1;$old$,$new$UPDATE pipeline_control.admin_evaluation_catalog_revision SET revision = revision + 1 WHERE singleton;$new$);\n EXECUTE replace(definition,source,patched);\n IF (SELECT to_jsonb(p)-'prosrc' FROM pg_proc p WHERE p.oid=target) IS DISTINCT FROM metadata\n OR (SELECT encode(sha256(convert_to(p.prosrc,'UTF8')),'hex') FROM pg_proc p WHERE p.oid=target)<>'4a3bf9e7cc7d91fb1a2a5195d01f40de728539daad1a0a8132a7c10404dc7936'\n OR (SELECT coalesce(jsonb_agg(to_jsonb(m) ORDER BY roleid,member,grantor),'[]') FROM pg_auth_members m) IS DISTINCT FROM before_members\n OR (SELECT jsonb_agg(to_jsonb(m) ORDER BY manifest_kind,manifest_key) FROM privacy_retention.g014_catalog_contract_manifest m) IS DISTINCT FROM before_manifest\n OR (SELECT to_jsonb(n) FROM pg_namespace n WHERE n.oid='pipeline_control'::regnamespace) IS DISTINCT FROM before_schema\n THEN RAISE EXCEPTION 'ADMIN_REVISION_POST_DRIFT'; END IF;\nEND $forward$",
+ "-- ADMIN_REVISION_READBACK_BEGIN\nDO $readback$\nBEGIN\n IF NOT EXISTS(SELECT 1 FROM pg_proc WHERE oid='pipeline_control.bump_admin_evaluation_revision()'::regprocedure\n AND proowner='postgres'::regrole AND prosecdef AND proconfig=ARRAY['search_path=\"\"']::text[]\n AND proacl=ARRAY['postgres=X/postgres']::aclitem[]\n AND encode(sha256(convert_to(prosrc,'UTF8')),'hex')='4a3bf9e7cc7d91fb1a2a5195d01f40de728539daad1a0a8132a7c10404dc7936')\n OR (SELECT count(*) FROM pipeline_control.admin_evaluation_catalog_revision WHERE singleton)<>1\n THEN RAISE EXCEPTION 'ADMIN_REVISION_READBACK_DRIFT'; END IF;\nEND $readback$",
+ "-- ADMIN_REVISION_READBACK_END\nCOMMIT"
+ ]
+ },
+ {
+ "id": "admin_record_expired_preview_readback",
+ "version": "20261011100730",
+ "name": "admin_record_expired_preview_readback",
+ "path": "backend/supabase/migrations/20261011100730_admin_record_expired_preview_readback.sql",
+ "bytes": 4163,
+ "sha256": "26c64074bb98e718ba96732ac2baeedb851afe6ff745e0ddb5ca075359892472",
+ "statementCount": 6,
+ "statementVectorSha256": "afa3246f159064fa19046cc9da75c3cc215150dc78ad8cf80f887f6263966f5a",
+ "originalStatementVector": [
+ "-- A locked, expired preview is definitively unapplied; never infer this from a client clock.\n-- Applied receipts remain recoverable after expiry. No Apply is retried here.\nBEGIN",
+ "SET LOCAL lock_timeout='2s'",
+ "SELECT pg_advisory_xact_lock(hashtextextended('tzudong:admin-preview-expiry:v1',0))",
+ "DO $forward$\nDECLARE target oid:='public.admin_record_action(uuid,text,uuid,text,uuid[],jsonb,text)'::regprocedure;\n definition text; source text; patched text; metadata jsonb; defaults text;\n before_members jsonb; before_manifest jsonb; before_schema jsonb;\nBEGIN\n IF current_user<>'postgres' OR session_user<>'postgres'\n OR current_setting('transaction_read_only')<>'off'\n OR current_setting('server_version_num')::integer/10000 NOT IN (15,17)\n THEN RAISE EXCEPTION 'ADMIN_PREVIEW_EXECUTOR_DENIED'; END IF;\n SELECT coalesce(jsonb_agg(to_jsonb(m) ORDER BY roleid,member,grantor),'[]') INTO before_members FROM pg_auth_members m;\n SELECT jsonb_agg(to_jsonb(m) ORDER BY manifest_kind,manifest_key) INTO before_manifest FROM privacy_retention.g014_catalog_contract_manifest m;\n SELECT to_jsonb(n) INTO before_schema FROM pg_namespace n WHERE n.oid='public'::regnamespace;\n SELECT pg_get_functiondef(p.oid),p.prosrc,to_jsonb(p)-ARRAY['prosrc','proargdefaults'],pg_get_expr(p.proargdefaults,0)\n INTO definition,source,metadata,defaults FROM pg_proc p WHERE p.oid=target;\n IF metadata->>'proowner'<>'postgres'::regrole::oid::text\n OR encode(sha256(convert_to(source,'UTF8')),'hex')<>'2353de06b4d93f45fd603380e678f4edbcc0b16a8bd9860b5967683bea5f1157'\n THEN RAISE EXCEPTION 'ADMIN_PREVIEW_SOURCE_DRIFT'; END IF;\n IF (length(source)-length(replace(source,$old$IF op.id IS NULL THEN RAISE EXCEPTION 'RECORD_ACTION_NOT_FOUND'; END IF;\n RETURN op.receipt||$old$,'')))/length($old$IF op.id IS NULL THEN RAISE EXCEPTION 'RECORD_ACTION_NOT_FOUND'; END IF;\n RETURN op.receipt||$old$)<>1\n THEN RAISE EXCEPTION 'ADMIN_PREVIEW_ANCHOR_DRIFT'; END IF;\n patched:=replace(source,$old$IF op.id IS NULL THEN RAISE EXCEPTION 'RECORD_ACTION_NOT_FOUND'; END IF;\n RETURN op.receipt||$old$,$new$IF op.id IS NULL THEN RAISE EXCEPTION 'RECORD_ACTION_NOT_FOUND'; END IF;\n IF op.state='preview' AND op.expires_at<=clock_timestamp() THEN RAISE EXCEPTION 'RECORD_ACTION_PREVIEW_EXPIRED'; END IF;\n RETURN op.receipt||$new$);\n EXECUTE replace(definition,source,patched);\n IF (SELECT to_jsonb(p)-ARRAY['prosrc','proargdefaults'] FROM pg_proc p WHERE p.oid=target) IS DISTINCT FROM metadata\n OR (SELECT pg_get_expr(p.proargdefaults,0) FROM pg_proc p WHERE p.oid=target) IS DISTINCT FROM defaults\n OR (SELECT encode(sha256(convert_to(p.prosrc,'UTF8')),'hex') FROM pg_proc p WHERE p.oid=target)<>'50b5900a7b6ca43062d6373dbba75378d714312c9ef02c6939997389f6833ba0'\n OR (SELECT coalesce(jsonb_agg(to_jsonb(m) ORDER BY roleid,member,grantor),'[]') FROM pg_auth_members m) IS DISTINCT FROM before_members\n OR (SELECT jsonb_agg(to_jsonb(m) ORDER BY manifest_kind,manifest_key) FROM privacy_retention.g014_catalog_contract_manifest m) IS DISTINCT FROM before_manifest\n OR (SELECT to_jsonb(n) FROM pg_namespace n WHERE n.oid='public'::regnamespace) IS DISTINCT FROM before_schema\n THEN RAISE EXCEPTION 'ADMIN_PREVIEW_POST_DRIFT'; END IF;\nEND $forward$",
+ "-- ADMIN_PREVIEW_EXPIRY_READBACK_BEGIN\nDO $readback$\nBEGIN\n IF NOT EXISTS(SELECT 1 FROM pg_proc WHERE oid='public.admin_record_action(uuid,text,uuid,text,uuid[],jsonb,text)'::regprocedure\n AND proowner='postgres'::regrole AND NOT prosecdef AND proconfig=ARRAY['search_path=\"\"']::text[]\n AND encode(sha256(convert_to(prosrc,'UTF8')),'hex')='50b5900a7b6ca43062d6373dbba75378d714312c9ef02c6939997389f6833ba0')\n OR has_function_privilege('anon','public.admin_record_action(uuid,text,uuid,text,uuid[],jsonb,text)','EXECUTE')\n OR has_function_privilege('authenticated','public.admin_record_action(uuid,text,uuid,text,uuid[],jsonb,text)','EXECUTE')\n OR NOT has_function_privilege('service_role','public.admin_record_action(uuid,text,uuid,text,uuid[],jsonb,text)','EXECUTE')\n THEN RAISE EXCEPTION 'ADMIN_PREVIEW_READBACK_DRIFT'; END IF;\nEND $readback$",
+ "-- ADMIN_PREVIEW_EXPIRY_READBACK_END\nCOMMIT"
+ ]
+ },
+ {
+ "id": "admin_verification_durable_staging",
+ "version": "20261011103501",
+ "name": "admin_verification_durable_staging",
+ "path": "backend/supabase/migrations/20261011103501_admin_verification_durable_staging.sql",
+ "bytes": 12008,
+ "sha256": "081c48e91172e4e98079b03aa210c0f7481d95a21945e5a288187263089e57db",
+ "statementCount": 6,
+ "statementVectorSha256": "9c2f696f41d5e001d574809b911ba6dc2451bbd6cc9f23684a98fe543d56b512",
+ "originalStatementVector": [
+ "-- Persist an owner-bound private verification key before Storage transport.\n-- Only retired unreferenced cleanup counts as outstanding work; shared references remain protected.\n-- Applied SQL, grants, actor authorization and canonical key ownership stay intact.\nBEGIN",
+ "SET LOCAL lock_timeout='2s'",
+ "SELECT pg_advisory_xact_lock(hashtextextended('tzudong:review-media-catalog:v2',0))",
+ "DO $forward$\nDECLARE target oid:='public.admin_review_verification_cleanup(uuid,text,text[])'::regprocedure;\n definition text; source text; patched text; metadata jsonb; defaults text;\n before_members jsonb; before_assertions jsonb; before_manifest jsonb; self_membership jsonb;\n before_schema jsonb; schema_lease boolean;\n workflow_owner oid:='privacy_workflow_owner'::regrole; runner oid:='postgres'::regrole;\n major integer:=current_setting('server_version_num')::integer/10000; pg15_lease boolean:=false;\nBEGIN\n IF current_user<>'postgres' OR session_user<>'postgres' OR major NOT IN (15,17)\n OR current_setting('transaction_read_only')<>'off' OR NOT EXISTS(SELECT 1 FROM pg_roles\n WHERE oid=workflow_owner AND NOT rolsuper AND NOT rolbypassrls AND NOT rolcanlogin AND NOT rolinherit)\n THEN RAISE EXCEPTION 'REVIEW_SHARED_EXECUTOR_DENIED'; END IF;\n SELECT coalesce(jsonb_agg(to_jsonb(m) ORDER BY roleid,member,grantor),'[]') INTO before_members FROM pg_auth_members m;\n SELECT jsonb_agg(to_jsonb(p) ORDER BY oid) INTO before_assertions FROM pg_proc p WHERE pronamespace='privacy_retention'::regnamespace;\n SELECT pg_get_functiondef(p.oid),p.prosrc,to_jsonb(p)-ARRAY['prosrc','proargdefaults'],pg_get_expr(p.proargdefaults,0) INTO definition,source,metadata,defaults FROM pg_proc p WHERE p.oid=target;\n IF metadata->>'proowner'<>workflow_owner::text OR encode(sha256(convert_to(source,'UTF8')),'hex')<>'f4d19bc5b41fe7808635240cae80f796d12f6cad8b13802577a88afbb1f024a7'\n THEN RAISE EXCEPTION 'REVIEW_SHARED_SOURCE_DRIFT'; END IF;\n SELECT to_jsonb(n) INTO before_schema FROM pg_namespace n WHERE n.oid='public'::regnamespace;\n IF before_schema->>'nspowner' NOT IN (runner::text,'pg_database_owner'::regrole::oid::text)\n THEN RAISE EXCEPTION 'REVIEW_SHARED_SCHEMA_OWNER_DRIFT'; END IF;\n schema_lease:=NOT has_schema_privilege(workflow_owner,'public','CREATE');\n IF schema_lease THEN GRANT CREATE ON SCHEMA public TO privacy_workflow_owner; END IF;\n SELECT to_jsonb(m) INTO self_membership FROM pg_auth_members m WHERE roleid=workflow_owner AND member=runner AND grantor=runner;\n IF self_membership IS NOT NULL AND (self_membership->>'admin_option')::boolean THEN RAISE EXCEPTION 'REVIEW_MEDIA_FORWARD_MEMBERSHIP_DENIED'; END IF;\n IF major=17 THEN\n IF NOT EXISTS(SELECT 1 FROM pg_auth_members WHERE roleid=workflow_owner AND member=runner AND admin_option AND grantor<>runner)\n THEN RAISE EXCEPTION 'REVIEW_MEDIA_FORWARD_OWNER_AUTHORITY_MISSING'; END IF;\n EXECUTE 'GRANT privacy_workflow_owner TO postgres WITH ADMIN FALSE,INHERIT FALSE,SET TRUE GRANTED BY postgres';\n ELSE\n pg15_lease:=NOT pg_has_role(runner,workflow_owner,'MEMBER');\n IF pg15_lease THEN GRANT privacy_workflow_owner TO postgres; END IF;\n END IF;\n SET LOCAL ROLE privacy_workflow_owner;\n SELECT jsonb_agg(to_jsonb(m) ORDER BY manifest_kind,manifest_key) INTO before_manifest FROM privacy_retention.g014_catalog_contract_manifest m;\n\n IF source IS DISTINCT FROM $old$\nDECLARE items jsonb;\nBEGIN\n IF p_review_id IS NULL OR p_phase IS NULL OR p_phase NOT IN ('prepare','finish')\n OR current_setting('transaction_isolation') <> 'read committed'\n OR (p_phase = 'prepare' AND p_paths IS NOT NULL)\n OR (p_phase = 'finish' AND (p_paths IS NULL OR cardinality(p_paths) > 20\n OR EXISTS (SELECT 1 FROM unnest(p_paths) x WHERE x IS NULL OR octet_length(x) > 8192)\n OR cardinality(p_paths) <> (SELECT count(DISTINCT x) FROM unnest(p_paths) x))) THEN\n RAISE EXCEPTION 'REVIEW_ADMIN_CLEANUP_INVALID';\n END IF;\n PERFORM pg_advisory_xact_lock(741093, 1);\n IF p_phase = 'prepare' THEN\n UPDATE review_media_private.cleanup c SET retired = true\n WHERE c.review_id = p_review_id AND c.purpose = 'verification'\n AND NOT c.retired AND NOT c.complete AND NOT review_media_private.referenced(c.path);\n SELECT coalesce(jsonb_agg(to_jsonb(x) ORDER BY x.path),'[]'::jsonb) INTO items\n FROM (SELECT c.path,c.owner_id,c.review_id,c.purpose FROM review_media_private.cleanup c\n WHERE c.review_id = p_review_id AND c.purpose = 'verification' AND c.retired AND NOT c.complete\n AND NOT review_media_private.referenced(c.path)\n AND (review_media_private.canonical(c.path,c.owner_id,c.review_id,c.purpose)\n OR review_media_private.owned_legacy(c.path,c.owner_id,c.purpose))\n ORDER BY c.path LIMIT 20) x;\n RETURN jsonb_build_object('status','READY','items',items);\n END IF;\n UPDATE review_media_private.cleanup c SET complete = true\n WHERE c.review_id = p_review_id AND c.purpose = 'verification' AND c.retired AND NOT c.complete\n AND c.path = ANY(p_paths)\n AND (review_media_private.canonical(c.path,c.owner_id,c.review_id,c.purpose)\n OR review_media_private.owned_legacy(c.path,c.owner_id,c.purpose))\n AND NOT review_media_private.referenced(c.path)\n AND NOT EXISTS (SELECT 1 FROM storage.objects o\n WHERE o.bucket_id IN ('review-photos','review-verifications') AND o.name = c.path);\n RETURN jsonb_build_object('remaining',(SELECT count(*) FROM review_media_private.cleanup c\n WHERE c.review_id = p_review_id AND c.purpose = 'verification' AND NOT c.complete));\nEND\n$old$ THEN RAISE EXCEPTION 'REVIEW_SHARED_ANCHOR_DRIFT'; END IF;\n patched:=$new$\nDECLARE items jsonb; owner uuid;\nBEGIN\n IF p_review_id IS NULL OR p_phase IS NULL OR p_phase NOT IN ('prepare','finish','stage')\n OR current_setting('transaction_isolation') <> 'read committed'\n OR (p_phase = 'prepare' AND p_paths IS NOT NULL)\n OR (p_phase IN ('finish','stage') AND (p_paths IS NULL OR cardinality(p_paths) > 20\n OR EXISTS (SELECT 1 FROM unnest(p_paths) x WHERE x IS NULL OR octet_length(x) > 8192)\n OR cardinality(p_paths) <> (SELECT count(DISTINCT x) FROM unnest(p_paths) x))) THEN\n RAISE EXCEPTION 'REVIEW_ADMIN_CLEANUP_INVALID';\n END IF;\n PERFORM pg_advisory_xact_lock(741093, 1);\n IF p_phase = 'stage' THEN\n SELECT user_id INTO owner FROM public.reviews WHERE id=p_review_id;\n IF owner IS NULL OR cardinality(p_paths)<>1\n OR NOT review_media_private.canonical(p_paths[1],owner,p_review_id,'verification')\n THEN RAISE EXCEPTION 'REVIEW_ADMIN_CLEANUP_INVALID'; END IF;\n INSERT INTO review_media_private.cleanup(path,owner_id,review_id,purpose,retired)\n VALUES(p_paths[1],owner,p_review_id,'verification',false) ON CONFLICT DO NOTHING;\n IF NOT EXISTS(SELECT 1 FROM review_media_private.cleanup WHERE path=p_paths[1]\n AND owner_id=owner AND review_id=p_review_id AND purpose='verification'\n AND NOT retired AND NOT complete) THEN RAISE EXCEPTION 'REVIEW_ADMIN_UPLOAD_RETIRED'; END IF;\n RETURN jsonb_build_object('status','STAGED');\n END IF;\n IF p_phase = 'prepare' THEN\n UPDATE review_media_private.cleanup c SET retired = true\n WHERE c.review_id = p_review_id AND c.purpose = 'verification'\n AND NOT c.retired AND NOT c.complete AND NOT review_media_private.referenced(c.path);\n SELECT coalesce(jsonb_agg(to_jsonb(x) ORDER BY x.path),'[]'::jsonb) INTO items\n FROM (SELECT c.path,c.owner_id,c.review_id,c.purpose FROM review_media_private.cleanup c\n WHERE c.review_id = p_review_id AND c.purpose = 'verification' AND c.retired AND NOT c.complete\n AND NOT review_media_private.referenced(c.path)\n AND (review_media_private.canonical(c.path,c.owner_id,c.review_id,c.purpose)\n OR review_media_private.owned_legacy(c.path,c.owner_id,c.purpose))\n ORDER BY c.path LIMIT 20) x;\n RETURN jsonb_build_object('status','READY','items',items);\n END IF;\n UPDATE review_media_private.cleanup c SET complete = true\n WHERE c.review_id = p_review_id AND c.purpose = 'verification' AND c.retired AND NOT c.complete\n AND c.path = ANY(p_paths)\n AND (review_media_private.canonical(c.path,c.owner_id,c.review_id,c.purpose)\n OR review_media_private.owned_legacy(c.path,c.owner_id,c.purpose))\n AND NOT review_media_private.referenced(c.path)\n AND NOT EXISTS (SELECT 1 FROM storage.objects o\n WHERE o.bucket_id IN ('review-photos','review-verifications') AND o.name = c.path);\n RETURN jsonb_build_object('remaining',(SELECT count(*) FROM review_media_private.cleanup c\n WHERE c.review_id = p_review_id AND c.purpose = 'verification' AND c.retired AND NOT c.complete\n AND NOT review_media_private.referenced(c.path)));\nEND\n$new$;\n EXECUTE replace(definition,source,patched);\n IF (SELECT to_jsonb(p)-ARRAY['prosrc','proargdefaults'] FROM pg_proc p WHERE p.oid=target) IS DISTINCT FROM metadata\n OR (SELECT pg_get_expr(p.proargdefaults,0) FROM pg_proc p WHERE p.oid=target) IS DISTINCT FROM defaults\n OR (SELECT encode(sha256(convert_to(p.prosrc,'UTF8')),'hex') FROM pg_proc p WHERE p.oid=target)<>'468c12ee6c3463876cbccb394fcba8f870459a1f6cf3d945d97307f8551f094b'\n THEN RAISE EXCEPTION 'REVIEW_SHARED_POST_DRIFT'; END IF;\n CREATE FUNCTION pg_temp.review_shared_assert() RETURNS void LANGUAGE plpgsql SECURITY DEFINER SET search_path='' AS $bridge$\n BEGIN\n PERFORM privacy_retention.assert_g014_public_rpc_allowlist();\n PERFORM privacy_retention.assert_g014_definer_contract();\n PERFORM privacy_retention.assert_g014_catalog_contract();\n DROP FUNCTION pg_temp.review_shared_assert();\n END $bridge$;\n REVOKE ALL ON FUNCTION pg_temp.review_shared_assert() FROM PUBLIC,anon,authenticated,service_role;\n GRANT EXECUTE ON FUNCTION pg_temp.review_shared_assert() TO postgres;\n IF (SELECT jsonb_agg(to_jsonb(m) ORDER BY manifest_kind,manifest_key) FROM privacy_retention.g014_catalog_contract_manifest m) IS DISTINCT FROM before_manifest\n THEN RAISE EXCEPTION 'REVIEW_SHARED_MANIFEST_DRIFT'; END IF;\n RESET ROLE;\n IF schema_lease THEN REVOKE CREATE ON SCHEMA public FROM privacy_workflow_owner; END IF;\n IF major=15 THEN\n IF pg15_lease THEN REVOKE privacy_workflow_owner FROM postgres; END IF;\n ELSIF self_membership IS NULL THEN\n REVOKE privacy_workflow_owner FROM postgres GRANTED BY postgres;\n ELSE\n EXECUTE format('GRANT privacy_workflow_owner TO postgres WITH ADMIN FALSE,INHERIT %s,SET %s GRANTED BY postgres',\n upper(self_membership->>'inherit_option'),upper(self_membership->>'set_option'));\n END IF;\n\n IF (SELECT coalesce(jsonb_agg(to_jsonb(m) ORDER BY roleid,member,grantor),'[]') FROM pg_auth_members m) IS DISTINCT FROM before_members\n OR (SELECT jsonb_agg(to_jsonb(p) ORDER BY oid) FROM pg_proc p WHERE pronamespace='privacy_retention'::regnamespace) IS DISTINCT FROM before_assertions\n OR (SELECT to_jsonb(n) FROM pg_namespace n WHERE n.oid='public'::regnamespace) IS DISTINCT FROM before_schema\n THEN RAISE EXCEPTION 'REVIEW_SHARED_PRESERVATION_DRIFT'; END IF;\n PERFORM pg_temp.review_shared_assert();\nEND $forward$",
+ "-- ADMIN_STAGING_READBACK_BEGIN\nDO $readback$\nBEGIN\n IF NOT EXISTS(SELECT 1 FROM pg_proc WHERE oid='public.admin_review_verification_cleanup(uuid,text,text[])'::regprocedure\n AND proowner='privacy_workflow_owner'::regrole AND prosecdef AND proconfig=ARRAY['search_path=\"\"']::text[]\n AND encode(sha256(convert_to(prosrc,'UTF8')),'hex')='468c12ee6c3463876cbccb394fcba8f870459a1f6cf3d945d97307f8551f094b')\n OR has_function_privilege('anon','public.admin_review_verification_cleanup(uuid,text,text[])','EXECUTE')\n OR has_function_privilege('authenticated','public.admin_review_verification_cleanup(uuid,text,text[])','EXECUTE')\n OR NOT has_function_privilege('service_role','public.admin_review_verification_cleanup(uuid,text,text[])','EXECUTE')\n THEN RAISE EXCEPTION 'ADMIN_STAGING_READBACK_DRIFT'; END IF;\nEND $readback$",
+ "-- ADMIN_STAGING_READBACK_END\nCOMMIT"
+ ]
+ },
+ {
+ "id": "review_media_storage_retirement_fence",
+ "version": "20261011105114",
+ "name": "review_media_storage_retirement_fence",
+ "path": "backend/supabase/migrations/20261011105114_review_media_storage_retirement_fence.sql",
+ "bytes": 11418,
+ "sha256": "79375138d7e82a3be2b85b219ff52147db13db9c93070569be40a61ab97c3d53",
+ "statementCount": 6,
+ "statementVectorSha256": "6be5e2a7387d2e6d71e8ee830cb9a48df74b592ffd18bb51eba43869fc005c89",
+ "originalStatementVector": [
+ "-- Retirement must also fence late privileged Storage metadata commits.\n-- A BEFORE STATEMENT serializer takes the global lock before row or object locks.\n-- Applied SQL, grants, actor authorization and canonical key ownership stay intact.\nBEGIN",
+ "SET LOCAL lock_timeout='2s'",
+ "SELECT pg_advisory_xact_lock(hashtextextended('tzudong:review-media-catalog:v2',0))",
+ "DO $forward$\nDECLARE target oid:='review_media_private.guard_references()'::regprocedure;\n definition text; source text; patched text; metadata jsonb;\n before_members jsonb; before_assertions jsonb; before_manifest jsonb; self_membership jsonb;\n before_schema jsonb; schema_lease boolean;\n workflow_owner oid:='privacy_workflow_owner'::regrole; runner oid:='postgres'::regrole;\n major integer:=current_setting('server_version_num')::integer/10000; pg15_lease boolean:=false;\nBEGIN\n IF current_user<>'postgres' OR session_user<>'postgres' OR major NOT IN (15,17)\n OR current_setting('transaction_read_only')<>'off' OR NOT EXISTS(SELECT 1 FROM pg_roles\n WHERE oid=workflow_owner AND NOT rolsuper AND NOT rolbypassrls AND NOT rolcanlogin AND NOT rolinherit)\n THEN RAISE EXCEPTION 'REVIEW_SHARED_EXECUTOR_DENIED'; END IF;\n SELECT coalesce(jsonb_agg(to_jsonb(m) ORDER BY roleid,member,grantor),'[]') INTO before_members FROM pg_auth_members m;\n SELECT jsonb_agg(to_jsonb(p) ORDER BY oid) INTO before_assertions FROM pg_proc p WHERE pronamespace='privacy_retention'::regnamespace;\n SELECT pg_get_functiondef(p.oid),p.prosrc,to_jsonb(p)-'prosrc' INTO definition,source,metadata FROM pg_proc p WHERE p.oid=target;\n IF metadata->>'proowner'<>workflow_owner::text OR encode(sha256(convert_to(source,'UTF8')),'hex')<>'846030547ffd76e17596755d7eee4279dcb9b563361435260826071d2f250d59'\n THEN RAISE EXCEPTION 'REVIEW_SHARED_SOURCE_DRIFT'; END IF;\n SELECT to_jsonb(n) INTO before_schema FROM pg_namespace n WHERE n.oid='review_media_private'::regnamespace;\n IF before_schema->>'nspowner'<>runner::text THEN RAISE EXCEPTION 'REVIEW_SHARED_SCHEMA_OWNER_DRIFT'; END IF;\n schema_lease:=NOT has_schema_privilege(workflow_owner,'review_media_private','CREATE');\n IF schema_lease THEN GRANT CREATE ON SCHEMA review_media_private TO privacy_workflow_owner; END IF;\n SELECT to_jsonb(m) INTO self_membership FROM pg_auth_members m WHERE roleid=workflow_owner AND member=runner AND grantor=runner;\n IF self_membership IS NOT NULL AND (self_membership->>'admin_option')::boolean THEN RAISE EXCEPTION 'REVIEW_MEDIA_FORWARD_MEMBERSHIP_DENIED'; END IF;\n IF major=17 THEN\n IF NOT EXISTS(SELECT 1 FROM pg_auth_members WHERE roleid=workflow_owner AND member=runner AND admin_option AND grantor<>runner)\n THEN RAISE EXCEPTION 'REVIEW_MEDIA_FORWARD_OWNER_AUTHORITY_MISSING'; END IF;\n EXECUTE 'GRANT privacy_workflow_owner TO postgres WITH ADMIN FALSE,INHERIT FALSE,SET TRUE GRANTED BY postgres';\n ELSE\n pg15_lease:=NOT pg_has_role(runner,workflow_owner,'MEMBER');\n IF pg15_lease THEN GRANT privacy_workflow_owner TO postgres; END IF;\n END IF;\n SET LOCAL ROLE privacy_workflow_owner;\n SELECT jsonb_agg(to_jsonb(m) ORDER BY manifest_kind,manifest_key) INTO before_manifest FROM privacy_retention.g014_catalog_contract_manifest m;\n\n IF source IS DISTINCT FROM $old$\nDECLARE p text; old_food text[] := ARRAY[]::text[]; old_verification text; purpose text;\nBEGIN\n IF TG_OP = 'UPDATE' AND NEW.user_id = OLD.user_id AND NEW.id = OLD.id THEN\n old_food := COALESCE(OLD.food_photos, ARRAY[]::text[]);\n old_verification := OLD.verification_photo;\n END IF;\n FOR p, purpose IN SELECT value, 'food' FROM unnest(COALESCE(NEW.food_photos, ARRAY[]::text[])) value\n UNION ALL SELECT NEW.verification_photo, 'verification' LOOP\n IF EXISTS (SELECT 1 FROM review_media_private.cleanup c\n WHERE c.path = review_media_private.reference_key(p) AND c.retired) THEN\n RAISE EXCEPTION USING MESSAGE = 'REVIEW_MEDIA_RETIRED', ERRCODE = '23514';\n END IF;\n -- Historical keys may remain unchanged in their original purpose only.\n IF NOT COALESCE(CASE WHEN purpose = 'food' THEN p = ANY(old_food) ELSE p = old_verification END, false) THEN\n IF NOT review_media_private.canonical(p, NEW.user_id, NEW.id, purpose) THEN\n RAISE EXCEPTION USING MESSAGE = 'REVIEW_MEDIA_INVALID', ERRCODE = '23514';\n END IF;\n END IF;\n END LOOP;\n RETURN NEW;\nEND $old$ THEN RAISE EXCEPTION 'REVIEW_SHARED_ANCHOR_DRIFT'; END IF;\n patched:=$new$\nDECLARE p text; old_food text[] := ARRAY[]::text[]; old_verification text; purpose text;\nBEGIN\n IF TG_TABLE_SCHEMA='storage' AND TG_TABLE_NAME='objects' THEN\n IF NEW.bucket_id IN ('review-photos','review-verifications') AND EXISTS(\n SELECT 1 FROM review_media_private.cleanup WHERE path=NEW.name AND retired)\n THEN RAISE EXCEPTION USING MESSAGE='REVIEW_MEDIA_RETIRED',ERRCODE='23514'; END IF;\n RETURN NEW;\n END IF;\n IF TG_OP = 'UPDATE' AND NEW.user_id = OLD.user_id AND NEW.id = OLD.id THEN\n old_food := COALESCE(OLD.food_photos, ARRAY[]::text[]);\n old_verification := OLD.verification_photo;\n END IF;\n FOR p, purpose IN SELECT value, 'food' FROM unnest(COALESCE(NEW.food_photos, ARRAY[]::text[])) value\n UNION ALL SELECT NEW.verification_photo, 'verification' LOOP\n IF EXISTS (SELECT 1 FROM review_media_private.cleanup c\n WHERE c.path = review_media_private.reference_key(p) AND c.retired) THEN\n RAISE EXCEPTION USING MESSAGE = 'REVIEW_MEDIA_RETIRED', ERRCODE = '23514';\n END IF;\n -- Historical keys may remain unchanged in their original purpose only.\n IF NOT COALESCE(CASE WHEN purpose = 'food' THEN p = ANY(old_food) ELSE p = old_verification END, false) THEN\n IF NOT review_media_private.canonical(p, NEW.user_id, NEW.id, purpose) THEN\n RAISE EXCEPTION USING MESSAGE = 'REVIEW_MEDIA_INVALID', ERRCODE = '23514';\n END IF;\n END IF;\n END LOOP;\n RETURN NEW;\nEND $new$;\n EXECUTE replace(definition,source,patched);\n IF (SELECT to_jsonb(p)-'prosrc' FROM pg_proc p WHERE p.oid=target) IS DISTINCT FROM metadata\n OR (SELECT encode(sha256(convert_to(p.prosrc,'UTF8')),'hex') FROM pg_proc p WHERE p.oid=target)<>'94113c8ae1231e8eae5a698f35a1370a38367e42c79cc87ac62db76c32e735b1'\n THEN RAISE EXCEPTION 'REVIEW_SHARED_POST_DRIFT'; END IF;\n CREATE FUNCTION pg_temp.review_shared_assert() RETURNS void LANGUAGE plpgsql SECURITY DEFINER SET search_path='' AS $bridge$\n BEGIN\n PERFORM privacy_retention.assert_g014_public_rpc_allowlist();\n PERFORM privacy_retention.assert_g014_definer_contract();\n PERFORM privacy_retention.assert_g014_catalog_contract();\n DROP FUNCTION pg_temp.review_shared_assert();\n END $bridge$;\n REVOKE ALL ON FUNCTION pg_temp.review_shared_assert() FROM PUBLIC,anon,authenticated,service_role;\n GRANT EXECUTE ON FUNCTION pg_temp.review_shared_assert() TO postgres;\n IF (SELECT jsonb_agg(to_jsonb(m) ORDER BY manifest_kind,manifest_key) FROM privacy_retention.g014_catalog_contract_manifest m) IS DISTINCT FROM before_manifest\n THEN RAISE EXCEPTION 'REVIEW_SHARED_MANIFEST_DRIFT'; END IF;\n IF has_function_privilege('postgres','review_media_private.guard_references()','EXECUTE')\n OR has_function_privilege('postgres','review_media_private.lock_changes()','EXECUTE')\n THEN RAISE EXCEPTION 'STORAGE_FENCE_LEASE_BASELINE_DRIFT'; END IF;\n GRANT EXECUTE ON FUNCTION review_media_private.guard_references(),review_media_private.lock_changes() TO postgres;\n RESET ROLE;\n CREATE TRIGGER a00_review_media_storage_serialize BEFORE INSERT OR UPDATE OF bucket_id,name ON storage.objects\n FOR EACH STATEMENT EXECUTE FUNCTION review_media_private.lock_changes();\n CREATE TRIGGER a00_review_media_storage_guard BEFORE INSERT OR UPDATE OF bucket_id,name ON storage.objects\n FOR EACH ROW EXECUTE FUNCTION review_media_private.guard_references();\n SET LOCAL ROLE privacy_workflow_owner;\n REVOKE EXECUTE ON FUNCTION review_media_private.guard_references(),review_media_private.lock_changes() FROM postgres;\n RESET ROLE;\n IF schema_lease THEN REVOKE CREATE ON SCHEMA review_media_private FROM privacy_workflow_owner; END IF;\n IF major=15 THEN\n IF pg15_lease THEN REVOKE privacy_workflow_owner FROM postgres; END IF;\n ELSIF self_membership IS NULL THEN\n REVOKE privacy_workflow_owner FROM postgres GRANTED BY postgres;\n ELSE\n EXECUTE format('GRANT privacy_workflow_owner TO postgres WITH ADMIN FALSE,INHERIT %s,SET %s GRANTED BY postgres',\n upper(self_membership->>'inherit_option'),upper(self_membership->>'set_option'));\n END IF;\n\n IF (SELECT coalesce(jsonb_agg(to_jsonb(m) ORDER BY roleid,member,grantor),'[]') FROM pg_auth_members m) IS DISTINCT FROM before_members\n OR (SELECT jsonb_agg(to_jsonb(p) ORDER BY oid) FROM pg_proc p WHERE pronamespace='privacy_retention'::regnamespace) IS DISTINCT FROM before_assertions\n OR (SELECT to_jsonb(n) FROM pg_namespace n WHERE n.oid='review_media_private'::regnamespace) IS DISTINCT FROM before_schema\n THEN RAISE EXCEPTION 'REVIEW_SHARED_PRESERVATION_DRIFT'; END IF;\n PERFORM pg_temp.review_shared_assert();\nEND $forward$",
+ "-- STORAGE_RETIREMENT_FENCE_READBACK_BEGIN\nDO $readback$\nBEGIN\n IF NOT EXISTS(SELECT 1 FROM pg_proc WHERE oid='review_media_private.guard_references()'::regprocedure\n AND proowner='privacy_workflow_owner'::regrole AND prosecdef AND proconfig=ARRAY['search_path=\"\"']::text[]\n AND encode(sha256(convert_to(prosrc,'UTF8')),'hex')='94113c8ae1231e8eae5a698f35a1370a38367e42c79cc87ac62db76c32e735b1')\n OR has_function_privilege('anon','review_media_private.guard_references()','EXECUTE')\n OR has_function_privilege('authenticated','review_media_private.guard_references()','EXECUTE')\n OR has_function_privilege('service_role','review_media_private.guard_references()','EXECUTE')\n OR has_function_privilege('postgres','review_media_private.guard_references()','EXECUTE')\n OR NOT EXISTS(SELECT 1 FROM pg_proc WHERE oid='review_media_private.lock_changes()'::regprocedure\n AND proowner='privacy_workflow_owner'::regrole AND prosecdef AND proconfig=ARRAY['search_path=\"\"']::text[]\n AND encode(sha256(convert_to(prosrc,'UTF8')),'hex')='381d89aa6bbf5a59c2ee0fef2a7392ef705147252b324527201b8b6f74376250')\n OR EXISTS(SELECT 1 FROM unnest(ARRAY['anon','authenticated','service_role','postgres']) r\n WHERE has_function_privilege(r,'review_media_private.lock_changes()','EXECUTE'))\n OR NOT EXISTS(SELECT 1 FROM pg_trigger WHERE tgrelid='storage.objects'::regclass AND tgname='a00_review_media_storage_serialize'\n AND tgenabled='O' AND NOT tgisinternal AND tgfoid='review_media_private.lock_changes()'::regprocedure AND tgtype=22 AND pg_get_triggerdef(oid)='CREATE TRIGGER a00_review_media_storage_serialize BEFORE INSERT OR UPDATE OF bucket_id, name ON storage.objects FOR EACH STATEMENT EXECUTE FUNCTION review_media_private.lock_changes()')\n OR NOT EXISTS(SELECT 1 FROM pg_trigger WHERE tgrelid='storage.objects'::regclass AND tgname='a00_review_media_storage_guard'\n AND tgenabled='O' AND NOT tgisinternal AND tgfoid='review_media_private.guard_references()'::regprocedure AND tgtype=23 AND pg_get_triggerdef(oid)='CREATE TRIGGER a00_review_media_storage_guard BEFORE INSERT OR UPDATE OF bucket_id, name ON storage.objects FOR EACH ROW EXECUTE FUNCTION review_media_private.guard_references()')\n THEN RAISE EXCEPTION 'STORAGE_RETIREMENT_FENCE_READBACK_DRIFT'; END IF;\nEND $readback$",
+ "-- STORAGE_RETIREMENT_FENCE_READBACK_END\nCOMMIT"
+ ]
+ },
+ {
+ "id": "review_storage_caller_bound_admin_policy",
+ "version": "20261011110108",
+ "name": "review_storage_caller_bound_admin_policy",
+ "path": "backend/supabase/migrations/20261011110108_review_storage_caller_bound_admin_policy.sql",
+ "bytes": 8463,
+ "sha256": "45b2a0db333a1e24ac1f68b7693c7cfb98e10cd0ab366b98089c3679a6b5dfbc",
+ "statementCount": 7,
+ "statementVectorSha256": "02a88a7bb3c01228d2534cd0147c59f5791e42c790b53dad009ac0d442a4f0cd",
+ "originalStatementVector": [
+ "-- Retired legacy administrator EXEC must not poison unrelated Storage writes.\n-- Admit only the two exact historical banner policies, or their absence in a\n-- fresh canonical replay. Private receipt reads remain owner-only; admin reads use the guarded server route.\n-- No public function, table grant or RPC is added.\nBEGIN",
+ "SET LOCAL lock_timeout='2s'",
+ "SET LOCAL search_path=public,extensions",
+ "SELECT pg_advisory_xact_lock(hashtextextended('tzudong:review-media-catalog:v2',0))",
+ "DO $forward$\nDECLARE item record; actual record; before_other jsonb; before_functions jsonb;\n before_members jsonb; before_manifest jsonb; before_schema jsonb;\n expression text := $predicate$(bucket_id = 'ad-banner-images' AND EXISTS (\n SELECT 1 FROM public.user_roles AS role_row\n JOIN public.user_account_status AS status_row ON status_row.user_id=role_row.user_id\n WHERE role_row.user_id=(SELECT auth.uid()) AND role_row.role::text='admin'\n AND status_row.account_status='active' AND status_row.disabled_at IS NULL))$predicate$;\nBEGIN\n IF current_user<>'postgres' OR session_user<>'postgres' OR current_setting('transaction_read_only')<>'off'\n OR current_setting('server_version_num')::integer/10000 NOT IN (15,17)\n THEN RAISE EXCEPTION 'STORAGE_ADMIN_POLICY_EXECUTOR_DENIED'; END IF;\n SELECT coalesce(jsonb_agg(to_jsonb(p) ORDER BY oid),'[]') INTO before_other FROM pg_policy p\n WHERE polrelid<>'storage.objects'::regclass OR polname NOT IN ('Admin upload access','Admin delete access','review_verifications_owner_read');\n SELECT jsonb_agg(to_jsonb(p) ORDER BY oid) INTO before_functions FROM pg_proc p\n WHERE pronamespace IN ('public'::regnamespace,'privacy_retention'::regnamespace,'review_media_private'::regnamespace);\n SELECT coalesce(jsonb_agg(to_jsonb(m) ORDER BY roleid,member,grantor),'[]') INTO before_members FROM pg_auth_members m;\n SELECT jsonb_agg(to_jsonb(m) ORDER BY manifest_kind,manifest_key) INTO before_manifest FROM privacy_retention.g014_catalog_contract_manifest m;\n SELECT jsonb_agg(to_jsonb(n) ORDER BY oid) INTO before_schema FROM pg_namespace n;\n IF (SELECT count(*) FROM pg_policy WHERE polrelid='storage.objects'::regclass\n AND polname IN ('Admin upload access','Admin delete access')) NOT IN (0,2)\n THEN RAISE EXCEPTION 'STORAGE_ADMIN_POLICY_INVENTORY_DRIFT'; END IF;\n FOR item IN SELECT * FROM (VALUES ('Admin upload access','a'::\"char\"),('Admin delete access','d'::\"char\")) AS expected(name,cmd) LOOP\n SELECT p.*,pg_get_expr(polqual,polrelid) AS using_expr,pg_get_expr(polwithcheck,polrelid) AS check_expr INTO actual\n FROM pg_policy p WHERE polrelid='storage.objects'::regclass AND polname=item.name;\n IF NOT FOUND THEN CONTINUE; END IF;\n IF NOT actual.polpermissive OR actual.polcmd<>item.cmd OR actual.polroles<>ARRAY[0::oid]\n OR (CASE WHEN item.cmd='a' THEN actual.using_expr IS NOT NULL ELSE actual.check_expr IS NOT NULL END)\n OR coalesce(CASE WHEN item.cmd='a' THEN actual.check_expr ELSE actual.using_expr END,'') NOT IN (\n '((bucket_id = ''ad-banner-images''::text) AND is_user_admin(uid()))',\n '((bucket_id = ''ad-banner-images''::text) AND is_user_admin(auth.uid()))')\n OR (SELECT count(*) FROM pg_depend WHERE classid='pg_policy'::regclass AND objid=actual.oid\n AND refclassid='pg_proc'::regclass AND refobjid='public.is_user_admin(uuid)'::regprocedure)<>1\n OR (SELECT count(*) FROM pg_depend WHERE classid='pg_policy'::regclass AND objid=actual.oid\n AND refclassid='pg_proc'::regclass AND refobjid='auth.uid()'::regprocedure)<>1\n THEN RAISE EXCEPTION 'STORAGE_ADMIN_POLICY_SOURCE_DRIFT'; END IF;\n EXECUTE format('ALTER POLICY %I ON storage.objects TO authenticated %s (%s)',item.name,\n CASE WHEN item.cmd='a' THEN 'WITH CHECK' ELSE 'USING' END,expression);\n END LOOP;\n SELECT p.*,pg_get_expr(polqual,polrelid) AS using_expr,pg_get_expr(polwithcheck,polrelid) AS check_expr INTO actual\n FROM pg_policy p WHERE polrelid='storage.objects'::regclass AND polname='review_verifications_owner_read';\n IF NOT FOUND OR actual.polcmd<>'r' OR NOT actual.polpermissive\n OR actual.polroles<>ARRAY['authenticated'::regrole::oid] OR actual.check_expr IS NOT NULL\n OR replace(actual.using_expr,'auth.uid()','uid()') IS DISTINCT FROM\n '((bucket_id = ''review-verifications''::text) AND (((storage.foldername(name))[1] = ( SELECT (uid())::text AS uid)) OR has_role(( SELECT uid() AS uid), ''admin''::app_role)))'\n OR (SELECT count(*) FROM pg_depend WHERE classid='pg_policy'::regclass AND objid=actual.oid\n AND refclassid='pg_proc'::regclass AND refobjid='public.has_role(uuid,public.app_role)'::regprocedure)<>1\n THEN RAISE EXCEPTION 'STORAGE_OWNER_READ_SOURCE_DRIFT'; END IF;\n ALTER POLICY review_verifications_owner_read ON storage.objects TO authenticated\n USING (bucket_id='review-verifications' AND (storage.foldername(name))[1]=(SELECT auth.uid()::text));\n IF (SELECT coalesce(jsonb_agg(to_jsonb(p) ORDER BY oid),'[]') FROM pg_policy p\n WHERE polrelid<>'storage.objects'::regclass OR polname NOT IN ('Admin upload access','Admin delete access','review_verifications_owner_read')) IS DISTINCT FROM before_other\n OR (SELECT jsonb_agg(to_jsonb(p) ORDER BY oid) FROM pg_proc p\n WHERE pronamespace IN ('public'::regnamespace,'privacy_retention'::regnamespace,'review_media_private'::regnamespace)) IS DISTINCT FROM before_functions\n OR (SELECT coalesce(jsonb_agg(to_jsonb(m) ORDER BY roleid,member,grantor),'[]') FROM pg_auth_members m) IS DISTINCT FROM before_members\n OR (SELECT jsonb_agg(to_jsonb(m) ORDER BY manifest_kind,manifest_key) FROM privacy_retention.g014_catalog_contract_manifest m) IS DISTINCT FROM before_manifest\n OR (SELECT jsonb_agg(to_jsonb(n) ORDER BY oid) FROM pg_namespace n) IS DISTINCT FROM before_schema\n THEN RAISE EXCEPTION 'STORAGE_ADMIN_POLICY_PRESERVATION_DRIFT'; END IF;\nEND $forward$",
+ "-- STORAGE_CALLER_BOUND_ADMIN_READBACK_BEGIN\nDO $readback$\nDECLARE item record; policy_row record; expression text;\nBEGIN\n IF EXISTS(SELECT 1 FROM pg_depend d JOIN pg_policy p ON d.classid='pg_policy'::regclass AND d.objid=p.oid\n WHERE p.polrelid='storage.objects'::regclass AND d.refclassid='pg_proc'::regclass\n AND d.refobjid IN ('public.is_user_admin(uuid)'::regprocedure,'public.has_role(uuid,public.app_role)'::regprocedure))\n OR has_function_privilege('anon','public.is_user_admin(uuid)','EXECUTE')\n OR has_function_privilege('authenticated','public.is_user_admin(uuid)','EXECUTE')\n OR has_function_privilege('service_role','public.is_user_admin(uuid)','EXECUTE')\n THEN RAISE EXCEPTION 'STORAGE_ADMIN_POLICY_READBACK_DRIFT'; END IF;\n IF NOT EXISTS(SELECT 1 FROM pg_policy WHERE polrelid='storage.objects'::regclass AND polname='review_verifications_owner_read'\n AND polcmd='r' AND polpermissive AND polroles=ARRAY['authenticated'::regrole::oid] AND polwithcheck IS NULL\n AND replace(pg_get_expr(polqual,polrelid),'auth.uid()','uid()')='((bucket_id = ''review-verifications''::text) AND ((storage.foldername(name))[1] = ( SELECT (uid())::text AS uid)))')\n THEN RAISE EXCEPTION 'STORAGE_OWNER_READ_READBACK_DRIFT'; END IF;\n FOR item IN SELECT * FROM (VALUES ('Admin upload access','a'::\"char\"),('Admin delete access','d'::\"char\")) AS expected(name,cmd) LOOP\n SELECT p.*,pg_get_expr(polqual,polrelid) AS using_expr,pg_get_expr(polwithcheck,polrelid) AS check_expr INTO policy_row\n FROM pg_policy p WHERE polrelid='storage.objects'::regclass AND polname=item.name;\n IF NOT FOUND THEN CONTINUE; END IF;\n expression:=coalesce(CASE WHEN item.cmd='a' THEN policy_row.check_expr ELSE policy_row.using_expr END,'');\n IF NOT policy_row.polpermissive OR policy_row.polcmd<>item.cmd\n OR policy_row.polroles<>ARRAY['authenticated'::regrole::oid]\n OR (CASE WHEN item.cmd='a' THEN policy_row.using_expr IS NOT NULL ELSE policy_row.check_expr IS NOT NULL END)\n OR encode(sha256(convert_to(regexp_replace(replace(expression,'auth.uid()','uid()'),'[[:space:]]+',' ','g'),'UTF8')),'hex')<>'049d579041975ddfaa0c560f56e4c57ee06099b2290f742636ba5bd9345463df'\n OR (SELECT count(*) FROM pg_depend WHERE classid='pg_policy'::regclass AND objid=policy_row.oid\n AND refclassid='pg_proc'::regclass AND refobjid='auth.uid()'::regprocedure)<>1\n THEN RAISE EXCEPTION 'STORAGE_ADMIN_POLICY_READBACK_DRIFT'; END IF;\n END LOOP;\nEND $readback$",
+ "-- STORAGE_CALLER_BOUND_ADMIN_READBACK_END\nCOMMIT"
+ ]
+ },
+ {
+ "id": "review_owner_cleanup_completion",
+ "version": "20261011175912",
+ "name": "review_owner_cleanup_completion",
+ "path": "backend/supabase/migrations/20261011175912_review_owner_cleanup_completion.sql",
+ "bytes": 6694,
+ "sha256": "9164be35fc17e67c3f1aaf98aad13139a8fd469130bd1315879bff5dac63ba26",
+ "statementCount": 6,
+ "statementVectorSha256": "1343501bbfeac4c075fcb09f07de1a11e3b02c27d12d5091b65d72c99555269a",
+ "originalStatementVector": [
+ "-- Count only retired, unreferenced owner cleanup, excluding live OCR staging.\n-- Forward correction preserves applied SQL, RPC metadata, ACLs and G014 rows.\nBEGIN",
+ "SET LOCAL lock_timeout='2s'",
+ "SELECT pg_advisory_xact_lock(hashtextextended('tzudong:review-media-catalog:v2',0))",
+ "DO $forward$\nDECLARE target oid:='public.finish_review_media_cleanup()'::regprocedure;\n definition text; source text; patched text; metadata jsonb; defaults text;\n before_members jsonb; before_assertions jsonb; before_manifest jsonb; self_membership jsonb;\n before_schema jsonb; schema_lease boolean;\n workflow_owner oid:='privacy_workflow_owner'::regrole; runner oid:='postgres'::regrole;\n major integer:=current_setting('server_version_num')::integer/10000; pg15_lease boolean:=false;\nBEGIN\n IF current_user<>'postgres' OR session_user<>'postgres' OR major NOT IN (15,17)\n OR current_setting('transaction_read_only')<>'off' OR NOT EXISTS(SELECT 1 FROM pg_roles\n WHERE oid=workflow_owner AND NOT rolsuper AND NOT rolbypassrls AND NOT rolcanlogin AND NOT rolinherit)\n THEN RAISE EXCEPTION 'REVIEW_SHARED_EXECUTOR_DENIED'; END IF;\n SELECT coalesce(jsonb_agg(to_jsonb(m) ORDER BY roleid,member,grantor),'[]') INTO before_members FROM pg_auth_members m;\n SELECT jsonb_agg(to_jsonb(p) ORDER BY oid) INTO before_assertions FROM pg_proc p WHERE pronamespace='privacy_retention'::regnamespace;\n SELECT pg_get_functiondef(p.oid),p.prosrc,to_jsonb(p)-ARRAY['prosrc','proargdefaults'],pg_get_expr(p.proargdefaults,0) INTO definition,source,metadata,defaults FROM pg_proc p WHERE p.oid=target;\n IF metadata->>'proowner'<>workflow_owner::text OR encode(sha256(convert_to(source,'UTF8')),'hex')<>'f6c4e71988ed2b789b887484af5fdf2e482bde98f1d1c5b5d4e7ad124c314bd1'\n THEN RAISE EXCEPTION 'REVIEW_SHARED_SOURCE_DRIFT'; END IF;\n SELECT to_jsonb(n) INTO before_schema FROM pg_namespace n WHERE n.oid='public'::regnamespace;\n IF before_schema->>'nspowner' NOT IN (runner::text,'pg_database_owner'::regrole::oid::text)\n THEN RAISE EXCEPTION 'REVIEW_SHARED_SCHEMA_OWNER_DRIFT'; END IF;\n schema_lease:=NOT has_schema_privilege(workflow_owner,'public','CREATE');\n IF schema_lease THEN GRANT CREATE ON SCHEMA public TO privacy_workflow_owner; END IF;\n SELECT to_jsonb(m) INTO self_membership FROM pg_auth_members m WHERE roleid=workflow_owner AND member=runner AND grantor=runner;\n IF self_membership IS NOT NULL AND (self_membership->>'admin_option')::boolean THEN RAISE EXCEPTION 'REVIEW_MEDIA_FORWARD_MEMBERSHIP_DENIED'; END IF;\n IF major=17 THEN\n IF NOT EXISTS(SELECT 1 FROM pg_auth_members WHERE roleid=workflow_owner AND member=runner AND admin_option AND grantor<>runner)\n THEN RAISE EXCEPTION 'REVIEW_MEDIA_FORWARD_OWNER_AUTHORITY_MISSING'; END IF;\n EXECUTE 'GRANT privacy_workflow_owner TO postgres WITH ADMIN FALSE,INHERIT FALSE,SET TRUE GRANTED BY postgres';\n ELSE\n pg15_lease:=NOT pg_has_role(runner,workflow_owner,'MEMBER');\n IF pg15_lease THEN GRANT privacy_workflow_owner TO postgres; END IF;\n END IF;\n SET LOCAL ROLE privacy_workflow_owner;\n SELECT jsonb_agg(to_jsonb(m) ORDER BY manifest_kind,manifest_key) INTO before_manifest FROM privacy_retention.g014_catalog_contract_manifest m;\n\n IF (length(source)-length(replace(source,$old$AND NOT c.complete);$old$,'')))/length($old$AND NOT c.complete);$old$)<>1\n THEN RAISE EXCEPTION 'OWNER_CLEANUP_ANCHOR_DRIFT'; END IF;\n patched:=replace(source,$old$AND NOT c.complete);$old$,$new$AND c.retired AND NOT c.complete AND NOT review_media_private.referenced(c.path));$new$);\n EXECUTE replace(definition,source,patched);\n IF (SELECT to_jsonb(p)-ARRAY['prosrc','proargdefaults'] FROM pg_proc p WHERE p.oid=target) IS DISTINCT FROM metadata\n OR (SELECT pg_get_expr(p.proargdefaults,0) FROM pg_proc p WHERE p.oid=target) IS DISTINCT FROM defaults\n OR (SELECT encode(sha256(convert_to(p.prosrc,'UTF8')),'hex') FROM pg_proc p WHERE p.oid=target)<>'a6b4211b2d8486d482f0758ce249eec157249e1a5176be286b76af7e7a775ee9'\n THEN RAISE EXCEPTION 'OWNER_CLEANUP_POST_DRIFT'; END IF;\n CREATE FUNCTION pg_temp.review_shared_assert() RETURNS void LANGUAGE plpgsql SECURITY DEFINER SET search_path='' AS $bridge$\n BEGIN\n PERFORM privacy_retention.assert_g014_public_rpc_allowlist();\n PERFORM privacy_retention.assert_g014_definer_contract();\n PERFORM privacy_retention.assert_g014_catalog_contract();\n DROP FUNCTION pg_temp.review_shared_assert();\n END $bridge$;\n REVOKE ALL ON FUNCTION pg_temp.review_shared_assert() FROM PUBLIC,anon,authenticated,service_role;\n GRANT EXECUTE ON FUNCTION pg_temp.review_shared_assert() TO postgres;\n IF (SELECT jsonb_agg(to_jsonb(m) ORDER BY manifest_kind,manifest_key) FROM privacy_retention.g014_catalog_contract_manifest m) IS DISTINCT FROM before_manifest\n THEN RAISE EXCEPTION 'REVIEW_SHARED_MANIFEST_DRIFT'; END IF;\n RESET ROLE;\n IF schema_lease THEN REVOKE CREATE ON SCHEMA public FROM privacy_workflow_owner; END IF;\n IF major=15 THEN\n IF pg15_lease THEN REVOKE privacy_workflow_owner FROM postgres; END IF;\n ELSIF self_membership IS NULL THEN\n REVOKE privacy_workflow_owner FROM postgres GRANTED BY postgres;\n ELSE\n EXECUTE format('GRANT privacy_workflow_owner TO postgres WITH ADMIN FALSE,INHERIT %s,SET %s GRANTED BY postgres',\n upper(self_membership->>'inherit_option'),upper(self_membership->>'set_option'));\n END IF;\n\n IF (SELECT coalesce(jsonb_agg(to_jsonb(m) ORDER BY roleid,member,grantor),'[]') FROM pg_auth_members m) IS DISTINCT FROM before_members\n OR (SELECT jsonb_agg(to_jsonb(p) ORDER BY oid) FROM pg_proc p WHERE pronamespace='privacy_retention'::regnamespace) IS DISTINCT FROM before_assertions\n OR (SELECT to_jsonb(n) FROM pg_namespace n WHERE n.oid='public'::regnamespace) IS DISTINCT FROM before_schema\n THEN RAISE EXCEPTION 'REVIEW_SHARED_PRESERVATION_DRIFT'; END IF;\n PERFORM pg_temp.review_shared_assert();\nEND $forward$",
+ "-- OWNER_CLEANUP_READBACK_BEGIN\nDO $readback$\nBEGIN\n IF NOT EXISTS(SELECT 1 FROM pg_proc WHERE oid='public.finish_review_media_cleanup()'::regprocedure\n AND proowner='privacy_workflow_owner'::regrole AND prosecdef AND proconfig=ARRAY['search_path=\"\"']::text[]\n AND encode(sha256(convert_to(prosrc,'UTF8')),'hex')='a6b4211b2d8486d482f0758ce249eec157249e1a5176be286b76af7e7a775ee9')\n OR has_function_privilege('anon','public.finish_review_media_cleanup()','EXECUTE')\n OR has_function_privilege('service_role','public.finish_review_media_cleanup()','EXECUTE')\n OR NOT has_function_privilege('authenticated','public.finish_review_media_cleanup()','EXECUTE')\n THEN RAISE EXCEPTION 'OWNER_CLEANUP_READBACK_DRIFT'; END IF;\nEND $readback$",
+ "-- OWNER_CLEANUP_READBACK_END\nCOMMIT"
+ ]
+ }
+ ],
+ "legacyReleaseManifest": {
+ "path": ".github/supabase-migration-release-manifest.v1.json",
+ "entries": 3,
+ "sha256": "515743d094b4b431a29df772a363837bdad8f7541aa3acf4a923efb79f460c0d"
+ },
+ "toolchain": [
+ {
+ "path": "apps/web/scripts/review-media-sql-successor.mjs",
+ "sha256": "4f1b2798e0cfc13db4f11cd6eb4a8653320aa8516eccd71d410fcff6d06a7f24"
+ },
+ {
+ "path": "apps/web/scripts/review-media-sql-rehearsal.mjs",
+ "sha256": "c893c294719776bf5b064c3355866ab97e0fa9412082d9b47d7de5266094c65f"
+ },
+ {
+ "path": "apps/web/scripts/supabase-migration-bundle.mjs",
+ "sha256": "fcebc20dc4846606dc14bdbe658a07daa32430ad632e52383151f5c22d22efb9"
+ },
+ {
+ "path": "apps/web/scripts/supabase-migration-transaction.mjs",
+ "sha256": "c67bdff4c63b1bf1b4a157863b3eb5253b5973a8fb54b7fd49351270787bed26"
+ },
+ {
+ "path": "apps/web/scripts/apply-supabase-migration.mjs",
+ "sha256": "02c404e0be3e81b7c8d4d7766ff29e750b1797f5ad49b5f496168170e21192d4"
+ },
+ {
+ "path": "apps/web/scripts/vercel-rollback-readback.mjs",
+ "sha256": "fa3876153f0e949d23115612cb34bc545c6c29bccb6ba2fd6042a38e70555e04"
+ },
+ {
+ "path": "backend/supabase/scripts/g037_supabase_statement_vector.mjs",
+ "sha256": "398e3945c0d0fb656daef0d0a42409dbdeb45a9bb1f6f8c03445e4436d4db0bd"
+ }
+ ],
+ "requiredWriterFences": {
+ "adminRecordMutationsHold": "active",
+ "g037WriteFreeze": "active",
+ "automaticReviewer": "stopped",
+ "browserRpc": "stopped",
+ "directSql": "stopped",
+ "dashboard": "stopped",
+ "providerIngress": "stopped"
+ }
+}
diff --git a/apps/web/components/admin/AdminOverviewDashboard.tsx b/apps/web/components/admin/AdminOverviewDashboard.tsx
index b2076683ee..b5b8ded97d 100644
--- a/apps/web/components/admin/AdminOverviewDashboard.tsx
+++ b/apps/web/components/admin/AdminOverviewDashboard.tsx
@@ -9,6 +9,7 @@ import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { Skeleton } from "@/components/ui/skeleton";
import { useNaverMaps } from "@/hooks/use-naver-maps";
+import { AdminMapLoadErrorState } from "@/components/admin/admin-map-load-error-state";
import { readAdminNaverCoordinate } from "@/lib/admin/naver-coordinate";
import { REGION_MAP_CONFIG } from "@/config/maps";
import { getNaverIndividualMarkerVisual } from "@/lib/naver-map-marker-visuals";
@@ -882,17 +883,7 @@ function AdminNaverMapSurface({
{isMapPreparing && }
{loadError && !isMapPreparing && (
-
-
-
- 네이버 지도를 불러오지 못했습니다
-
-
- {loadError.message} 지도 키가 준비되면 홈과 같은 마커와 클러스터로
- 자동 표시됩니다.
-
-
-
+
)}
);
diff --git a/apps/web/components/admin/admin-map-load-error-state.tsx b/apps/web/components/admin/admin-map-load-error-state.tsx
new file mode 100644
index 0000000000..c1be18d687
--- /dev/null
+++ b/apps/web/components/admin/admin-map-load-error-state.tsx
@@ -0,0 +1,13 @@
+import { Button } from '@/components/ui/button';
+
+export function AdminMapLoadErrorState({ onRetry }: { onRetry: () => void }) {
+ return (
+
+
+
네이버 지도를 불러오지 못했습니다
+
연결 상태를 확인한 뒤 다시 시도해 주세요.
+
다시 시도
+
+
+ );
+}
diff --git a/apps/web/components/map/NaverMapView.tsx b/apps/web/components/map/NaverMapView.tsx
index c1dc803486..465145a75f 100644
--- a/apps/web/components/map/NaverMapView.tsx
+++ b/apps/web/components/map/NaverMapView.tsx
@@ -3687,7 +3687,7 @@ const NaverMapView = memo(({
// 로딩 에러 처리
if (loadError || mapInitError) {
- return ;
+ return ;
}
// 로딩 중
diff --git a/apps/web/components/map/map-view-status-panels.tsx b/apps/web/components/map/map-view-status-panels.tsx
index 4c0ad92e3c..ff11f84c53 100644
--- a/apps/web/components/map/map-view-status-panels.tsx
+++ b/apps/web/components/map/map-view-status-panels.tsx
@@ -24,6 +24,6 @@ export function MapViewGoogleLoadErrorState() {
return ;
}
-export function NaverMapLoadErrorState(_props: { message: string }) {
- return ;
+export function NaverMapLoadErrorState({ onRetry }: { message: string; onRetry?: () => void }) {
+ return ;
}
diff --git a/apps/web/hooks/use-naver-maps.tsx b/apps/web/hooks/use-naver-maps.tsx
index eb48694944..5798382f67 100644
--- a/apps/web/hooks/use-naver-maps.tsx
+++ b/apps/web/hooks/use-naver-maps.tsx
@@ -1,10 +1,9 @@
'use client';
-import { useEffect, useState, useCallback } from "react";
+import { useEffect, useState, useCallback, useRef } from "react";
import { NAVER_MAPS_CONFIG } from "@/config/maps";
import {
isLocalNaverStyleFallbackHost,
- LOCAL_NAVER_MAP_STUB_SCRIPT_URL,
resolveNaverMapScriptUrl,
rewriteNaverStyleUrlForLocalhost,
shouldUseLocalNaverMapStub,
@@ -106,53 +105,14 @@ function hasUsableNaverMaps() {
&& maps.Event
);
}
-function loadLocalNaverStubFallback(
- setIsLoaded: (value: boolean) => void,
- setIsLoading: (value: boolean) => void,
- setLoadError: (value: Error | null) => void,
-) {
- if (hasUsableNaverMaps()) {
- setIsLoaded(true);
- setIsLoading(false);
- return;
- }
-
- const existingStub = document.querySelector('script[data-local-naver-maps="true"]');
- const attachStub = (script: HTMLScriptElement) => {
- script.addEventListener('load', () => {
- if (hasUsableNaverMaps()) {
- setIsLoaded(true);
- setLoadError(null);
- } else {
- setLoadError(new Error('네이버 지도 API 초기화 실패'));
- }
- setIsLoading(false);
- });
- script.addEventListener('error', () => {
- setLoadError(new Error('네이버 지도 API 로딩 실패 - 네트워크 오류'));
- setIsLoading(false);
- });
- };
-
- if (existingStub instanceof HTMLScriptElement) {
- attachStub(existingStub);
- return;
- }
-
- const script = document.createElement('script');
- script.type = 'text/javascript';
- script.dataset.localNaverMaps = 'true';
- script.src = LOCAL_NAVER_MAP_STUB_SCRIPT_URL;
- script.async = true;
- attachStub(script);
- document.head.appendChild(script);
-}
-
const EFFECTIVE_SCRIPT_URL = resolveNaverMapScriptUrl({
clientId: NAVER_MAPS_CONFIG.clientId,
configuredScriptUrl: process.env.NEXT_PUBLIC_NAVER_MAPS_SCRIPT_URL,
useLocalStub: LOCAL_OFFLINE_MAP,
});
+const SHARED_SCRIPT_SELECTOR = LOCAL_OFFLINE_MAP
+ ? 'script[data-local-naver-maps="true"]'
+ : 'script[data-tzudong-naver-maps="true"], script[src*="oapi.map.naver.com"]';
export function useNaverMaps(options: UseNaverMapsOptions = {}) {
const { autoLoad = false, strategy = 'afterInteractive' } = options;
@@ -163,11 +123,46 @@ export function useNaverMaps(options: UseNaverMapsOptions = {}) {
});
const [loadError, setLoadError] = useState(null);
const [isLoading, setIsLoading] = useState(false);
+ const scheduledLoadRef = useRef<(() => void) | null>(null);
+
+ // A retry belongs to the shared provider, not only to the consumer that
+ // requested it. Capture script completion for every mounted consumer;
+ // remove the subscription when that consumer leaves.
+ useEffect(() => {
+ const observeScript = (event: Event) => {
+ const script = event.target;
+ if (!(script instanceof HTMLScriptElement)) return;
+ if (!script.matches(SHARED_SCRIPT_SELECTOR)) return;
+
+ scheduledLoadRef.current?.();
+ scheduledLoadRef.current = null;
+ setIsLoading(false);
+ if (event.type === 'load' && hasUsableNaverMaps()) {
+ installLocalNaverStyleHostFallback();
+ setIsLoaded(true);
+ setLoadError(null);
+ } else if (!hasUsableNaverMaps()) {
+ setLoadError(new Error(event.type === 'load'
+ ? '네이버 지도 API 초기화 실패'
+ : '네이버 지도 API 로딩 실패 - 네트워크 오류'));
+ }
+ };
+ document.addEventListener('load', observeScript, true);
+ document.addEventListener('error', observeScript, true);
+ return () => {
+ scheduledLoadRef.current?.();
+ scheduledLoadRef.current = null;
+ document.removeEventListener('load', observeScript, true);
+ document.removeEventListener('error', observeScript, true);
+ };
+ }, []);
// 수동으로 스크립트 로드하는 함수
const load = useCallback(() => {
// 이미 로드되었거나 로딩 중이면 무시
- if (isLoaded || isLoading) return;
+ if (isLoaded || isLoading || scheduledLoadRef.current) return;
+
+ setLoadError(null);
// Client ID 검증
if (!NAVER_MAPS_CONFIG.clientId && !LOCAL_OFFLINE_MAP) {
@@ -182,61 +177,52 @@ export function useNaverMaps(options: UseNaverMapsOptions = {}) {
}
// 이미 로딩 중인 스크립트가 있는지 확인
- const existingScript = document.querySelector(
- LOCAL_OFFLINE_MAP
- ? 'script[data-local-naver-maps="true"]'
- : 'script[src*="oapi.map.naver.com"]'
- );
+ const existingScript = document.querySelector(SHARED_SCRIPT_SELECTOR);
if (existingScript) {
setIsLoading(true);
- existingScript.addEventListener("load", () => {
- installLocalNaverStyleHostFallback();
- if (hasUsableNaverMaps()) {
- setIsLoaded(true);
- setIsLoading(false);
- } else {
- setLoadError(new Error("네이버 지도 API 초기화 실패"));
- setIsLoading(false);
- }
- });
- existingScript.addEventListener("error", () => {
- setLoadError(new Error("네이버 지도 API 로딩 실패 - 네트워크 오류"));
- setIsLoading(false);
- });
return;
}
// 스크립트 로드 실행 함수
const injectScript = () => {
+ // Another consumer may have completed or injected the shared SDK
+ // while this consumer was waiting for an idle callback.
+ if (hasUsableNaverMaps()) {
+ setIsLoaded(true);
+ setIsLoading(false);
+ return;
+ }
+ if (document.querySelector(SHARED_SCRIPT_SELECTOR)) return;
installLocalNaverStyleHostFallback();
setIsLoading(true);
const script = document.createElement("script");
script.type = "text/javascript";
+ script.dataset.tzudongNaverMaps = "true";
if (LOCAL_OFFLINE_MAP) script.dataset.localNaverMaps = "true";
script.src = EFFECTIVE_SCRIPT_URL;
script.async = true;
script.onload = () => {
+ script.onload = null;
+ script.onerror = null;
installLocalNaverStyleHostFallback();
if (hasUsableNaverMaps()) {
setIsLoaded(true);
setIsLoading(false);
return;
}
- if (!LOCAL_OFFLINE_MAP) {
- loadLocalNaverStubFallback(setIsLoaded, setIsLoading, setLoadError);
- return;
- }
+ // A failed live provider must never become a test map. Remove
+ // this failed owned script so a user retry can request it again.
+ script.remove();
setLoadError(new Error("네이버 지도 API 초기화 실패"));
setIsLoading(false);
};
script.onerror = () => {
- if (!LOCAL_OFFLINE_MAP) {
- loadLocalNaverStubFallback(setIsLoaded, setIsLoading, setLoadError);
- return;
- }
+ script.onload = null;
+ script.onerror = null;
+ script.remove();
setLoadError(
new Error(
"네이버 지도 API 스크립트 로드 실패 - Client ID 또는 웹 서비스 URL을 확인해주세요."
@@ -250,14 +236,31 @@ export function useNaverMaps(options: UseNaverMapsOptions = {}) {
// 전략에 따른 로드 실행
if (strategy === 'lazyOnload') {
+ setIsLoading(true);
+ let cancelled = false;
+ const run = () => {
+ scheduledLoadRef.current = null;
+ if (!cancelled) injectScript();
+ };
// 브라우저 유휴 상태일 때 로드하거나 2초 후 로드
const idleWindow = window as Window & {
requestIdleCallback?: RequestIdleCallbackLike;
+ cancelIdleCallback?: (handle: IdleCallbackHandle) => void;
};
if (typeof idleWindow.requestIdleCallback === 'function') {
- idleWindow.requestIdleCallback(() => injectScript(), { timeout: 2000 });
+ let handle: IdleCallbackHandle | undefined;
+ scheduledLoadRef.current = () => {
+ cancelled = true;
+ if (handle !== undefined) idleWindow.cancelIdleCallback?.(handle);
+ };
+ handle = idleWindow.requestIdleCallback(run, { timeout: 2000 });
} else {
- setTimeout(injectScript, 2000);
+ let handle: number | undefined;
+ scheduledLoadRef.current = () => {
+ cancelled = true;
+ if (handle !== undefined) window.clearTimeout(handle);
+ };
+ handle = window.setTimeout(run, 2000);
}
} else {
// afterInteractive: 즉시 비동기 로드
@@ -268,10 +271,12 @@ export function useNaverMaps(options: UseNaverMapsOptions = {}) {
// autoLoad가 true면 로드 시도
useEffect(() => {
- if (autoLoad) {
+ // After a provider failure, wait for an explicit retry. A loading-state
+ // change must not create an unbounded automatic request loop.
+ if (autoLoad && !loadError) {
load();
}
- }, [autoLoad, load]);
+ }, [autoLoad, load, loadError]);
return { isLoaded, loadError, isLoading, load };
}
diff --git a/apps/web/performance/review-db-rehearsal-20261012/README.md b/apps/web/performance/review-db-rehearsal-20261012/README.md
new file mode 100644
index 0000000000..781d456487
--- /dev/null
+++ b/apps/web/performance/review-db-rehearsal-20261012/README.md
@@ -0,0 +1,129 @@
+# Review-media successor rollback rehearsal
+
+The held eleven-migration controller now has a separate rollback-only rehearsal
+and durable publication protocol. Operating application remains unperformed.
+
+## Behavior and failure boundaries
+
+- The rehearsal admission binds the clean detached protected-main source,
+ rollback identity, seven writer fences, private paths and exact statement vectors.
+- The eleven vectors run once in one explicit transaction. State 0 through 11
+ is captured before ROLLBACK, and a separate read-only transaction must match
+ state 0. Transport failure triggers readback only, never a repeated mutation.
+- The pending receipt is exclusively created and fsynced; the verification
+ journal entry is fsynced before the canonical hard link is created. The custody
+ directory is fsynced before publication confirmation is appended.
+- Publication audit fsync failure cannot reconcile from visible bytes. Only a
+ close acknowledgement failure after successful fsync permits exact-byte
+ reconciliation. Apply separately validates and fsyncs the exact admitted
+ receipt and three-entry journal plus their custody directory. Thus an earlier
+ unconfirmed fsync cannot be silently treated as durable by a later process.
+- Apply writes and fsyncs its own start journal and parent directory before any
+ mutating transport. Five injected failures across receipt, rehearsal journal,
+ custody directory, start journal and start directory produced zero mutations.
+- A failed terminal audit preserves its bounded operation code alongside the
+ journal failure. Arbitrary diagnostics and throwing getters cannot escape the
+ bounded failure JSON.
+- Both SQL paths check expiry after acquiring the advisory lock. Apply arms
+ PostgreSQL 17 transaction_timeout against the original absolute deadline and
+ checks after each migration and after the final readback, before COMMIT. The
+ receipt timestamps use the original admitted window rather than a 30-second
+ execution limit; 30 seconds remains only a current-clock skew allowance.
+
+## Current verification
+
+Pinned Node 24 and Bun 1.4.0 verified six affected suites across targeted runs: **94 passed, 0 failed,
+518 assertions**. The checks include publication fsync/close failures, five
+pre-mutation durability failures, expiry plus audit failure, tampered/orphan
+journals, retained evidence, private admission formatting, database/role ownership
+and lost acknowledgements. Filesystem expiry is rechecked before verification,
+publication, and successful return. A committed database outcome survives terminal
+audit failure; only proven durability plus exact journal bytes permits reconciliation.
+Targeted ESLint and Node syntax checks passed.
+
+The final source was replayed once in an isolated Supabase PostgreSQL 17.6
+container. It compiled and executed eleven immutable migrations (112 source
+statements), captured twelve states, and verified exact restoration after normal
+rollback, forced SQL failure, a discarded response and contended expiry. The
+lost-response case attempted the mutation once. The owned container was removed.
+This database replay exercises the SQL compiler and SQL behavior; filesystem
+controller failures are covered by the separate fault-injection tests above.
+
+The source fixture is local `ranking_clone`, copied with its contract seed rows.
+The verifier explicitly checks `public.profiles` and `public.reviews` are empty;
+it does not claim a universal personal-data census. Its synthetic ledger has 86
+entries, not an operating ledger readback.
+
+- Current proof (selector-bound): `pg17-reviewed-boundaries-20261013.json`
+- Proof SHA-256: `9c73b47c0841f2585d7ceee77423d27410a488e61080004cf227d4329dd88076`
+- Manifest SHA-256: `23bf7e7d7e9e5d7962021dd3ec860e87b47d07e119ba0e39de2e423a0a601f4e`
+- Toolchain root: `579f315ba6285ea7ccc562cf2cf8949203a0a3289e4de5f82be1687ddea4170b`
+- Source root: `61f9d7637c0714a7b0d9108c6f7959ea820175a7870dba681498b570296a1544`
+- Verifier SHA-256: `f7321f607a5bf3a24ba099d51abc67254db52fcc385945692426d1b2dfa6e431`
+- Compiled SQL SHA-256: `285d0c859117006646389dbd2a22de9aef1c0b97bc796a9e4d4448a8a232b093`
+
+## Reproduce without overwriting evidence
+
+From the repository root:
+
+```sh
+/opt/homebrew/opt/node@24/bin/node \
+ apps/web/performance/review-db-rehearsal-20261012/verify-review-media-sql-rehearsal.mjs
+```
+
+The stable `retained-current-proof.json` selector declares the current retained
+proof. The detached map digest, selector, every mapped artifact and selected proof
+are verified from the same byte snapshots; mismatched source bindings are reported
+as historical. Each execution prints a current proof to stdout. Optional
+`--output /absolute/new-proof.json` creates a separate private file exclusively;
+it refuses any existing output. The verifier does not delete the source
+container or a fixed-name container: creation and cleanup require a unique name,
+per-run owner label and exact inspected container ID. Lost create/delete
+acknowledgements are reconciled by ownership/absence readback.
+
+`pg17-rollback-rehearsal.json` and `SHA256SUMS-pre-hardening-historical` preserve
+the earlier pre-hardening evidence. `pg17-rollback-rehearsal-current-20261013.json`
+is the intermediate replay before the safe-getter fix. Neither is labelled as
+final-source proof. The original `performance/review-db-successor-20261012/`
+README, retained proof and SHA256SUMS remain unchanged.
+
+## Limits and next operating step
+
+Launch policy remains held. No hosted DDL, operating row read, Storage mutation,
+deployment, cost reduction or performance improvement is established here.
+Operating execution needs current protected main, fresh actual-ledger rehearsal,
+source/rollback and writer-fence receipts, private admission and a new journal.
+Storage projections exclude timestamps and cannot distinguish recreation of an
+otherwise identical bucket. The retained evidence and source hashes are in
+`SHA256SUMS`; raw SQL, credentials and operating rows are absent from the proof.
+
+The earlier `pg17-rollback-rehearsal-final-20261013.json` remains immutable
+historical evidence for the earlier durability implementation.
+`interrupted-attempt-20261013.json` records a stopped attempt without a result;
+its exact owned container was removed after the process was confirmed absent.
+It is not counted as a successful replay.
+
+Private admission formatting does not authenticate observation origin. The
+operating capture adapter, fresh writer-fence evidence, protected promotion,
+hosted application and live user flows remain separate unfinished steps.
+
+The reviewed replay additionally reached the actual eleven-stage apply terminal
+readback, injected an expired timestamp in only the final wrapper guard, observed
+SUCCESSOR_ADMISSION_EXPIRED and confirmed exact baseline restoration from a separate
+read-only connection. This explicitly labelled fault injection proves the final
+guard/rollback path; it does not measure an actual fifteen-minute operating lease.
+The original migration statements and statement vectors remain unchanged.
+# Review-fix verification — 2026-10-14
+
+The current selector now points to `pg17-review-fixes-20261014-v1.json`.
+It binds the changed successor manifest and the unchanged rollback harness.
+Twelve stage readbacks, exact rollback, forced failure, lost acknowledgement
+with one mutation attempt, real contended expiry, and apply-final deadline
+rejection passed on isolated PostgreSQL 17.6. The owned container was removed.
+No hosted rows or operating changes were involved.
+
+The first PR3202 Ubuntu npm full suite correctly rejected stale dependency
+hashes in this directory's complete artifact map. Its bounded failure receipt
+and the old map, pin and selector are retained. The current map binds current
+source bytes while historical proofs remain historical; the new selector is
+backed by a fresh replay, not by relabelling an older proof.
diff --git a/apps/web/performance/review-db-rehearsal-20261012/SHA256SUMS b/apps/web/performance/review-db-rehearsal-20261012/SHA256SUMS
new file mode 100644
index 0000000000..a70e1ddfd0
--- /dev/null
+++ b/apps/web/performance/review-db-rehearsal-20261012/SHA256SUMS
@@ -0,0 +1,36 @@
+295b2354c562ed87102c5e256497a2705f90941587fefa7e9ad8a4ba8e40787f .github/review-media-sql-successor.v1.json
+ef4c24b489343213733ca719b1fca4cb456d4cdd8510e21e6f5c038d5b968b63 apps/web/performance/review-db-rehearsal-20261012/README.md
+ea529addcf42cdb766f622786ccfffb13fe337f70faae6ade6da83736be58f57 apps/web/performance/review-db-rehearsal-20261012/SHA256SUMS-0833ed81-historical
+d8ad511edf7d3832aa432e499b27f33e2c9ad423530ffdff06dea42869eaff85 apps/web/performance/review-db-rehearsal-20261012/SHA256SUMS-4ba9c6d2-historical
+756ead633ae6d2a7805043203c6781d0726cdd3042980031ebc508028f0d3621 apps/web/performance/review-db-rehearsal-20261012/SHA256SUMS-4ba9c6d2-historical.sha256
+6d9cbca375e6790ee1051aa9d9bf1c45beeeece8d919651281885b4c04962981 apps/web/performance/review-db-rehearsal-20261012/SHA256SUMS-4d4ecf60-historical
+5df2ade9cb3618b774eb4d3796df06ade6ba069409de49d0bdd542b34b6ec4ba apps/web/performance/review-db-rehearsal-20261012/SHA256SUMS-7d111349-historical
+7d395c9246782322ccc326dd99ab0d5e94e0d82e203eb519a6baf91a4a62ece2 apps/web/performance/review-db-rehearsal-20261012/SHA256SUMS-7d111349-historical.sha256
+6c474d8e78c0eba3f15c077dbdd8634f073524c3b5f842213372bf0f1fed84fc apps/web/performance/review-db-rehearsal-20261012/SHA256SUMS-pre-hardening-historical
+88a814a1fc0d9b8a8fa7bc6df299d9374bef5f97595736e72579deeb2c475456 apps/web/performance/review-db-rehearsal-20261012/ci-npm-failure-20261014-v1.json
+75b801b80a2d23a7c415085e8fb9b187b03469d2c2d861ba3279d52b2ba7545c apps/web/performance/review-db-rehearsal-20261012/fixture-url-unit-verification-20261013.json
+a12f8880f2a0b8b5493e37ea4b2619962d42643581c14c889f0c6f6e8d528129 apps/web/performance/review-db-rehearsal-20261012/interrupted-attempt-20261013.json
+23fb9b471a06dbd36aafab3a678152ca897893b3cdc968f2d623035d217b1e38 apps/web/performance/review-db-rehearsal-20261012/pg17-review-fixes-20261014-v1.json
+e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 apps/web/performance/review-db-rehearsal-20261012/pg17-review-fixes-20261014-v1.stderr.log
+97682860c19c701a9062a409160bd0e9f2f692f7e296118c48cfba75ad2cc5f9 apps/web/performance/review-db-rehearsal-20261012/pg17-review-fixes-20261014-v1.stdout.log
+9c73b47c0841f2585d7ceee77423d27410a488e61080004cf227d4329dd88076 apps/web/performance/review-db-rehearsal-20261012/pg17-reviewed-boundaries-20261013.json
+b279a6b900dd3be10b9084a6362d169e87435bae955b2a1419858709645a6883 apps/web/performance/review-db-rehearsal-20261012/pg17-rollback-rehearsal-current-20261013.json
+7f74c175be3eee72b5e93eb75dac07bfefc0ef88d7d9957c26d877ee125ff590 apps/web/performance/review-db-rehearsal-20261012/pg17-rollback-rehearsal-final-20261013.json
+cb766e279baad0315e9d7055bc326b41b78a7d50a4972dac3214bc071aee0e49 apps/web/performance/review-db-rehearsal-20261012/pg17-rollback-rehearsal.json
+0cfc9c10ff7b70c606f6eeef33fff8dbd14636dc15b7c963925097454ee454a2 apps/web/performance/review-db-rehearsal-20261012/pg17-terminal-boundaries-20261013.json
+128773e5dabbbb120889716ffbba41e498a37a437aa6799311a81af729703880 apps/web/performance/review-db-rehearsal-20261012/retained-current-proof-0833ed81-historical.json
+71ff7b8dde2ff6ef936a012b84453fcc0990bd9724d89b046bb1ebaa7bfddf1e apps/web/performance/review-db-rehearsal-20261012/retained-current-proof.json
+78f3e0ad5dbc4e3c53cf8dce3b2a9cb2f484f8f72683cbf1a5c659c8f142acb6 apps/web/performance/review-db-rehearsal-20261012/retained-current-proof.json-4ba9c6d2-historical
+20aa7a87b82f034eac71b843b6d5ab4547c4b6e0f6bba950b3e0b7484b2952ff apps/web/performance/review-db-rehearsal-20261012/review-fix-index-units-20261014-v1.log
+7bdf3cd525427da824674ce50cb3ba7c5d54bf6cb0ef8ebe85b21bb2372eae0a apps/web/performance/review-db-rehearsal-20261012/reviewed-boundaries-unit-verification-20261013.json
+c53a467eaf9b929647bc1d66248308ba1ff73eba89fc9221dbf7db4711eb8562 apps/web/performance/review-db-rehearsal-20261012/scanner-readback.json
+fad17a393e5fc74d06dec2be58c41265d21f355d5ddce92a3947bb67a823e73a apps/web/performance/review-db-rehearsal-20261012/terminal-boundary-unit-verification-20261013.json
+f7321f607a5bf3a24ba099d51abc67254db52fcc385945692426d1b2dfa6e431 apps/web/performance/review-db-rehearsal-20261012/verify-review-media-sql-rehearsal.mjs
+7e750e9d0126f6f10afe9056d747a2ea854d462161ea4e8f0168e2751d554c0a apps/web/performance/review-db-successor-20261012/verify-review-media-sql-successor.mjs
+c893c294719776bf5b064c3355866ab97e0fa9412082d9b47d7de5266094c65f apps/web/scripts/review-media-sql-rehearsal.mjs
+4f1b2798e0cfc13db4f11cd6eb4a8653320aa8516eccd71d410fcff6d06a7f24 apps/web/scripts/review-media-sql-successor.mjs
+fcebc20dc4846606dc14bdbe658a07daa32430ad632e52383151f5c22d22efb9 apps/web/scripts/supabase-migration-bundle.mjs
+dc1f2594e629d5af16541db93e2e51f9b98d8282dd7fbeb802dd240fa6f522c0 apps/web/tests-unit/review-media-migration-bundle.test.ts
+54a9560b77a3afb9eb70631224ba4f481caf82eb8b5d2cdc1a076d8831c1ec7e apps/web/tests-unit/review-media-sql-rehearsal-verifier.test.ts
+99c6bc0221188f93317640d4d65affdfeb91fea233bb0f0cc0f409407e419d84 apps/web/tests-unit/review-media-sql-successor-harness.test.ts
+f1ce764e0bffea5c05cbca0017574b305c353f9498623df928a177fee3b780ff apps/web/tests-unit/review-media-sql-successor.test.ts
diff --git a/apps/web/performance/review-db-rehearsal-20261012/SHA256SUMS-0833ed81-historical b/apps/web/performance/review-db-rehearsal-20261012/SHA256SUMS-0833ed81-historical
new file mode 100644
index 0000000000..3db2e6c74b
--- /dev/null
+++ b/apps/web/performance/review-db-rehearsal-20261012/SHA256SUMS-0833ed81-historical
@@ -0,0 +1,20 @@
+ae764bb760102a6d7d066be669c85248feed1823eafded4bf8c246a062090a8c .github/review-media-sql-successor.v1.json
+9a3fd226f9506f83b3be3f09c64d31770536d99ee3e4167bce081b4e5c3d3ad0 apps/web/performance/review-db-rehearsal-20261012/README.md
+6d9cbca375e6790ee1051aa9d9bf1c45beeeece8d919651281885b4c04962981 apps/web/performance/review-db-rehearsal-20261012/SHA256SUMS-4d4ecf60-historical
+6c474d8e78c0eba3f15c077dbdd8634f073524c3b5f842213372bf0f1fed84fc apps/web/performance/review-db-rehearsal-20261012/SHA256SUMS-pre-hardening-historical
+a12f8880f2a0b8b5493e37ea4b2619962d42643581c14c889f0c6f6e8d528129 apps/web/performance/review-db-rehearsal-20261012/interrupted-attempt-20261013.json
+b279a6b900dd3be10b9084a6362d169e87435bae955b2a1419858709645a6883 apps/web/performance/review-db-rehearsal-20261012/pg17-rollback-rehearsal-current-20261013.json
+7f74c175be3eee72b5e93eb75dac07bfefc0ef88d7d9957c26d877ee125ff590 apps/web/performance/review-db-rehearsal-20261012/pg17-rollback-rehearsal-final-20261013.json
+cb766e279baad0315e9d7055bc326b41b78a7d50a4972dac3214bc071aee0e49 apps/web/performance/review-db-rehearsal-20261012/pg17-rollback-rehearsal.json
+0cfc9c10ff7b70c606f6eeef33fff8dbd14636dc15b7c963925097454ee454a2 apps/web/performance/review-db-rehearsal-20261012/pg17-terminal-boundaries-20261013.json
+c53a467eaf9b929647bc1d66248308ba1ff73eba89fc9221dbf7db4711eb8562 apps/web/performance/review-db-rehearsal-20261012/scanner-readback.json
+fad17a393e5fc74d06dec2be58c41265d21f355d5ddce92a3947bb67a823e73a apps/web/performance/review-db-rehearsal-20261012/terminal-boundary-unit-verification-20261013.json
+8088aa57f39045e2bf93cba084711119395098d053318b70aaa676b5c13fea26 apps/web/performance/review-db-rehearsal-20261012/verify-review-media-sql-rehearsal.mjs
+8c3d7df52c281a9df1d6a4bd2acefb59f9a23b60185559fc6a8298cd65fde370 apps/web/performance/review-db-successor-20261012/verify-review-media-sql-successor.mjs
+c893c294719776bf5b064c3355866ab97e0fa9412082d9b47d7de5266094c65f apps/web/scripts/review-media-sql-rehearsal.mjs
+dcf552697251c2f10d859c7a2f56937a0f6e92ee9467e5b919807e498eff1d49 apps/web/scripts/review-media-sql-successor.mjs
+fcebc20dc4846606dc14bdbe658a07daa32430ad632e52383151f5c22d22efb9 apps/web/scripts/supabase-migration-bundle.mjs
+dc1f2594e629d5af16541db93e2e51f9b98d8282dd7fbeb802dd240fa6f522c0 apps/web/tests-unit/review-media-migration-bundle.test.ts
+b25b5dcc6516435a1c74a038c20856cc5dd719b303d9c3f3e600dc314197c7cd apps/web/tests-unit/review-media-sql-rehearsal-verifier.test.ts
+31bd78568b7bff5196626d87cf7f0fe9b84d695da8cef597fa93e4d742f99845 apps/web/tests-unit/review-media-sql-successor-harness.test.ts
+7d32dccb70c6b1b79379ee69864d2c092d3a2e39863ab365494622a0effeba62 apps/web/tests-unit/review-media-sql-successor.test.ts
diff --git a/apps/web/performance/review-db-rehearsal-20261012/SHA256SUMS-4ba9c6d2-historical b/apps/web/performance/review-db-rehearsal-20261012/SHA256SUMS-4ba9c6d2-historical
new file mode 100644
index 0000000000..f12e0be4cc
--- /dev/null
+++ b/apps/web/performance/review-db-rehearsal-20261012/SHA256SUMS-4ba9c6d2-historical
@@ -0,0 +1,28 @@
+23bf7e7d7e9e5d7962021dd3ec860e87b47d07e119ba0e39de2e423a0a601f4e .github/review-media-sql-successor.v1.json
+ac14e363f38d6cb6acba0e24a5ad6c6943c8e8c15152cff49fcd86a2d014bd9d apps/web/performance/review-db-rehearsal-20261012/README.md
+ea529addcf42cdb766f622786ccfffb13fe337f70faae6ade6da83736be58f57 apps/web/performance/review-db-rehearsal-20261012/SHA256SUMS-0833ed81-historical
+6d9cbca375e6790ee1051aa9d9bf1c45beeeece8d919651281885b4c04962981 apps/web/performance/review-db-rehearsal-20261012/SHA256SUMS-4d4ecf60-historical
+5df2ade9cb3618b774eb4d3796df06ade6ba069409de49d0bdd542b34b6ec4ba apps/web/performance/review-db-rehearsal-20261012/SHA256SUMS-7d111349-historical
+7d395c9246782322ccc326dd99ab0d5e94e0d82e203eb519a6baf91a4a62ece2 apps/web/performance/review-db-rehearsal-20261012/SHA256SUMS-7d111349-historical.sha256
+6c474d8e78c0eba3f15c077dbdd8634f073524c3b5f842213372bf0f1fed84fc apps/web/performance/review-db-rehearsal-20261012/SHA256SUMS-pre-hardening-historical
+75b801b80a2d23a7c415085e8fb9b187b03469d2c2d861ba3279d52b2ba7545c apps/web/performance/review-db-rehearsal-20261012/fixture-url-unit-verification-20261013.json
+a12f8880f2a0b8b5493e37ea4b2619962d42643581c14c889f0c6f6e8d528129 apps/web/performance/review-db-rehearsal-20261012/interrupted-attempt-20261013.json
+9c73b47c0841f2585d7ceee77423d27410a488e61080004cf227d4329dd88076 apps/web/performance/review-db-rehearsal-20261012/pg17-reviewed-boundaries-20261013.json
+b279a6b900dd3be10b9084a6362d169e87435bae955b2a1419858709645a6883 apps/web/performance/review-db-rehearsal-20261012/pg17-rollback-rehearsal-current-20261013.json
+7f74c175be3eee72b5e93eb75dac07bfefc0ef88d7d9957c26d877ee125ff590 apps/web/performance/review-db-rehearsal-20261012/pg17-rollback-rehearsal-final-20261013.json
+cb766e279baad0315e9d7055bc326b41b78a7d50a4972dac3214bc071aee0e49 apps/web/performance/review-db-rehearsal-20261012/pg17-rollback-rehearsal.json
+0cfc9c10ff7b70c606f6eeef33fff8dbd14636dc15b7c963925097454ee454a2 apps/web/performance/review-db-rehearsal-20261012/pg17-terminal-boundaries-20261013.json
+128773e5dabbbb120889716ffbba41e498a37a437aa6799311a81af729703880 apps/web/performance/review-db-rehearsal-20261012/retained-current-proof-0833ed81-historical.json
+78f3e0ad5dbc4e3c53cf8dce3b2a9cb2f484f8f72683cbf1a5c659c8f142acb6 apps/web/performance/review-db-rehearsal-20261012/retained-current-proof.json
+7bdf3cd525427da824674ce50cb3ba7c5d54bf6cb0ef8ebe85b21bb2372eae0a apps/web/performance/review-db-rehearsal-20261012/reviewed-boundaries-unit-verification-20261013.json
+c53a467eaf9b929647bc1d66248308ba1ff73eba89fc9221dbf7db4711eb8562 apps/web/performance/review-db-rehearsal-20261012/scanner-readback.json
+fad17a393e5fc74d06dec2be58c41265d21f355d5ddce92a3947bb67a823e73a apps/web/performance/review-db-rehearsal-20261012/terminal-boundary-unit-verification-20261013.json
+f7321f607a5bf3a24ba099d51abc67254db52fcc385945692426d1b2dfa6e431 apps/web/performance/review-db-rehearsal-20261012/verify-review-media-sql-rehearsal.mjs
+768b8887a30c2b8f496a73a21d373b85a0b64fadaed3a1485034a729ee704d39 apps/web/performance/review-db-successor-20261012/verify-review-media-sql-successor.mjs
+c893c294719776bf5b064c3355866ab97e0fa9412082d9b47d7de5266094c65f apps/web/scripts/review-media-sql-rehearsal.mjs
+5d61808b45661d412937fbfdfd3bfb75b1cf92c7c285e4d8ad061a2f667c3c0b apps/web/scripts/review-media-sql-successor.mjs
+fcebc20dc4846606dc14bdbe658a07daa32430ad632e52383151f5c22d22efb9 apps/web/scripts/supabase-migration-bundle.mjs
+dc1f2594e629d5af16541db93e2e51f9b98d8282dd7fbeb802dd240fa6f522c0 apps/web/tests-unit/review-media-migration-bundle.test.ts
+54a9560b77a3afb9eb70631224ba4f481caf82eb8b5d2cdc1a076d8831c1ec7e apps/web/tests-unit/review-media-sql-rehearsal-verifier.test.ts
+983a62a3f81973e18914835b2d483a7a05fef3fa9f7d129ffd7a60ed5cd4e551 apps/web/tests-unit/review-media-sql-successor-harness.test.ts
+f74f30aa6f1237a757c7fd18693fa839cb4c199409a87d870260ec574bfc5d33 apps/web/tests-unit/review-media-sql-successor.test.ts
diff --git a/apps/web/performance/review-db-rehearsal-20261012/SHA256SUMS-4ba9c6d2-historical.sha256 b/apps/web/performance/review-db-rehearsal-20261012/SHA256SUMS-4ba9c6d2-historical.sha256
new file mode 100644
index 0000000000..98e05b4991
--- /dev/null
+++ b/apps/web/performance/review-db-rehearsal-20261012/SHA256SUMS-4ba9c6d2-historical.sha256
@@ -0,0 +1 @@
+d8ad511edf7d3832aa432e499b27f33e2c9ad423530ffdff06dea42869eaff85
diff --git a/apps/web/performance/review-db-rehearsal-20261012/SHA256SUMS-4d4ecf60-historical b/apps/web/performance/review-db-rehearsal-20261012/SHA256SUMS-4d4ecf60-historical
new file mode 100644
index 0000000000..2fbd3f4e24
--- /dev/null
+++ b/apps/web/performance/review-db-rehearsal-20261012/SHA256SUMS-4d4ecf60-historical
@@ -0,0 +1,16 @@
+8297b18793513956118c720e3aa5bd481d0769645c263f680a5272825b70569e .github/review-media-sql-successor.v1.json
+2080947cbc3c92934c98cee259d4f41e9c1eebc80e437f20862849fd36bd4f40 apps/web/scripts/review-media-sql-successor.mjs
+c893c294719776bf5b064c3355866ab97e0fa9412082d9b47d7de5266094c65f apps/web/scripts/review-media-sql-rehearsal.mjs
+fcebc20dc4846606dc14bdbe658a07daa32430ad632e52383151f5c22d22efb9 apps/web/scripts/supabase-migration-bundle.mjs
+7ab17835c727c22bad4138b86f4d3695d55bd1b4511141d1d374b2ca4694d080 apps/web/performance/review-db-successor-20261012/verify-review-media-sql-successor.mjs
+0489bf7de7252e652c9c4967be77664cdeacb8afc07dcf846d73f0cf7c25dbe9 apps/web/performance/review-db-rehearsal-20261012/README.md
+6c474d8e78c0eba3f15c077dbdd8634f073524c3b5f842213372bf0f1fed84fc apps/web/performance/review-db-rehearsal-20261012/SHA256SUMS-pre-hardening-historical
+b279a6b900dd3be10b9084a6362d169e87435bae955b2a1419858709645a6883 apps/web/performance/review-db-rehearsal-20261012/pg17-rollback-rehearsal-current-20261013.json
+7f74c175be3eee72b5e93eb75dac07bfefc0ef88d7d9957c26d877ee125ff590 apps/web/performance/review-db-rehearsal-20261012/pg17-rollback-rehearsal-final-20261013.json
+cb766e279baad0315e9d7055bc326b41b78a7d50a4972dac3214bc071aee0e49 apps/web/performance/review-db-rehearsal-20261012/pg17-rollback-rehearsal.json
+c53a467eaf9b929647bc1d66248308ba1ff73eba89fc9221dbf7db4711eb8562 apps/web/performance/review-db-rehearsal-20261012/scanner-readback.json
+8088aa57f39045e2bf93cba084711119395098d053318b70aaa676b5c13fea26 apps/web/performance/review-db-rehearsal-20261012/verify-review-media-sql-rehearsal.mjs
+099e0b728cbfde7f1b292ec5c90e1ab14af54bdf9e87d07ef44ead9bb885883e apps/web/tests-unit/review-media-sql-successor.test.ts
+dc1f2594e629d5af16541db93e2e51f9b98d8282dd7fbeb802dd240fa6f522c0 apps/web/tests-unit/review-media-migration-bundle.test.ts
+c2c833a6fb2529477e02bbdc353a1e840d0c079d045a81dbc8a0e905ef16a601 apps/web/tests-unit/review-media-sql-successor-harness.test.ts
+b25b5dcc6516435a1c74a038c20856cc5dd719b303d9c3f3e600dc314197c7cd apps/web/tests-unit/review-media-sql-rehearsal-verifier.test.ts
diff --git a/apps/web/performance/review-db-rehearsal-20261012/SHA256SUMS-7d111349-historical b/apps/web/performance/review-db-rehearsal-20261012/SHA256SUMS-7d111349-historical
new file mode 100644
index 0000000000..a86feae869
--- /dev/null
+++ b/apps/web/performance/review-db-rehearsal-20261012/SHA256SUMS-7d111349-historical
@@ -0,0 +1,25 @@
+23bf7e7d7e9e5d7962021dd3ec860e87b47d07e119ba0e39de2e423a0a601f4e .github/review-media-sql-successor.v1.json
+ac14e363f38d6cb6acba0e24a5ad6c6943c8e8c15152cff49fcd86a2d014bd9d apps/web/performance/review-db-rehearsal-20261012/README.md
+ea529addcf42cdb766f622786ccfffb13fe337f70faae6ade6da83736be58f57 apps/web/performance/review-db-rehearsal-20261012/SHA256SUMS-0833ed81-historical
+6d9cbca375e6790ee1051aa9d9bf1c45beeeece8d919651281885b4c04962981 apps/web/performance/review-db-rehearsal-20261012/SHA256SUMS-4d4ecf60-historical
+6c474d8e78c0eba3f15c077dbdd8634f073524c3b5f842213372bf0f1fed84fc apps/web/performance/review-db-rehearsal-20261012/SHA256SUMS-pre-hardening-historical
+a12f8880f2a0b8b5493e37ea4b2619962d42643581c14c889f0c6f6e8d528129 apps/web/performance/review-db-rehearsal-20261012/interrupted-attempt-20261013.json
+9c73b47c0841f2585d7ceee77423d27410a488e61080004cf227d4329dd88076 apps/web/performance/review-db-rehearsal-20261012/pg17-reviewed-boundaries-20261013.json
+b279a6b900dd3be10b9084a6362d169e87435bae955b2a1419858709645a6883 apps/web/performance/review-db-rehearsal-20261012/pg17-rollback-rehearsal-current-20261013.json
+7f74c175be3eee72b5e93eb75dac07bfefc0ef88d7d9957c26d877ee125ff590 apps/web/performance/review-db-rehearsal-20261012/pg17-rollback-rehearsal-final-20261013.json
+cb766e279baad0315e9d7055bc326b41b78a7d50a4972dac3214bc071aee0e49 apps/web/performance/review-db-rehearsal-20261012/pg17-rollback-rehearsal.json
+0cfc9c10ff7b70c606f6eeef33fff8dbd14636dc15b7c963925097454ee454a2 apps/web/performance/review-db-rehearsal-20261012/pg17-terminal-boundaries-20261013.json
+128773e5dabbbb120889716ffbba41e498a37a437aa6799311a81af729703880 apps/web/performance/review-db-rehearsal-20261012/retained-current-proof-0833ed81-historical.json
+78f3e0ad5dbc4e3c53cf8dce3b2a9cb2f484f8f72683cbf1a5c659c8f142acb6 apps/web/performance/review-db-rehearsal-20261012/retained-current-proof.json
+7bdf3cd525427da824674ce50cb3ba7c5d54bf6cb0ef8ebe85b21bb2372eae0a apps/web/performance/review-db-rehearsal-20261012/reviewed-boundaries-unit-verification-20261013.json
+c53a467eaf9b929647bc1d66248308ba1ff73eba89fc9221dbf7db4711eb8562 apps/web/performance/review-db-rehearsal-20261012/scanner-readback.json
+fad17a393e5fc74d06dec2be58c41265d21f355d5ddce92a3947bb67a823e73a apps/web/performance/review-db-rehearsal-20261012/terminal-boundary-unit-verification-20261013.json
+f7321f607a5bf3a24ba099d51abc67254db52fcc385945692426d1b2dfa6e431 apps/web/performance/review-db-rehearsal-20261012/verify-review-media-sql-rehearsal.mjs
+768b8887a30c2b8f496a73a21d373b85a0b64fadaed3a1485034a729ee704d39 apps/web/performance/review-db-successor-20261012/verify-review-media-sql-successor.mjs
+c893c294719776bf5b064c3355866ab97e0fa9412082d9b47d7de5266094c65f apps/web/scripts/review-media-sql-rehearsal.mjs
+5d61808b45661d412937fbfdfd3bfb75b1cf92c7c285e4d8ad061a2f667c3c0b apps/web/scripts/review-media-sql-successor.mjs
+fcebc20dc4846606dc14bdbe658a07daa32430ad632e52383151f5c22d22efb9 apps/web/scripts/supabase-migration-bundle.mjs
+dc1f2594e629d5af16541db93e2e51f9b98d8282dd7fbeb802dd240fa6f522c0 apps/web/tests-unit/review-media-migration-bundle.test.ts
+54a9560b77a3afb9eb70631224ba4f481caf82eb8b5d2cdc1a076d8831c1ec7e apps/web/tests-unit/review-media-sql-rehearsal-verifier.test.ts
+983a62a3f81973e18914835b2d483a7a05fef3fa9f7d129ffd7a60ed5cd4e551 apps/web/tests-unit/review-media-sql-successor-harness.test.ts
+b9fbfa0fdc249e3c495043679c39a8a98cd14f8de6a225f20d926d83702fd196 apps/web/tests-unit/review-media-sql-successor.test.ts
diff --git a/apps/web/performance/review-db-rehearsal-20261012/SHA256SUMS-7d111349-historical.sha256 b/apps/web/performance/review-db-rehearsal-20261012/SHA256SUMS-7d111349-historical.sha256
new file mode 100644
index 0000000000..fcebbfbfad
--- /dev/null
+++ b/apps/web/performance/review-db-rehearsal-20261012/SHA256SUMS-7d111349-historical.sha256
@@ -0,0 +1 @@
+5df2ade9cb3618b774eb4d3796df06ade6ba069409de49d0bdd542b34b6ec4ba
diff --git a/apps/web/performance/review-db-rehearsal-20261012/SHA256SUMS-pre-hardening-historical b/apps/web/performance/review-db-rehearsal-20261012/SHA256SUMS-pre-hardening-historical
new file mode 100644
index 0000000000..b53872fe14
--- /dev/null
+++ b/apps/web/performance/review-db-rehearsal-20261012/SHA256SUMS-pre-hardening-historical
@@ -0,0 +1,7 @@
+f6cf33c2f5f5fdc8f5a5361f1ffc54a2c2cb70aa752da4815e4f614dffba3410 .github/review-media-sql-successor.v1.json
+dfef9d60199b8656cf6bb50ce32ba19825a8caf1a1dbca28f08e91e4d6c7e38c apps/web/scripts/review-media-sql-successor.mjs
+0857216fc85b1a59a29bf70a0550a285952cde97fb9553b0860da04046908a49 apps/web/scripts/review-media-sql-rehearsal.mjs
+fcebc20dc4846606dc14bdbe658a07daa32430ad632e52383151f5c22d22efb9 apps/web/scripts/supabase-migration-bundle.mjs
+29622de79b7ebc63080ef3edb4c7f9f8ba61e14efa5600fcb589791a53323356 apps/web/performance/review-db-rehearsal-20261012/verify-review-media-sql-rehearsal.mjs
+cb766e279baad0315e9d7055bc326b41b78a7d50a4972dac3214bc071aee0e49 apps/web/performance/review-db-rehearsal-20261012/pg17-rollback-rehearsal.json
+159d19163e920882ce0ae98c16eed8120d53bebac4e3c173615d930ebddcfc31 apps/web/performance/review-db-rehearsal-20261012/README.md
diff --git a/apps/web/performance/review-db-rehearsal-20261012/SHA256SUMS.sha256 b/apps/web/performance/review-db-rehearsal-20261012/SHA256SUMS.sha256
new file mode 100644
index 0000000000..290cf460a0
--- /dev/null
+++ b/apps/web/performance/review-db-rehearsal-20261012/SHA256SUMS.sha256
@@ -0,0 +1 @@
+c1af1afd832bd2b0bbdcd4419589de778525e1b18a4dff196e457b82381c97fb
diff --git a/apps/web/performance/review-db-rehearsal-20261012/ci-npm-failure-20261014-v1.json b/apps/web/performance/review-db-rehearsal-20261012/ci-npm-failure-20261014-v1.json
new file mode 100644
index 0000000000..970d468726
--- /dev/null
+++ b/apps/web/performance/review-db-rehearsal-20261012/ci-npm-failure-20261014-v1.json
@@ -0,0 +1,19 @@
+{
+ "observedAtUTC": "2026-10-13T20:31:32.330565+00:00",
+ "sourceRevision": "71dcfc98ab03a016d05449780574a4b9edd81e36",
+ "runId": 38461911693,
+ "jobId": 115518972088,
+ "job": "Ubuntu npm",
+ "boundedFailure": "LOCAL_REHEARSAL_ARTIFACT_SHA256_MISMATCH",
+ "failedTest": "default retained proof uses a stable current-proof selector",
+ "summary": [
+ "(pass) verified review count pagination > thrown page 2 failure rejects instead of returning partial counts [0.01ms]",
+ " 3318 pass",
+ " 21 skip",
+ " 1 fail",
+ "Ran 3340 tests across 420 files. [164.92s]"
+ ],
+ "cause": "Rollback evidence map retained hashes for the changed successor controller, manifest and fixture helper. The full-map validator correctly rejected stale dependencies.",
+ "fullProviderLogPersistedInRepository": false,
+ "operatingEvidence": false
+}
diff --git a/apps/web/performance/review-db-rehearsal-20261012/fixture-url-unit-verification-20261013.json b/apps/web/performance/review-db-rehearsal-20261012/fixture-url-unit-verification-20261013.json
new file mode 100644
index 0000000000..dce33ee5f0
--- /dev/null
+++ b/apps/web/performance/review-db-rehearsal-20261012/fixture-url-unit-verification-20261013.json
@@ -0,0 +1,22 @@
+{
+ "observedAtUtc": "2026-10-13T12:27:16.299025+00:00",
+ "sourceHead": "55a47b105c8709726d578cc935afc253bf6d2cc7",
+ "baselineHead": "7d11134938d79010073bf69c4970c9144a5308c5",
+ "changedSource": [
+ "apps/web/tests-unit/review-media-sql-successor.test.ts"
+ ],
+ "finalFixtureUriByteEqual": true,
+ "passwordPresent": false,
+ "hostedConnectionsPerformed": 0,
+ "testPass": 54,
+ "testFail": 0,
+ "expectCalls": 339,
+ "durationS": 28.39,
+ "lintExit": 0,
+ "actualPostgresReplayRerun": false,
+ "sqlControllerManifestAndReplayDriverUnchanged": true,
+ "retainedPg17ProofUnchanged": true,
+ "oldMapSha256": "5df2ade9cb3618b774eb4d3796df06ade6ba069409de49d0bdd542b34b6ec4ba",
+ "oldDetachedPinSha256": "7d395c9246782322ccc326dd99ab0d5e94e0d82e203eb519a6baf91a4a62ece2",
+ "reason": "Refresh mutable current artifact index for one semantically identical fixture source. Frozen proof results and historical index bytes are retained; no fresh database run is claimed."
+}
diff --git a/apps/web/performance/review-db-rehearsal-20261012/interrupted-attempt-20261013.json b/apps/web/performance/review-db-rehearsal-20261012/interrupted-attempt-20261013.json
new file mode 100644
index 0000000000..c3c408a521
--- /dev/null
+++ b/apps/web/performance/review-db-rehearsal-20261012/interrupted-attempt-20261013.json
@@ -0,0 +1,13 @@
+{
+ "schemaVersion": 1,
+ "kind": "interrupted-local-rehearsal",
+ "recordedAtUTC": "2026-10-13T06:50:57.884Z",
+ "processAbsent": true,
+ "proofPresent": false,
+ "containerId": "b856aa37d9d1d3963b4b6a8a01591e80bff4cb98de4e89681d206d76fed643f3",
+ "containerName": "tzudong-review-rollback-rehearsal-59656-300e3940feb7",
+ "ownershipVerified": true,
+ "removed": true,
+ "mutationOutcome": "not-established",
+ "sourceContainerRemoved": false
+}
diff --git a/apps/web/performance/review-db-rehearsal-20261012/pg17-review-fixes-20261014-v1.json b/apps/web/performance/review-db-rehearsal-20261012/pg17-review-fixes-20261014-v1.json
new file mode 100644
index 0000000000..fac22e271d
--- /dev/null
+++ b/apps/web/performance/review-db-rehearsal-20261012/pg17-review-fixes-20261014-v1.json
@@ -0,0 +1,72 @@
+{
+ "schemaVersion": 1,
+ "status": "passed",
+ "environment": {
+ "engine": "17.6",
+ "image": "supabase/postgres@sha256:fbe6c858a2ea9616aead3c39e132afcc0660decc5cb7dc656208ed145f5331ab",
+ "emptyRowChecks": [
+ "public.profiles",
+ "public.reviews"
+ ]
+ },
+ "manifestSha256": "295b2354c562ed87102c5e256497a2705f90941587fefa7e9ad8a4ba8e40787f",
+ "manifestToolchainRoot": "6409387ff1da0bb5e499dab76e4b0b7326a9a81aa71fd21f2d51682a48df0a2f",
+ "rehearsalHarnessSha256": "f7321f607a5bf3a24ba099d51abc67254db52fcc385945692426d1b2dfa6e431",
+ "sourceRoot": "61f9d7637c0714a7b0d9108c6f7959ea820175a7870dba681498b570296a1544",
+ "fixtureDumpSha256": "b7777306a43cc84408434e9835973604b2eb520cf9e9e8d0ee4bba6dcb41257f",
+ "predecessorSources": [
+ {
+ "path": "backend/supabase/migrations/20261004190259_admin_record_guarded_actions.sql",
+ "sha256": "b373b7ea472c0352a33a4d4043cf8d6aa8474c04cc1ca5778805edfa77ac9c95"
+ },
+ {
+ "path": "backend/supabase/migrations/20261004192657_admin_evaluation_raw_warning_groups.sql",
+ "sha256": "66eace1d6fb0dd55c1d780776bab855cc37690335ad40b0fdc1294c610e07d3a"
+ },
+ {
+ "path": "backend/supabase/migrations/20261004194715_admin_evaluation_raw_warning_invoker_contract.sql",
+ "sha256": "e1c105df82c4f814d3e6adad807ff9d072b8cd42ae770b4b78f75b89a9387020"
+ },
+ {
+ "path": "backend/supabase/migrations/20261009022915_restaurant_review_manual_preview_eligibility.sql",
+ "sha256": "8acf6d1428764260ed57dac5fe09711868a82896f0a6d631d502128004c168a3"
+ },
+ {
+ "path": "backend/supabase/migrations/20261009091342_admin_record_private_verification_cleanup.sql",
+ "sha256": "b596b200e52c6813a4cfa1b0a2818625f067864549e8854f3497afdcdab706da"
+ },
+ {
+ "path": "backend/supabase/migrations/20261009101645_admin_user_management_rpc_forward.sql",
+ "sha256": "2067538f89c9f90d28e784672c7a1288306ba22d5ae92b087c8692503da9b1ae"
+ }
+ ],
+ "migrationCount": 11,
+ "statementCount": 112,
+ "baselineStateSha256": "3802888324e1651ad7b2f71e7cf15fac78ab77aa57637d0c60431f8da67e6298",
+ "stageStatesSha256": "bd25ecbd82759ca75e994ca0f1da38b49acc848d5dd556e702e4e43024edaa65",
+ "terminalStateSha256": "9cf59a5d983e78f6f7d9a594267c6feda3276611bf3539289b8a3de3295f700a",
+ "compiledSqlBytes": 2510560,
+ "compiledSqlSha256": "285d0c859117006646389dbd2a22de9aef1c0b97bc796a9e4d4448a8a232b093",
+ "applyCompiledSqlSha256": "91e378c58d58d7d7f818464c9c92aff02c7b01ca528e9db858e2b1d2d00bd92f",
+ "applyExpiryFaultedSqlSha256": "727ff0f6be2dbf8ee06551d4efd6ebe665f7187189f7a0a1159d5281ae90635b",
+ "results": {
+ "rollbackStageCount": 12,
+ "rollbackExact": true,
+ "forcedErrorRejected": true,
+ "forcedErrorRollbackExact": true,
+ "lostResponseMutationAttempts": 1,
+ "lostResponseRestorationExact": true,
+ "contendedExpiryRejected": true,
+ "contendedExpiryRollbackExact": true,
+ "applyFinalGuardDeadlineFaultInjected": true,
+ "applyTerminalReadbackObserved": true,
+ "applyExpiryRejectedBeforeCommit": true,
+ "applyExpiryMutationAttempts": 1,
+ "applyExpiryRollbackExact": true
+ },
+ "hostedWrites": false,
+ "operatingRowsRead": false,
+ "rawSqlInProof": false,
+ "sourceMutation": false,
+ "temporaryContainerRemoved": true
+}
diff --git a/apps/web/performance/review-db-rehearsal-20261012/pg17-review-fixes-20261014-v1.stderr.log b/apps/web/performance/review-db-rehearsal-20261012/pg17-review-fixes-20261014-v1.stderr.log
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/apps/web/performance/review-db-rehearsal-20261012/pg17-review-fixes-20261014-v1.stdout.log b/apps/web/performance/review-db-rehearsal-20261012/pg17-review-fixes-20261014-v1.stdout.log
new file mode 100644
index 0000000000..429c11d1fc
--- /dev/null
+++ b/apps/web/performance/review-db-rehearsal-20261012/pg17-review-fixes-20261014-v1.stdout.log
@@ -0,0 +1 @@
+{"kind":"review-media-sql-local-rehearsal","retainedProof":{"present":true,"classification":"retained-historical-evidence","repositoryPath":"apps/web/performance/review-db-rehearsal-20261012/pg17-reviewed-boundaries-20261013.json","selector":{"repositoryPath":"apps/web/performance/review-db-rehearsal-20261012/retained-current-proof.json","sha256":"78f3e0ad5dbc4e3c53cf8dce3b2a9cb2f484f8f72683cbf1a5c659c8f142acb6","proofPath":"pg17-reviewed-boundaries-20261013.json","proofSha256":"9c73b47c0841f2585d7ceee77423d27410a488e61080004cf227d4329dd88076"},"expectedSha256":"9c73b47c0841f2585d7ceee77423d27410a488e61080004cf227d4329dd88076","actualSha256":"9c73b47c0841f2585d7ceee77423d27410a488e61080004cf227d4329dd88076","currentSourceMatch":false,"usedAsCurrentProof":false,"bindingDifferences":[{"field":"manifestSha256","current":"295b2354c562ed87102c5e256497a2705f90941587fefa7e9ad8a4ba8e40787f","retained":"23bf7e7d7e9e5d7962021dd3ec860e87b47d07e119ba0e39de2e423a0a601f4e"}]},"currentProof":{"schemaVersion":1,"status":"passed","environment":{"engine":"17.6","image":"supabase/postgres@sha256:fbe6c858a2ea9616aead3c39e132afcc0660decc5cb7dc656208ed145f5331ab","emptyRowChecks":["public.profiles","public.reviews"]},"manifestSha256":"295b2354c562ed87102c5e256497a2705f90941587fefa7e9ad8a4ba8e40787f","manifestToolchainRoot":"6409387ff1da0bb5e499dab76e4b0b7326a9a81aa71fd21f2d51682a48df0a2f","rehearsalHarnessSha256":"f7321f607a5bf3a24ba099d51abc67254db52fcc385945692426d1b2dfa6e431","sourceRoot":"61f9d7637c0714a7b0d9108c6f7959ea820175a7870dba681498b570296a1544","fixtureDumpSha256":"b7777306a43cc84408434e9835973604b2eb520cf9e9e8d0ee4bba6dcb41257f","predecessorSources":[{"path":"backend/supabase/migrations/20261004190259_admin_record_guarded_actions.sql","sha256":"b373b7ea472c0352a33a4d4043cf8d6aa8474c04cc1ca5778805edfa77ac9c95"},{"path":"backend/supabase/migrations/20261004192657_admin_evaluation_raw_warning_groups.sql","sha256":"66eace1d6fb0dd55c1d780776bab855cc37690335ad40b0fdc1294c610e07d3a"},{"path":"backend/supabase/migrations/20261004194715_admin_evaluation_raw_warning_invoker_contract.sql","sha256":"e1c105df82c4f814d3e6adad807ff9d072b8cd42ae770b4b78f75b89a9387020"},{"path":"backend/supabase/migrations/20261009022915_restaurant_review_manual_preview_eligibility.sql","sha256":"8acf6d1428764260ed57dac5fe09711868a82896f0a6d631d502128004c168a3"},{"path":"backend/supabase/migrations/20261009091342_admin_record_private_verification_cleanup.sql","sha256":"b596b200e52c6813a4cfa1b0a2818625f067864549e8854f3497afdcdab706da"},{"path":"backend/supabase/migrations/20261009101645_admin_user_management_rpc_forward.sql","sha256":"2067538f89c9f90d28e784672c7a1288306ba22d5ae92b087c8692503da9b1ae"}],"migrationCount":11,"statementCount":112,"baselineStateSha256":"3802888324e1651ad7b2f71e7cf15fac78ab77aa57637d0c60431f8da67e6298","stageStatesSha256":"bd25ecbd82759ca75e994ca0f1da38b49acc848d5dd556e702e4e43024edaa65","terminalStateSha256":"9cf59a5d983e78f6f7d9a594267c6feda3276611bf3539289b8a3de3295f700a","compiledSqlBytes":2510560,"compiledSqlSha256":"285d0c859117006646389dbd2a22de9aef1c0b97bc796a9e4d4448a8a232b093","applyCompiledSqlSha256":"91e378c58d58d7d7f818464c9c92aff02c7b01ca528e9db858e2b1d2d00bd92f","applyExpiryFaultedSqlSha256":"727ff0f6be2dbf8ee06551d4efd6ebe665f7187189f7a0a1159d5281ae90635b","results":{"rollbackStageCount":12,"rollbackExact":true,"forcedErrorRejected":true,"forcedErrorRollbackExact":true,"lostResponseMutationAttempts":1,"lostResponseRestorationExact":true,"contendedExpiryRejected":true,"contendedExpiryRollbackExact":true,"applyFinalGuardDeadlineFaultInjected":true,"applyTerminalReadbackObserved":true,"applyExpiryRejectedBeforeCommit":true,"applyExpiryMutationAttempts":1,"applyExpiryRollbackExact":true},"hostedWrites":false,"operatingRowsRead":false,"rawSqlInProof":false,"sourceMutation":false,"temporaryContainerRemoved":true},"currentOutputPath":"/Users/twoimo/.codex/worktrees/mobile-nav-read-20261012/tzudong/apps/web/performance/review-db-rehearsal-20261012/pg17-review-fixes-20261014-v1.json"}
diff --git a/apps/web/performance/review-db-rehearsal-20261012/pg17-reviewed-boundaries-20261013.json b/apps/web/performance/review-db-rehearsal-20261012/pg17-reviewed-boundaries-20261013.json
new file mode 100644
index 0000000000..8bb6d809cc
--- /dev/null
+++ b/apps/web/performance/review-db-rehearsal-20261012/pg17-reviewed-boundaries-20261013.json
@@ -0,0 +1,72 @@
+{
+ "schemaVersion": 1,
+ "status": "passed",
+ "environment": {
+ "engine": "17.6",
+ "image": "supabase/postgres@sha256:fbe6c858a2ea9616aead3c39e132afcc0660decc5cb7dc656208ed145f5331ab",
+ "emptyRowChecks": [
+ "public.profiles",
+ "public.reviews"
+ ]
+ },
+ "manifestSha256": "23bf7e7d7e9e5d7962021dd3ec860e87b47d07e119ba0e39de2e423a0a601f4e",
+ "manifestToolchainRoot": "579f315ba6285ea7ccc562cf2cf8949203a0a3289e4de5f82be1687ddea4170b",
+ "rehearsalHarnessSha256": "f7321f607a5bf3a24ba099d51abc67254db52fcc385945692426d1b2dfa6e431",
+ "sourceRoot": "61f9d7637c0714a7b0d9108c6f7959ea820175a7870dba681498b570296a1544",
+ "fixtureDumpSha256": "b7777306a43cc84408434e9835973604b2eb520cf9e9e8d0ee4bba6dcb41257f",
+ "predecessorSources": [
+ {
+ "path": "backend/supabase/migrations/20261004190259_admin_record_guarded_actions.sql",
+ "sha256": "b373b7ea472c0352a33a4d4043cf8d6aa8474c04cc1ca5778805edfa77ac9c95"
+ },
+ {
+ "path": "backend/supabase/migrations/20261004192657_admin_evaluation_raw_warning_groups.sql",
+ "sha256": "66eace1d6fb0dd55c1d780776bab855cc37690335ad40b0fdc1294c610e07d3a"
+ },
+ {
+ "path": "backend/supabase/migrations/20261004194715_admin_evaluation_raw_warning_invoker_contract.sql",
+ "sha256": "e1c105df82c4f814d3e6adad807ff9d072b8cd42ae770b4b78f75b89a9387020"
+ },
+ {
+ "path": "backend/supabase/migrations/20261009022915_restaurant_review_manual_preview_eligibility.sql",
+ "sha256": "8acf6d1428764260ed57dac5fe09711868a82896f0a6d631d502128004c168a3"
+ },
+ {
+ "path": "backend/supabase/migrations/20261009091342_admin_record_private_verification_cleanup.sql",
+ "sha256": "b596b200e52c6813a4cfa1b0a2818625f067864549e8854f3497afdcdab706da"
+ },
+ {
+ "path": "backend/supabase/migrations/20261009101645_admin_user_management_rpc_forward.sql",
+ "sha256": "2067538f89c9f90d28e784672c7a1288306ba22d5ae92b087c8692503da9b1ae"
+ }
+ ],
+ "migrationCount": 11,
+ "statementCount": 112,
+ "baselineStateSha256": "3802888324e1651ad7b2f71e7cf15fac78ab77aa57637d0c60431f8da67e6298",
+ "stageStatesSha256": "bd25ecbd82759ca75e994ca0f1da38b49acc848d5dd556e702e4e43024edaa65",
+ "terminalStateSha256": "9cf59a5d983e78f6f7d9a594267c6feda3276611bf3539289b8a3de3295f700a",
+ "compiledSqlBytes": 2510560,
+ "compiledSqlSha256": "285d0c859117006646389dbd2a22de9aef1c0b97bc796a9e4d4448a8a232b093",
+ "applyCompiledSqlSha256": "91e378c58d58d7d7f818464c9c92aff02c7b01ca528e9db858e2b1d2d00bd92f",
+ "applyExpiryFaultedSqlSha256": "727ff0f6be2dbf8ee06551d4efd6ebe665f7187189f7a0a1159d5281ae90635b",
+ "results": {
+ "rollbackStageCount": 12,
+ "rollbackExact": true,
+ "forcedErrorRejected": true,
+ "forcedErrorRollbackExact": true,
+ "lostResponseMutationAttempts": 1,
+ "lostResponseRestorationExact": true,
+ "contendedExpiryRejected": true,
+ "contendedExpiryRollbackExact": true,
+ "applyFinalGuardDeadlineFaultInjected": true,
+ "applyTerminalReadbackObserved": true,
+ "applyExpiryRejectedBeforeCommit": true,
+ "applyExpiryMutationAttempts": 1,
+ "applyExpiryRollbackExact": true
+ },
+ "hostedWrites": false,
+ "operatingRowsRead": false,
+ "rawSqlInProof": false,
+ "sourceMutation": false,
+ "temporaryContainerRemoved": true
+}
diff --git a/apps/web/performance/review-db-rehearsal-20261012/pg17-rollback-rehearsal-current-20261013.json b/apps/web/performance/review-db-rehearsal-20261012/pg17-rollback-rehearsal-current-20261013.json
new file mode 100644
index 0000000000..2eeda57552
--- /dev/null
+++ b/apps/web/performance/review-db-rehearsal-20261012/pg17-rollback-rehearsal-current-20261013.json
@@ -0,0 +1,65 @@
+{
+ "schemaVersion": 1,
+ "status": "passed",
+ "environment": {
+ "engine": "17.6",
+ "image": "supabase/postgres@sha256:fbe6c858a2ea9616aead3c39e132afcc0660decc5cb7dc656208ed145f5331ab",
+ "emptyRowChecks": [
+ "public.profiles",
+ "public.reviews"
+ ]
+ },
+ "manifestSha256": "4c4d8bd20dfee1b20706670a63acf66eacb8286999119b72690489a1a2202c8c",
+ "manifestToolchainRoot": "a95a588b05726a1c57739ea570f93bf3602346c817f73f0c648d628455d5d0db",
+ "rehearsalHarnessSha256": "8088aa57f39045e2bf93cba084711119395098d053318b70aaa676b5c13fea26",
+ "sourceRoot": "61f9d7637c0714a7b0d9108c6f7959ea820175a7870dba681498b570296a1544",
+ "fixtureDumpSha256": "b7777306a43cc84408434e9835973604b2eb520cf9e9e8d0ee4bba6dcb41257f",
+ "predecessorSources": [
+ {
+ "path": "backend/supabase/migrations/20261004190259_admin_record_guarded_actions.sql",
+ "sha256": "b373b7ea472c0352a33a4d4043cf8d6aa8474c04cc1ca5778805edfa77ac9c95"
+ },
+ {
+ "path": "backend/supabase/migrations/20261004192657_admin_evaluation_raw_warning_groups.sql",
+ "sha256": "66eace1d6fb0dd55c1d780776bab855cc37690335ad40b0fdc1294c610e07d3a"
+ },
+ {
+ "path": "backend/supabase/migrations/20261004194715_admin_evaluation_raw_warning_invoker_contract.sql",
+ "sha256": "e1c105df82c4f814d3e6adad807ff9d072b8cd42ae770b4b78f75b89a9387020"
+ },
+ {
+ "path": "backend/supabase/migrations/20261009022915_restaurant_review_manual_preview_eligibility.sql",
+ "sha256": "8acf6d1428764260ed57dac5fe09711868a82896f0a6d631d502128004c168a3"
+ },
+ {
+ "path": "backend/supabase/migrations/20261009091342_admin_record_private_verification_cleanup.sql",
+ "sha256": "b596b200e52c6813a4cfa1b0a2818625f067864549e8854f3497afdcdab706da"
+ },
+ {
+ "path": "backend/supabase/migrations/20261009101645_admin_user_management_rpc_forward.sql",
+ "sha256": "2067538f89c9f90d28e784672c7a1288306ba22d5ae92b087c8692503da9b1ae"
+ }
+ ],
+ "migrationCount": 11,
+ "statementCount": 112,
+ "baselineStateSha256": "3802888324e1651ad7b2f71e7cf15fac78ab77aa57637d0c60431f8da67e6298",
+ "stageStatesSha256": "bd25ecbd82759ca75e994ca0f1da38b49acc848d5dd556e702e4e43024edaa65",
+ "terminalStateSha256": "9cf59a5d983e78f6f7d9a594267c6feda3276611bf3539289b8a3de3295f700a",
+ "compiledSqlBytes": 2510560,
+ "compiledSqlSha256": "285d0c859117006646389dbd2a22de9aef1c0b97bc796a9e4d4448a8a232b093",
+ "results": {
+ "rollbackStageCount": 12,
+ "rollbackExact": true,
+ "forcedErrorRejected": true,
+ "forcedErrorRollbackExact": true,
+ "lostResponseMutationAttempts": 1,
+ "lostResponseRestorationExact": true,
+ "contendedExpiryRejected": true,
+ "contendedExpiryRollbackExact": true
+ },
+ "hostedWrites": false,
+ "operatingRowsRead": false,
+ "rawSqlInProof": false,
+ "sourceMutation": false,
+ "temporaryContainerRemoved": true
+}
diff --git a/apps/web/performance/review-db-rehearsal-20261012/pg17-rollback-rehearsal-final-20261013.json b/apps/web/performance/review-db-rehearsal-20261012/pg17-rollback-rehearsal-final-20261013.json
new file mode 100644
index 0000000000..7fc1ac6b3b
--- /dev/null
+++ b/apps/web/performance/review-db-rehearsal-20261012/pg17-rollback-rehearsal-final-20261013.json
@@ -0,0 +1,65 @@
+{
+ "schemaVersion": 1,
+ "status": "passed",
+ "environment": {
+ "engine": "17.6",
+ "image": "supabase/postgres@sha256:fbe6c858a2ea9616aead3c39e132afcc0660decc5cb7dc656208ed145f5331ab",
+ "emptyRowChecks": [
+ "public.profiles",
+ "public.reviews"
+ ]
+ },
+ "manifestSha256": "8297b18793513956118c720e3aa5bd481d0769645c263f680a5272825b70569e",
+ "manifestToolchainRoot": "91a7166b76b0f867b4e74dd0e2258d1f22457c75a61e55b161148378f08ac739",
+ "rehearsalHarnessSha256": "8088aa57f39045e2bf93cba084711119395098d053318b70aaa676b5c13fea26",
+ "sourceRoot": "61f9d7637c0714a7b0d9108c6f7959ea820175a7870dba681498b570296a1544",
+ "fixtureDumpSha256": "b7777306a43cc84408434e9835973604b2eb520cf9e9e8d0ee4bba6dcb41257f",
+ "predecessorSources": [
+ {
+ "path": "backend/supabase/migrations/20261004190259_admin_record_guarded_actions.sql",
+ "sha256": "b373b7ea472c0352a33a4d4043cf8d6aa8474c04cc1ca5778805edfa77ac9c95"
+ },
+ {
+ "path": "backend/supabase/migrations/20261004192657_admin_evaluation_raw_warning_groups.sql",
+ "sha256": "66eace1d6fb0dd55c1d780776bab855cc37690335ad40b0fdc1294c610e07d3a"
+ },
+ {
+ "path": "backend/supabase/migrations/20261004194715_admin_evaluation_raw_warning_invoker_contract.sql",
+ "sha256": "e1c105df82c4f814d3e6adad807ff9d072b8cd42ae770b4b78f75b89a9387020"
+ },
+ {
+ "path": "backend/supabase/migrations/20261009022915_restaurant_review_manual_preview_eligibility.sql",
+ "sha256": "8acf6d1428764260ed57dac5fe09711868a82896f0a6d631d502128004c168a3"
+ },
+ {
+ "path": "backend/supabase/migrations/20261009091342_admin_record_private_verification_cleanup.sql",
+ "sha256": "b596b200e52c6813a4cfa1b0a2818625f067864549e8854f3497afdcdab706da"
+ },
+ {
+ "path": "backend/supabase/migrations/20261009101645_admin_user_management_rpc_forward.sql",
+ "sha256": "2067538f89c9f90d28e784672c7a1288306ba22d5ae92b087c8692503da9b1ae"
+ }
+ ],
+ "migrationCount": 11,
+ "statementCount": 112,
+ "baselineStateSha256": "3802888324e1651ad7b2f71e7cf15fac78ab77aa57637d0c60431f8da67e6298",
+ "stageStatesSha256": "bd25ecbd82759ca75e994ca0f1da38b49acc848d5dd556e702e4e43024edaa65",
+ "terminalStateSha256": "9cf59a5d983e78f6f7d9a594267c6feda3276611bf3539289b8a3de3295f700a",
+ "compiledSqlBytes": 2510560,
+ "compiledSqlSha256": "285d0c859117006646389dbd2a22de9aef1c0b97bc796a9e4d4448a8a232b093",
+ "results": {
+ "rollbackStageCount": 12,
+ "rollbackExact": true,
+ "forcedErrorRejected": true,
+ "forcedErrorRollbackExact": true,
+ "lostResponseMutationAttempts": 1,
+ "lostResponseRestorationExact": true,
+ "contendedExpiryRejected": true,
+ "contendedExpiryRollbackExact": true
+ },
+ "hostedWrites": false,
+ "operatingRowsRead": false,
+ "rawSqlInProof": false,
+ "sourceMutation": false,
+ "temporaryContainerRemoved": true
+}
diff --git a/apps/web/performance/review-db-rehearsal-20261012/pg17-rollback-rehearsal.json b/apps/web/performance/review-db-rehearsal-20261012/pg17-rollback-rehearsal.json
new file mode 100644
index 0000000000..d14f6d473a
--- /dev/null
+++ b/apps/web/performance/review-db-rehearsal-20261012/pg17-rollback-rehearsal.json
@@ -0,0 +1,62 @@
+{
+ "schemaVersion": 1,
+ "status": "passed",
+ "environment": {
+ "engine": "17.6",
+ "image": "supabase/postgres@sha256:fbe6c858a2ea9616aead3c39e132afcc0660decc5cb7dc656208ed145f5331ab",
+ "personalRowsEmpty": true
+ },
+ "manifestSha256": "f6cf33c2f5f5fdc8f5a5361f1ffc54a2c2cb70aa752da4815e4f614dffba3410",
+ "manifestToolchainRoot": "a173ad52d4612b5f08e0b0dce6acaa1dadd3a15996e6854dca196bda00c5c65b",
+ "rehearsalHarnessSha256": "29622de79b7ebc63080ef3edb4c7f9f8ba61e14efa5600fcb589791a53323356",
+ "sourceRoot": "61f9d7637c0714a7b0d9108c6f7959ea820175a7870dba681498b570296a1544",
+ "fixtureDumpSha256": "b7777306a43cc84408434e9835973604b2eb520cf9e9e8d0ee4bba6dcb41257f",
+ "predecessorSources": [
+ {
+ "path": "backend/supabase/migrations/20261004190259_admin_record_guarded_actions.sql",
+ "sha256": "b373b7ea472c0352a33a4d4043cf8d6aa8474c04cc1ca5778805edfa77ac9c95"
+ },
+ {
+ "path": "backend/supabase/migrations/20261004192657_admin_evaluation_raw_warning_groups.sql",
+ "sha256": "66eace1d6fb0dd55c1d780776bab855cc37690335ad40b0fdc1294c610e07d3a"
+ },
+ {
+ "path": "backend/supabase/migrations/20261004194715_admin_evaluation_raw_warning_invoker_contract.sql",
+ "sha256": "e1c105df82c4f814d3e6adad807ff9d072b8cd42ae770b4b78f75b89a9387020"
+ },
+ {
+ "path": "backend/supabase/migrations/20261009022915_restaurant_review_manual_preview_eligibility.sql",
+ "sha256": "8acf6d1428764260ed57dac5fe09711868a82896f0a6d631d502128004c168a3"
+ },
+ {
+ "path": "backend/supabase/migrations/20261009091342_admin_record_private_verification_cleanup.sql",
+ "sha256": "b596b200e52c6813a4cfa1b0a2818625f067864549e8854f3497afdcdab706da"
+ },
+ {
+ "path": "backend/supabase/migrations/20261009101645_admin_user_management_rpc_forward.sql",
+ "sha256": "2067538f89c9f90d28e784672c7a1288306ba22d5ae92b087c8692503da9b1ae"
+ }
+ ],
+ "migrationCount": 11,
+ "statementCount": 112,
+ "baselineStateSha256": "3802888324e1651ad7b2f71e7cf15fac78ab77aa57637d0c60431f8da67e6298",
+ "stageStatesSha256": "bd25ecbd82759ca75e994ca0f1da38b49acc848d5dd556e702e4e43024edaa65",
+ "terminalStateSha256": "9cf59a5d983e78f6f7d9a594267c6feda3276611bf3539289b8a3de3295f700a",
+ "compiledSqlBytes": 2510560,
+ "compiledSqlSha256": "285d0c859117006646389dbd2a22de9aef1c0b97bc796a9e4d4448a8a232b093",
+ "results": {
+ "rollbackStageCount": 12,
+ "rollbackExact": true,
+ "forcedErrorRejected": true,
+ "forcedErrorRollbackExact": true,
+ "lostResponseMutationAttempts": 1,
+ "lostResponseRestorationExact": true,
+ "contendedExpiryRejected": true,
+ "contendedExpiryRollbackExact": true
+ },
+ "hostedWrites": false,
+ "operatingRowsRead": false,
+ "rawSqlInProof": false,
+ "sourceMutation": false,
+ "temporaryContainerRemoved": true
+}
diff --git a/apps/web/performance/review-db-rehearsal-20261012/pg17-terminal-boundaries-20261013.json b/apps/web/performance/review-db-rehearsal-20261012/pg17-terminal-boundaries-20261013.json
new file mode 100644
index 0000000000..d09caf27bd
--- /dev/null
+++ b/apps/web/performance/review-db-rehearsal-20261012/pg17-terminal-boundaries-20261013.json
@@ -0,0 +1,65 @@
+{
+ "schemaVersion": 1,
+ "status": "passed",
+ "environment": {
+ "engine": "17.6",
+ "image": "supabase/postgres@sha256:fbe6c858a2ea9616aead3c39e132afcc0660decc5cb7dc656208ed145f5331ab",
+ "emptyRowChecks": [
+ "public.profiles",
+ "public.reviews"
+ ]
+ },
+ "manifestSha256": "ae764bb760102a6d7d066be669c85248feed1823eafded4bf8c246a062090a8c",
+ "manifestToolchainRoot": "a0eeeb623c80b8a5e416b84397bdb07e4bd65c670d98b70707cc816d37e9a18b",
+ "rehearsalHarnessSha256": "8088aa57f39045e2bf93cba084711119395098d053318b70aaa676b5c13fea26",
+ "sourceRoot": "61f9d7637c0714a7b0d9108c6f7959ea820175a7870dba681498b570296a1544",
+ "fixtureDumpSha256": "b7777306a43cc84408434e9835973604b2eb520cf9e9e8d0ee4bba6dcb41257f",
+ "predecessorSources": [
+ {
+ "path": "backend/supabase/migrations/20261004190259_admin_record_guarded_actions.sql",
+ "sha256": "b373b7ea472c0352a33a4d4043cf8d6aa8474c04cc1ca5778805edfa77ac9c95"
+ },
+ {
+ "path": "backend/supabase/migrations/20261004192657_admin_evaluation_raw_warning_groups.sql",
+ "sha256": "66eace1d6fb0dd55c1d780776bab855cc37690335ad40b0fdc1294c610e07d3a"
+ },
+ {
+ "path": "backend/supabase/migrations/20261004194715_admin_evaluation_raw_warning_invoker_contract.sql",
+ "sha256": "e1c105df82c4f814d3e6adad807ff9d072b8cd42ae770b4b78f75b89a9387020"
+ },
+ {
+ "path": "backend/supabase/migrations/20261009022915_restaurant_review_manual_preview_eligibility.sql",
+ "sha256": "8acf6d1428764260ed57dac5fe09711868a82896f0a6d631d502128004c168a3"
+ },
+ {
+ "path": "backend/supabase/migrations/20261009091342_admin_record_private_verification_cleanup.sql",
+ "sha256": "b596b200e52c6813a4cfa1b0a2818625f067864549e8854f3497afdcdab706da"
+ },
+ {
+ "path": "backend/supabase/migrations/20261009101645_admin_user_management_rpc_forward.sql",
+ "sha256": "2067538f89c9f90d28e784672c7a1288306ba22d5ae92b087c8692503da9b1ae"
+ }
+ ],
+ "migrationCount": 11,
+ "statementCount": 112,
+ "baselineStateSha256": "3802888324e1651ad7b2f71e7cf15fac78ab77aa57637d0c60431f8da67e6298",
+ "stageStatesSha256": "bd25ecbd82759ca75e994ca0f1da38b49acc848d5dd556e702e4e43024edaa65",
+ "terminalStateSha256": "9cf59a5d983e78f6f7d9a594267c6feda3276611bf3539289b8a3de3295f700a",
+ "compiledSqlBytes": 2510560,
+ "compiledSqlSha256": "285d0c859117006646389dbd2a22de9aef1c0b97bc796a9e4d4448a8a232b093",
+ "results": {
+ "rollbackStageCount": 12,
+ "rollbackExact": true,
+ "forcedErrorRejected": true,
+ "forcedErrorRollbackExact": true,
+ "lostResponseMutationAttempts": 1,
+ "lostResponseRestorationExact": true,
+ "contendedExpiryRejected": true,
+ "contendedExpiryRollbackExact": true
+ },
+ "hostedWrites": false,
+ "operatingRowsRead": false,
+ "rawSqlInProof": false,
+ "sourceMutation": false,
+ "temporaryContainerRemoved": true
+}
diff --git a/apps/web/performance/review-db-rehearsal-20261012/retained-current-proof-0833ed81-historical.json b/apps/web/performance/review-db-rehearsal-20261012/retained-current-proof-0833ed81-historical.json
new file mode 100644
index 0000000000..66de073ccf
--- /dev/null
+++ b/apps/web/performance/review-db-rehearsal-20261012/retained-current-proof-0833ed81-historical.json
@@ -0,0 +1,6 @@
+{
+ "schemaVersion": 1,
+ "kind": "review-media-sql-retained-current-proof",
+ "proofPath": "pg17-terminal-boundaries-20261013.json",
+ "proofSha256": "0cfc9c10ff7b70c606f6eeef33fff8dbd14636dc15b7c963925097454ee454a2"
+}
diff --git a/apps/web/performance/review-db-rehearsal-20261012/retained-current-proof.json b/apps/web/performance/review-db-rehearsal-20261012/retained-current-proof.json
new file mode 100644
index 0000000000..9f1af55bac
--- /dev/null
+++ b/apps/web/performance/review-db-rehearsal-20261012/retained-current-proof.json
@@ -0,0 +1,6 @@
+{
+ "schemaVersion": 1,
+ "kind": "review-media-sql-retained-current-proof",
+ "proofPath": "pg17-review-fixes-20261014-v1.json",
+ "proofSha256": "23fb9b471a06dbd36aafab3a678152ca897893b3cdc968f2d623035d217b1e38"
+}
diff --git a/apps/web/performance/review-db-rehearsal-20261012/retained-current-proof.json-4ba9c6d2-historical b/apps/web/performance/review-db-rehearsal-20261012/retained-current-proof.json-4ba9c6d2-historical
new file mode 100644
index 0000000000..dc2a656581
--- /dev/null
+++ b/apps/web/performance/review-db-rehearsal-20261012/retained-current-proof.json-4ba9c6d2-historical
@@ -0,0 +1,6 @@
+{
+ "schemaVersion": 1,
+ "kind": "review-media-sql-retained-current-proof",
+ "proofPath": "pg17-reviewed-boundaries-20261013.json",
+ "proofSha256": "9c73b47c0841f2585d7ceee77423d27410a488e61080004cf227d4329dd88076"
+}
diff --git a/apps/web/performance/review-db-rehearsal-20261012/review-fix-index-units-20261014-v1.log b/apps/web/performance/review-db-rehearsal-20261012/review-fix-index-units-20261014-v1.log
new file mode 100644
index 0000000000..79f44e6288
--- /dev/null
+++ b/apps/web/performance/review-db-rehearsal-20261012/review-fix-index-units-20261014-v1.log
@@ -0,0 +1,44 @@
+bun test v1.4.0 (34cbb9a40)
+
+apps/web/tests-unit/review-media-sql-successor-harness.test.ts:
+(pass) fixture observes delayed advisory contention before expiry without the old 700ms cutoff [0.12ms]
+(pass) fixture rejects contention returned by a slow observation after expiry [0.09ms]
+(pass) successor acknowledgement loss reconciles a committed terminal state without SQL replay [0.18ms]
+(pass) successor failure at the baseline is classified without diagnostic SQL replay [0.05ms]
+(pass) retained proof is checksum-verified and distinguished from the current harness source [1.16ms]
+(pass) retained proof with bytes not authorized by SHA256SUMS is rejected [1476.90ms]
+(pass) jointly altered retained proof and checksum map are rejected by the independent digest [2.89ms]
+(pass) owned database is cleaned after predecessor SQL fails [0.75ms]
+(pass) owned database is cleaned after fixture SQL fails [0.12ms]
+(pass) pre-existing database is never acquired or dropped [0.08ms]
+(pass) database CREATE acknowledgement loss is reconciled without replay and cleaned by exact ownership [0.23ms]
+(pass) cleanup re-reads and removes the exact owned database after the first CREATE readback fails [0.09ms]
+(pass) cleanup refuses a database whose exact owner metadata does not match [0.18ms]
+(pass) fixture role creation acknowledgement loss is reconciled and exact owned roles are cleaned [0.60ms]
+(pass) cleanup re-reads exact owned roles when the first post-create readback fails [0.20ms]
+(pass) fixture role creation failure without owned readback never authorizes cleanup [0.11ms]
+(pass) pre-existing fixture role collision is preserved and never acquired for cleanup [0.10ms]
+(pass) uncertain create collision with another owner is not guessed or removed [0.09ms]
+(pass) cleanup refuses roles whose ownership marker changed after creation [0.08ms]
+(pass) cleanup refuses to remove owned roles after an out-of-scope membership is added [0.06ms]
+
+apps/web/tests-unit/review-media-sql-rehearsal-verifier.test.ts:
+(pass) default retained proof uses a stable current-proof selector [2.44ms]
+(pass) selector rejects unauthenticated bytes, path escape, and proof-SHA relabelling [3523.67ms]
+(pass) retained proof verifies the complete non-circular artifact map from one byte snapshot [1.82ms]
+(pass) retained rollback proof rejects bytes not authorized by SHA256SUMS [0.76ms]
+(pass) explicit current output is exclusive and separate from retained proof [0.10ms]
+(pass) apply expiry fault changes only the final wrapper guard after the exported marker [0.10ms]
+(pass) container create acknowledgement loss is reconciled by unique name and owner label [0.23ms]
+(pass) first inspect failure after uncertain create is reconciled and cleaned by exact owned ID [0.09ms]
+(pass) same-process rehearsal runs derive distinct container names from their owner tokens [0.03ms]
+(pass) create failure reconciles proven absence without issuing a removal [0.03ms]
+(pass) owner-label mismatch is rejected and the unowned container is not removed [0.10ms]
+(pass) acknowledgement mismatch retains exact owned ID for safe finally cleanup [0.10ms]
+(pass) cleanup failure leaves ownership live and cannot be misreported as removed [0.05ms]
+(pass) cleanup acknowledgement loss succeeds only after exact-ID absence readback [0.08ms]
+
+ 34 pass
+ 0 fail
+ 153 expect() calls
+Ran 34 tests across 2 files. [5.12s]
diff --git a/apps/web/performance/review-db-rehearsal-20261012/reviewed-boundaries-unit-verification-20261013.json b/apps/web/performance/review-db-rehearsal-20261012/reviewed-boundaries-unit-verification-20261013.json
new file mode 100644
index 0000000000..675d3826f6
--- /dev/null
+++ b/apps/web/performance/review-db-rehearsal-20261012/reviewed-boundaries-unit-verification-20261013.json
@@ -0,0 +1,89 @@
+{
+ "schemaVersion": 1,
+ "kind": "local-reviewed-successor-unit-verification",
+ "recordedAtUTC": "2026-10-13T08:32:39.695495+00:00",
+ "environment": {
+ "node": "24.21.0",
+ "bun": "1.4.0",
+ "platform": "macOS"
+ },
+ "finalAffectedModules": 6,
+ "finalUniqueTestsPassed": 94,
+ "finalUniqueAssertions": 518,
+ "unresolvedFailures": 0,
+ "verificationRuns": [
+ {
+ "modules": 5,
+ "passed": 80,
+ "failed": 0,
+ "assertions": 463
+ },
+ {
+ "modules": 6,
+ "passed": 93,
+ "failed": 1,
+ "assertions": 514,
+ "failure": "selector unit assertion still named prior immutable proof; corrected to verify authenticated selection independently of current filename"
+ },
+ {
+ "modules": 1,
+ "passed": 14,
+ "failed": 0,
+ "assertions": 55,
+ "afterCorrection": true
+ }
+ ],
+ "sourceBindings": [
+ {
+ "path": ".github/review-media-sql-successor.v1.json",
+ "sha256": "23bf7e7d7e9e5d7962021dd3ec860e87b47d07e119ba0e39de2e423a0a601f4e"
+ },
+ {
+ "path": "apps/web/scripts/review-media-sql-successor.mjs",
+ "sha256": "5d61808b45661d412937fbfdfd3bfb75b1cf92c7c285e4d8ad061a2f667c3c0b"
+ },
+ {
+ "path": "apps/web/scripts/review-media-operating-admission.mjs",
+ "sha256": "586847d5030cedc6575328cbe78b5eb9f1d9ff47d91e8d8f1aef40076b6d2277"
+ },
+ {
+ "path": "apps/web/performance/review-db-rehearsal-20261012/verify-review-media-sql-rehearsal.mjs",
+ "sha256": "f7321f607a5bf3a24ba099d51abc67254db52fcc385945692426d1b2dfa6e431"
+ },
+ {
+ "path": "apps/web/performance/review-db-successor-20261012/verify-review-media-sql-successor.mjs",
+ "sha256": "768b8887a30c2b8f496a73a21d373b85a0b64fadaed3a1485034a729ee704d39"
+ },
+ {
+ "path": "apps/web/performance/review-db-successor-20261012/verify-fixture-ownership.mjs",
+ "sha256": "41972cf6c4ac2b819b42cf9f06b762d54fb9d161f4594192492fccf79cca7418"
+ },
+ {
+ "path": "apps/web/tests-unit/review-media-sql-successor.test.ts",
+ "sha256": "b9fbfa0fdc249e3c495043679c39a8a98cd14f8de6a225f20d926d83702fd196"
+ },
+ {
+ "path": "apps/web/tests-unit/review-media-migration-bundle.test.ts",
+ "sha256": "dc1f2594e629d5af16541db93e2e51f9b98d8282dd7fbeb802dd240fa6f522c0"
+ },
+ {
+ "path": "apps/web/tests-unit/review-media-sql-successor-harness.test.ts",
+ "sha256": "983a62a3f81973e18914835b2d483a7a05fef3fa9f7d129ffd7a60ed5cd4e551"
+ },
+ {
+ "path": "apps/web/tests-unit/review-media-sql-rehearsal-verifier.test.ts",
+ "sha256": "54a9560b77a3afb9eb70631224ba4f481caf82eb8b5d2cdc1a076d8831c1ec7e"
+ },
+ {
+ "path": "apps/web/tests-unit/review-media-operating-admission.test.ts",
+ "sha256": "184368546c3668da99449f3d7ec0f08b95e1c06f44ca85100a6d2dfa533bc84a"
+ },
+ {
+ "path": "apps/web/tests-unit/review-media-fixture-ownership-verifier.test.ts",
+ "sha256": "92651102154fd4d573ca841137bd31c319ef545b2527e7da17398bd2e6b97d47"
+ }
+ ],
+ "hostedWrites": false,
+ "providerCalls": 0,
+ "performanceClaim": false
+}
diff --git a/apps/web/performance/review-db-rehearsal-20261012/scanner-readback.json b/apps/web/performance/review-db-rehearsal-20261012/scanner-readback.json
new file mode 100644
index 0000000000..bf73e67768
--- /dev/null
+++ b/apps/web/performance/review-db-rehearsal-20261012/scanner-readback.json
@@ -0,0 +1,33 @@
+{
+ "schema": "tzudong-gitguardian-single-incident-readback/v1",
+ "recordedAtUtc": "2026-10-12T13:42:52.816277+00:00",
+ "repository": "twoimo/tzudong",
+ "pullRequest": 3196,
+ "sourceRevision": "f4739a77822ce218595db67ebe0664eb10922688",
+ "incidentId": 38128430,
+ "occurrenceId": 303564026,
+ "incidentCount": 1,
+ "classification": "test credential",
+ "action": "Skip: test cred",
+ "actionRequests": 2,
+ "reason": "The same single incident was present in the original check and an already-existing rerun. No new workflow or check rerun was requested by this task.",
+ "apiReadback": [
+ {
+ "id": 114780500942,
+ "status": "completed",
+ "conclusion": "skipped",
+ "title": "You tagged this GitGuardian alert as a test credential"
+ },
+ {
+ "id": 114833294224,
+ "status": "completed",
+ "conclusion": "skipped",
+ "title": "You tagged this GitGuardian alert as a test credential"
+ }
+ ],
+ "incidentStatusShown": "Triggered",
+ "incidentResolvedClaimed": false,
+ "scannerSettingsChanged": false,
+ "otherIncidentsChanged": false,
+ "currentSourcePasswordFixtureRemoved": true
+}
diff --git a/apps/web/performance/review-db-rehearsal-20261012/terminal-boundary-unit-verification-20261013.json b/apps/web/performance/review-db-rehearsal-20261012/terminal-boundary-unit-verification-20261013.json
new file mode 100644
index 0000000000..5d077cf1eb
--- /dev/null
+++ b/apps/web/performance/review-db-rehearsal-20261012/terminal-boundary-unit-verification-20261013.json
@@ -0,0 +1,67 @@
+{
+ "schemaVersion": 1,
+ "kind": "local-branch-verification",
+ "recordedAtUTC": "2026-10-13T07:10:46.250689+00:00",
+ "environment": {
+ "node": "24.21.0",
+ "bun": "1.4.0",
+ "platform": "macOS"
+ },
+ "tests": {
+ "passed": 81,
+ "failed": 0,
+ "assertions": 450,
+ "files": 5
+ },
+ "sources": [
+ {
+ "path": "apps/web/scripts/review-media-sql-successor.mjs",
+ "sha256": "dcf552697251c2f10d859c7a2f56937a0f6e92ee9467e5b919807e498eff1d49"
+ },
+ {
+ "path": "apps/web/scripts/review-media-operating-admission.mjs",
+ "sha256": "4ad55ed526b7af8c0ee1282057dbfa3706249deea04472a2749909af45492064"
+ },
+ {
+ "path": "apps/web/performance/review-db-successor-20261012/verify-review-media-sql-successor.mjs",
+ "sha256": "8c3d7df52c281a9df1d6a4bd2acefb59f9a23b60185559fc6a8298cd65fde370"
+ },
+ {
+ "path": ".github/review-media-sql-successor.v1.json",
+ "sha256": "ae764bb760102a6d7d066be669c85248feed1823eafded4bf8c246a062090a8c"
+ },
+ {
+ "path": "apps/web/tests-unit/review-media-sql-successor.test.ts",
+ "sha256": "7d32dccb70c6b1b79379ee69864d2c092d3a2e39863ab365494622a0effeba62"
+ },
+ {
+ "path": "apps/web/tests-unit/review-media-migration-bundle.test.ts",
+ "sha256": "dc1f2594e629d5af16541db93e2e51f9b98d8282dd7fbeb802dd240fa6f522c0"
+ },
+ {
+ "path": "apps/web/tests-unit/review-media-sql-successor-harness.test.ts",
+ "sha256": "31bd78568b7bff5196626d87cf7f0fe9b84d695da8cef597fa93e4d742f99845"
+ },
+ {
+ "path": "apps/web/tests-unit/review-media-sql-rehearsal-verifier.test.ts",
+ "sha256": "b25b5dcc6516435a1c74a038c20856cc5dd719b303d9c3f3e600dc314197c7cd"
+ },
+ {
+ "path": "apps/web/tests-unit/review-media-operating-admission.test.ts",
+ "sha256": "26d7fe68effda2813b3f9384dd2d7e4f9e8998447cd988b89820d93be7f6bdc7"
+ }
+ ],
+ "scope": [
+ "rehearsal filesystem expiry",
+ "committed outcome plus terminal audit failure",
+ "private admission formatting",
+ "database/role creation and cleanup acknowledgement loss",
+ "rollback verifier ownership"
+ ],
+ "performanceClaim": false,
+ "hostedClaim": false,
+ "confidenceInterval": {
+ "applicable": false,
+ "reason": "Deterministic branch checks; not a reliability or performance sample."
+ }
+}
diff --git a/apps/web/performance/review-db-rehearsal-20261012/verify-review-media-sql-rehearsal.mjs b/apps/web/performance/review-db-rehearsal-20261012/verify-review-media-sql-rehearsal.mjs
new file mode 100644
index 0000000000..d35f1e6de9
--- /dev/null
+++ b/apps/web/performance/review-db-rehearsal-20261012/verify-review-media-sql-rehearsal.mjs
@@ -0,0 +1,715 @@
+#!/usr/bin/env node
+
+/**
+ * Isolated PostgreSQL 17.6 replay for the rollback-only successor controller.
+ *
+ * A disposable container is populated from the empty-row ranking_clone
+ * schema fixture and its non-personal contract seed rows. No hosted endpoint
+ * is contacted. Every scenario verifies the same restored state 0 in a
+ * database named `postgres`, matching the operating database contract.
+ */
+import { createHash, randomBytes } from 'node:crypto';
+import { existsSync, readFileSync, writeFileSync } from 'node:fs';
+import { dirname, relative, resolve } from 'node:path';
+import { fileURLToPath } from 'node:url';
+import { spawn, spawnSync } from 'node:child_process';
+
+import {
+ buildSuccessorStateQuery,
+ compileSuccessorPlan,
+ compileSuccessorRehearsalPlan,
+ loadSuccessorManifest,
+ SUCCESSOR_PRE_COMMIT_EXPIRY_MARKER,
+ validateSuccessorStageStates,
+} from '../../scripts/review-media-sql-successor.mjs';
+
+const HERE = dirname(fileURLToPath(import.meta.url));
+const REPOSITORY_ROOT = resolve(HERE, '../../../..');
+export const DEFAULT_RETAINED_PROOF_SELECTOR_PATH = resolve(HERE, 'retained-current-proof.json');
+const CHECKSUMS_PATH = resolve(HERE, 'SHA256SUMS');
+const CHECKSUMS_DIGEST_PATH = resolve(HERE, 'SHA256SUMS.sha256');
+const DOCKER_CONTEXT = process.env.TZUDONG_REHEARSAL_DOCKER_CONTEXT || 'colima-tzudong-catalog-20261007';
+const SOURCE_CONTAINER = process.env.TZUDONG_REHEARSAL_SOURCE_CONTAINER || 'tzudong-ranking-clone-20261008';
+const CONTAINER_PREFIX = 'tzudong-review-rollback-rehearsal';
+const OWNER_LABEL = 'tzudong.review-rehearsal.owner';
+const IMAGE = 'supabase/postgres@sha256:fbe6c858a2ea9616aead3c39e132afcc0660decc5cb7dc656208ed145f5331ab';
+const BOOTSTRAP_PASSWORD = randomBytes(24).toString('hex');
+const ADVISORY_LOCK_KEY = '7311754282260211';
+const PREDECESSOR_PATHS = Object.freeze([
+ 'backend/supabase/migrations/20261004190259_admin_record_guarded_actions.sql',
+ 'backend/supabase/migrations/20261004192657_admin_evaluation_raw_warning_groups.sql',
+ 'backend/supabase/migrations/20261004194715_admin_evaluation_raw_warning_invoker_contract.sql',
+ 'backend/supabase/migrations/20261009022915_restaurant_review_manual_preview_eligibility.sql',
+ 'backend/supabase/migrations/20261009091342_admin_record_private_verification_cleanup.sql',
+ 'backend/supabase/migrations/20261009101645_admin_user_management_rpc_forward.sql',
+]);
+const REQUIRED_ROLES = Object.freeze([
+ 'cli_login_postgres',
+ 'privacy_auth_bridge',
+ 'privacy_retention_activation_operator',
+ 'privacy_retention_legal_approver',
+ 'privacy_retention_operator_approver',
+ 'privacy_workflow_owner',
+ 'supabase_privileged_role',
+ 'supabase_realtime_admin',
+ 'tzudong_g037_readonly',
+]);
+const sha256 = value => createHash('sha256').update(value).digest('hex');
+const quote = value => `'${String(value).replaceAll("'", "''")}'`;
+const canonical = value => Array.isArray(value)
+ ? value.map(canonical)
+ : value && typeof value === 'object'
+ ? Object.fromEntries(Object.keys(value).sort().map(key => [key, canonical(value[key])]))
+ : value;
+const equal = (left, right) => JSON.stringify(canonical(left)) === JSON.stringify(canonical(right));
+
+function fail(code) {
+ const error = new Error(code);
+ error.code = code;
+ throw error;
+}
+
+function currentBindings(record, manifestBytes, harnessBytes) {
+ return Object.freeze({
+ manifestSha256: sha256(manifestBytes),
+ rehearsalHarnessSha256: sha256(harnessBytes),
+ sourceRoot: record.manifest.sourceRoot,
+ });
+}
+
+function checksumEntries(checksumsBytes) {
+ const entries = checksumsBytes.toString('utf8').split(/\r?\n/).filter(Boolean).map(line => {
+ const match = /^([0-9a-f]{64}) (.+)$/.exec(line);
+ if (!match) fail('LOCAL_REHEARSAL_CHECKSUMS_INVALID');
+ return { sha256: match[1], path: match[2] };
+ });
+ if (new Set(entries.map(entry => entry.path)).size !== entries.length) {
+ fail('LOCAL_REHEARSAL_CHECKSUMS_DUPLICATE_PATH');
+ }
+ return entries;
+}
+
+function checksumEntry(entries, repositoryPath, missingCode = 'LOCAL_REHEARSAL_RETAINED_CHECKSUM_MISSING') {
+ const matches = entries.filter(entry => entry.path === repositoryPath);
+ if (matches.length !== 1) fail(missingCode);
+ return matches[0];
+}
+
+function repositoryPathFor(repositoryRoot, absolutePath, invalidCode) {
+ const repositoryPath = relative(repositoryRoot, absolutePath).replaceAll('\\', '/');
+ if (!repositoryPath || repositoryPath.startsWith('../')) fail(invalidCode);
+ return repositoryPath;
+}
+
+function parseRetainedProofSelector(bytes, { selectorPath, selectorRepositoryPath, repositoryRoot }) {
+ let selector;
+ try { selector = JSON.parse(bytes.toString('utf8')); }
+ catch { fail('LOCAL_REHEARSAL_SELECTOR_JSON_INVALID'); }
+ const keys = selector && typeof selector === 'object' && !Array.isArray(selector)
+ ? Object.keys(selector).sort()
+ : [];
+ if (!equal(keys, ['kind', 'proofPath', 'proofSha256', 'schemaVersion'])
+ || selector.schemaVersion !== 1
+ || selector.kind !== 'review-media-sql-retained-current-proof'
+ || typeof selector.proofPath !== 'string'
+ || !/^[A-Za-z0-9][A-Za-z0-9._-]*\.json$/.test(selector.proofPath)
+ || !/^[0-9a-f]{64}$/.test(selector.proofSha256)) {
+ fail('LOCAL_REHEARSAL_SELECTOR_INVALID');
+ }
+ const proofAbsolutePath = resolve(dirname(selectorPath), selector.proofPath);
+ const proofRepositoryPath = repositoryPathFor(
+ repositoryRoot,
+ proofAbsolutePath,
+ 'LOCAL_REHEARSAL_SELECTOR_PATH_INVALID',
+ );
+ if (dirname(proofRepositoryPath) !== dirname(selectorRepositoryPath)) {
+ fail('LOCAL_REHEARSAL_SELECTOR_PATH_INVALID');
+ }
+ return Object.freeze({ ...selector, proofAbsolutePath, proofRepositoryPath });
+}
+
+function readArtifactMap({
+ checksumsPath,
+ checksumsDigestPath,
+ repositoryRoot,
+ retainedRepositoryPath,
+ selectorPath,
+}) {
+ const checksumsBytes = readFileSync(checksumsPath);
+ if (checksumsDigestPath) {
+ if (!existsSync(checksumsDigestPath)) fail('LOCAL_REHEARSAL_CHECKSUMS_DIGEST_MISSING');
+ const digest = readFileSync(checksumsDigestPath, 'utf8').trim();
+ if (!/^[0-9a-f]{64}$/.test(digest)) fail('LOCAL_REHEARSAL_CHECKSUMS_DIGEST_INVALID');
+ if (sha256(checksumsBytes) !== digest) fail('LOCAL_REHEARSAL_CHECKSUMS_DIGEST_MISMATCH');
+ }
+ const entries = checksumEntries(checksumsBytes);
+ const snapshots = new Map();
+ const checksumsAbsolute = resolve(checksumsPath);
+ const digestAbsolute = checksumsDigestPath ? resolve(checksumsDigestPath) : null;
+ let selector = null;
+ let selectorRepositoryPath = null;
+ let selectedRepositoryPath = retainedRepositoryPath;
+ if (selectorPath) {
+ selectorRepositoryPath = repositoryPathFor(
+ repositoryRoot,
+ resolve(selectorPath),
+ 'LOCAL_REHEARSAL_SELECTOR_PATH_INVALID',
+ );
+ const selectorEntry = checksumEntry(
+ entries,
+ selectorRepositoryPath,
+ 'LOCAL_REHEARSAL_SELECTOR_CHECKSUM_MISSING',
+ );
+ if (!existsSync(selectorPath)) fail('LOCAL_REHEARSAL_SELECTOR_MISSING');
+ const selectorBytes = readFileSync(selectorPath);
+ if (sha256(selectorBytes) !== selectorEntry.sha256) fail('LOCAL_REHEARSAL_SELECTOR_SHA256_MISMATCH');
+ snapshots.set(selectorRepositoryPath, selectorBytes);
+ selector = parseRetainedProofSelector(selectorBytes, {
+ selectorPath: resolve(selectorPath),
+ selectorRepositoryPath,
+ repositoryRoot,
+ });
+ const proofEntry = checksumEntry(
+ entries,
+ selector.proofRepositoryPath,
+ 'LOCAL_REHEARSAL_SELECTOR_PROOF_CHECKSUM_MISSING',
+ );
+ if (proofEntry.sha256 !== selector.proofSha256) fail('LOCAL_REHEARSAL_SELECTOR_PROOF_SHA256_MISMATCH');
+ selectedRepositoryPath = selector.proofRepositoryPath;
+ }
+ const expected = checksumEntry(entries, selectedRepositoryPath);
+ for (const entry of entries) {
+ const absolutePath = resolve(repositoryRoot, entry.path);
+ const normalizedPath = relative(repositoryRoot, absolutePath).replaceAll('\\', '/');
+ if (!normalizedPath
+ || normalizedPath.startsWith('../')
+ || normalizedPath !== entry.path
+ || absolutePath === checksumsAbsolute
+ || absolutePath === digestAbsolute) {
+ fail('LOCAL_REHEARSAL_ARTIFACT_PATH_INVALID');
+ }
+ if (!existsSync(absolutePath)) {
+ fail(entry.path === selectedRepositoryPath
+ ? 'LOCAL_REHEARSAL_CURRENT_PROOF_MISSING'
+ : 'LOCAL_REHEARSAL_ARTIFACT_MISSING');
+ }
+ const bytes = snapshots.get(entry.path) ?? readFileSync(absolutePath);
+ const actualSha256 = sha256(bytes);
+ if (actualSha256 !== entry.sha256) {
+ fail(entry.path === selectedRepositoryPath
+ ? 'LOCAL_REHEARSAL_RETAINED_SHA256_MISMATCH'
+ : selectorPath && resolve(absolutePath) === resolve(selectorPath)
+ ? 'LOCAL_REHEARSAL_SELECTOR_SHA256_MISMATCH'
+ : 'LOCAL_REHEARSAL_ARTIFACT_SHA256_MISMATCH');
+ }
+ snapshots.set(entry.path, bytes);
+ }
+ return Object.freeze({
+ expected,
+ retainedRepositoryPath: selectedRepositoryPath,
+ selector: selector && Object.freeze({
+ repositoryPath: selectorRepositoryPath,
+ sha256: sha256(snapshots.get(selectorRepositoryPath)),
+ proofPath: selector.proofPath,
+ proofSha256: selector.proofSha256,
+ }),
+ snapshots,
+ });
+}
+
+export function inspectRetainedProof({
+ outputPath,
+ selectorPath = outputPath ? null : DEFAULT_RETAINED_PROOF_SELECTOR_PATH,
+ checksumsPath = CHECKSUMS_PATH,
+ checksumsDigestPath,
+ bindings,
+ repositoryRoot = REPOSITORY_ROOT,
+ required = Boolean(selectorPath),
+} = {}) {
+ if (outputPath && selectorPath) fail('LOCAL_REHEARSAL_RETAINED_SOURCE_AMBIGUOUS');
+ if (outputPath && !existsSync(outputPath)) {
+ if (required) fail('LOCAL_REHEARSAL_CURRENT_PROOF_MISSING');
+ return Object.freeze({ present: false, classification: 'missing' });
+ }
+ if (!existsSync(checksumsPath)) fail('LOCAL_REHEARSAL_CHECKSUMS_MISSING');
+ const repositoryPath = outputPath
+ ? repositoryPathFor(repositoryRoot, outputPath, 'LOCAL_REHEARSAL_RETAINED_PATH_INVALID')
+ : null;
+ const effectiveDigestPath = checksumsDigestPath === undefined && resolve(checksumsPath) === CHECKSUMS_PATH
+ ? CHECKSUMS_DIGEST_PATH
+ : checksumsDigestPath;
+ const artifactMap = readArtifactMap({
+ checksumsPath,
+ checksumsDigestPath: effectiveDigestPath,
+ repositoryRoot,
+ retainedRepositoryPath: repositoryPath,
+ selectorPath,
+ });
+ const selectedRepositoryPath = artifactMap.retainedRepositoryPath;
+ const proofBytes = artifactMap.snapshots.get(selectedRepositoryPath);
+ const expected = artifactMap.expected;
+ const actualSha256 = sha256(proofBytes);
+ let proof;
+ try { proof = JSON.parse(proofBytes.toString('utf8')); } catch { fail('LOCAL_REHEARSAL_RETAINED_JSON_INVALID'); }
+ const fields = ['manifestSha256', 'rehearsalHarnessSha256', 'sourceRoot'];
+ const bindingDifferences = bindings
+ ? fields.filter(field => proof[field] !== bindings[field]).map(field => ({
+ field,
+ current: bindings[field],
+ retained: proof[field] ?? null,
+ }))
+ : [];
+ return Object.freeze({
+ present: true,
+ classification: bindingDifferences.length === 0
+ ? 'retained-current-source-evidence'
+ : 'retained-historical-evidence',
+ repositoryPath: selectedRepositoryPath,
+ selector: artifactMap.selector,
+ expectedSha256: expected.sha256,
+ actualSha256,
+ currentSourceMatch: bindingDifferences.length === 0,
+ usedAsCurrentProof: false,
+ bindingDifferences,
+ });
+}
+
+function docker(args, { allowFailure = false, input, maxBuffer = 64 * 1024 * 1024 } = {}) {
+ const result = spawnSync('docker', ['--context', DOCKER_CONTEXT, ...args], {
+ encoding: 'utf8',
+ input,
+ maxBuffer,
+ });
+ if (!allowFailure && (result.error || result.status !== 0)) {
+ const marker = /ERROR:\s+([A-Z][A-Z0-9_]{3,96})\b/.exec(result.stderr)?.[1]
+ || /FATAL:\s+([A-Z][A-Z0-9_]{3,96})\b/.exec(result.stderr)?.[1]
+ || 'UNKNOWN';
+ fail(`LOCAL_REHEARSAL_COMMAND_FAILED_${marker}`);
+ }
+ return result;
+}
+
+function psql(container, database, sql, { actor = 'postgres', allowFailure = false } = {}) {
+ return docker([
+ 'exec', '-i', '-e', `PGPASSWORD=${BOOTSTRAP_PASSWORD}`, container,
+ 'psql', '-XAtq', '-v', 'ON_ERROR_STOP=1', '-v', 'VERBOSITY=terse',
+ '-h', '127.0.0.1', '-U', actor, '-d', database, '--file=-',
+ ], { allowFailure, input: sql });
+}
+
+function scalar(container, database, sql, actor = 'postgres') {
+ return psql(container, database, `${sql}\n`, { actor }).stdout.trim();
+}
+
+function state(container, database, stateQuery) {
+ const lines = scalar(container, database, `${stateQuery};`).split(/\r?\n/).filter(Boolean);
+ if (lines.length !== 1) fail('LOCAL_REHEARSAL_STATE_SHAPE_INVALID');
+ try { return JSON.parse(lines[0]); } catch { fail('LOCAL_REHEARSAL_STATE_JSON_INVALID'); }
+}
+
+function waitForReady(container) {
+ for (let attempt = 0; attempt < 60; attempt += 1) {
+ const ready = docker([
+ 'exec', '-e', `PGPASSWORD=${BOOTSTRAP_PASSWORD}`, container,
+ 'pg_isready', '-h', '127.0.0.1', '-U', 'postgres', '-d', 'postgres',
+ ], { allowFailure: true });
+ if (ready.status === 0) return;
+ Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, 250);
+ }
+ fail('LOCAL_REHEARSAL_CONTAINER_NOT_READY');
+}
+
+function createRequiredRoles(container) {
+ const roleArray = `ARRAY[${REQUIRED_ROLES.map(quote).join(',')}]`;
+ psql(container, 'postgres', `DO $roles$
+DECLARE role_name text;
+BEGIN
+ FOREACH role_name IN ARRAY ${roleArray} LOOP
+ IF to_regrole(role_name) IS NULL THEN
+ EXECUTE format('CREATE ROLE %I NOLOGIN NOSUPERUSER NOCREATEDB NOCREATEROLE NOREPLICATION NOBYPASSRLS',role_name);
+ END IF;
+ END LOOP;
+ ALTER ROLE privacy_auth_bridge INHERIT;
+ ALTER ROLE privacy_retention_activation_operator NOINHERIT;
+ ALTER ROLE privacy_retention_legal_approver NOINHERIT;
+ ALTER ROLE privacy_retention_operator_approver NOINHERIT;
+ ALTER ROLE privacy_workflow_owner NOINHERIT;
+END $roles$;`);
+ psql(container, 'postgres', `GRANT privacy_auth_bridge TO postgres WITH ADMIN TRUE,INHERIT FALSE,SET FALSE GRANTED BY supabase_admin;
+GRANT privacy_retention_activation_operator TO postgres WITH ADMIN TRUE,INHERIT FALSE,SET FALSE GRANTED BY supabase_admin;
+GRANT privacy_retention_legal_approver TO postgres WITH ADMIN TRUE,INHERIT FALSE,SET FALSE GRANTED BY supabase_admin;
+GRANT privacy_retention_operator_approver TO postgres WITH ADMIN TRUE,INHERIT FALSE,SET FALSE GRANTED BY supabase_admin;
+GRANT privacy_workflow_owner TO postgres WITH ADMIN TRUE,INHERIT FALSE,SET FALSE GRANTED BY supabase_admin;`, { actor: 'supabase_admin' });
+ psql(container, 'postgres', 'GRANT privacy_workflow_owner TO privacy_auth_bridge WITH ADMIN FALSE,INHERIT TRUE,SET TRUE GRANTED BY postgres;');
+}
+
+function fixtureSql() {
+ return `DO $fixture$
+BEGIN
+ IF (SELECT count(*) FROM supabase_migrations.schema_migrations)<>0
+ OR (SELECT count(*) FROM public.profiles)<>0
+ OR (SELECT count(*) FROM public.reviews)<>0
+ THEN RAISE EXCEPTION 'LOCAL_REHEARSAL_FIXTURE_INVALID'; END IF;
+END $fixture$;
+INSERT INTO supabase_migrations.schema_migrations(version,statements,name)
+SELECT lpad(value::text,14,'0'),ARRAY['local schema-only predecessor fixture'],format('local_predecessor_%s',value)
+FROM generate_series(1,86) value;
+INSERT INTO pipeline_control.restaurant_review_policy(singleton,enabled)
+VALUES(true,false)
+ON CONFLICT(singleton) DO UPDATE SET enabled=false,operator_id=NULL,last_run_at=NULL;
+INSERT INTO pipeline_control.admin_evaluation_catalog_revision(singleton,revision)
+VALUES(true,1)
+ON CONFLICT(singleton) DO NOTHING;`;
+}
+
+function inspectContainer(target, expected, dockerImpl) {
+ const result = dockerImpl(['container', 'inspect', target], { allowFailure: true });
+ if (result.error || result.status !== 0) {
+ if (/No such (?:object|container)/i.test(result.stderr || '')) return null;
+ fail('LOCAL_REHEARSAL_CONTAINER_INSPECTION_FAILED');
+ }
+ let values;
+ try { values = JSON.parse(result.stdout); } catch { fail('LOCAL_REHEARSAL_CONTAINER_INSPECTION_INVALID'); }
+ if (!Array.isArray(values) || values.length !== 1) fail('LOCAL_REHEARSAL_CONTAINER_INSPECTION_INVALID');
+ const value = values[0];
+ const id = typeof value?.Id === 'string' ? value.Id : '';
+ const name = typeof value?.Name === 'string' ? value.Name.replace(/^\//, '') : '';
+ const owner = value?.Config?.Labels?.[OWNER_LABEL];
+ if (!/^[0-9a-f]{12,64}$/.test(id)
+ || (expected.id && id !== expected.id)
+ || name !== expected.name
+ || owner !== expected.token) {
+ fail('LOCAL_REHEARSAL_CONTAINER_OWNERSHIP_UNPROVEN');
+ }
+ return Object.freeze({ id, name, token: owner });
+}
+
+export function createOwnedContainer(ownership, {
+ dockerImpl = docker,
+ generateToken = () => randomBytes(16).toString('hex'),
+ processId = process.pid,
+} = {}) {
+ if (ownership.attempted || ownership.created || ownership.id || ownership.name || ownership.token) {
+ fail('LOCAL_REHEARSAL_CONTAINER_OWNERSHIP_STATE_INVALID');
+ }
+ const token = generateToken();
+ if (!/^[0-9a-f]{32}$/.test(token)) fail('LOCAL_REHEARSAL_CONTAINER_TOKEN_INVALID');
+ const name = `${CONTAINER_PREFIX}-${processId}-${token.slice(0, 12)}`;
+ ownership.name = name;
+ ownership.token = token;
+ ownership.attempted = true;
+ const creation = dockerImpl([
+ 'run', '-d', '--rm', '--name', name, '--label', `${OWNER_LABEL}=${token}`,
+ '-e', `POSTGRES_PASSWORD=${BOOTSTRAP_PASSWORD}`, IMAGE,
+ ], { allowFailure: true });
+ const observed = inspectContainer(name, ownership, dockerImpl);
+ if (!observed) fail('LOCAL_REHEARSAL_CONTAINER_CREATION_UNCONFIRMED');
+ // The name and per-run label are the ownership authority. Record the exact
+ // inspected ID before reconciling the run acknowledgement so any later
+ // mismatch is still cleaned up by the caller's finally block.
+ ownership.id = observed.id;
+ ownership.created = true;
+ const acknowledgedId = creation.status === 0 ? creation.stdout.trim() : '';
+ if (acknowledgedId && acknowledgedId !== observed.id) fail('LOCAL_REHEARSAL_CONTAINER_ACK_MISMATCH');
+ ownership.creationAcknowledged = !creation.error && creation.status === 0 && Boolean(acknowledgedId);
+ ownership.creationReconciled = !ownership.creationAcknowledged;
+ return observed.id;
+}
+
+export function removeOwnedContainer(ownership, { dockerImpl = docker } = {}) {
+ if ((!ownership.attempted && !ownership.created) || !ownership.name || !ownership.token) return false;
+ let observed;
+ try {
+ observed = inspectContainer(ownership.id || ownership.name, ownership, dockerImpl);
+ } catch (error) {
+ if (!ownership.id && error?.code === 'LOCAL_REHEARSAL_CONTAINER_OWNERSHIP_UNPROVEN') return false;
+ throw error;
+ }
+ if (!observed) {
+ ownership.created = false;
+ ownership.removed = true;
+ return true;
+ }
+ ownership.id = observed.id;
+ ownership.created = true;
+ const removal = dockerImpl(['container', 'rm', '--force', observed.id], { allowFailure: true });
+ const after = inspectContainer(observed.id, ownership, dockerImpl);
+ if (after) fail('LOCAL_REHEARSAL_CONTAINER_REMOVAL_UNCONFIRMED');
+ ownership.created = false;
+ ownership.removed = true;
+ ownership.removalAcknowledged = removal.status === 0;
+ return true;
+}
+
+function initializeFixture(ownership) {
+ const container = createOwnedContainer(ownership);
+ waitForReady(container);
+ psql(container, 'template1', 'ALTER ROLE postgres SUPERUSER;\nDROP DATABASE postgres WITH (FORCE);\nCREATE DATABASE postgres OWNER postgres TEMPLATE template0;', { actor: 'supabase_admin' });
+ createRequiredRoles(container);
+ const dump = docker([
+ 'exec', SOURCE_CONTAINER,
+ 'pg_dump', '-U', 'postgres', '-d', 'ranking_clone',
+ ]).stdout;
+ const deterministicDump = dump.replace(/^\\(?:un)?restrict [^\n]*\n/gm, '');
+ psql(container, 'postgres', dump);
+ // pg_dump recreates enum/type OIDs. Rebind only the fixture's derived OID
+ // projection to each unchanged source signature before replaying migrations.
+ psql(container, 'postgres', `UPDATE privacy_retention.g014_public_rpc_allowlist allowed
+SET identity_arguments=procedure.proargtypes::text
+FROM pg_proc procedure
+WHERE procedure.oid=to_regprocedure(allowed.source_signature)
+ AND allowed.identity_arguments IS DISTINCT FROM procedure.proargtypes::text;`);
+ psql(container, 'postgres', 'ALTER ROLE postgres NOSUPERUSER;', { actor: 'supabase_admin' });
+ PREDECESSOR_PATHS.forEach((path, index) => {
+ const result = psql(container, 'postgres', readFileSync(resolve(REPOSITORY_ROOT, path)), { allowFailure: true });
+ if (result.status !== 0) {
+ const marker = /ERROR:\s+([A-Z][A-Z0-9_]{3,96})\b/.exec(result.stderr)?.[1] || 'UNKNOWN';
+ fail(`LOCAL_REHEARSAL_PREDECESSOR_FAILED_${index + 1}_${marker}`);
+ }
+ });
+ psql(container, 'postgres', fixtureSql());
+ return Object.freeze({
+ container,
+ dumpSha256: sha256(deterministicDump),
+ engine: scalar(container, 'postgres', 'SHOW server_version;'),
+ });
+}
+
+function parseStages(output, manifest) {
+ const lines = output.split(/\r?\n/).map(line => line.trim()).filter(Boolean);
+ if (lines.length !== 12) fail('LOCAL_REHEARSAL_STAGE_COUNT_INVALID');
+ let stages;
+ try {
+ stages = lines.map((line, index) => {
+ const value = JSON.parse(line);
+ if (value.kind !== 'successor-rehearsal-stage'
+ || value.index !== index
+ || value.serverVersionNum !== 170006) fail('LOCAL_REHEARSAL_STAGE_SHAPE_INVALID');
+ return value.state;
+ });
+ } catch { fail('LOCAL_REHEARSAL_STAGE_JSON_INVALID'); }
+ validateSuccessorStageStates(stages, manifest);
+ return stages;
+}
+
+export function injectFinalApplyExpiryFault(sql, expiresAt, {
+ expiredAt = '1970-01-01T00:00:00.000Z',
+} = {}) {
+ if (typeof sql !== 'string'
+ || !Number.isFinite(Date.parse(expiresAt))
+ || !Number.isFinite(Date.parse(expiredAt))) {
+ fail('LOCAL_REHEARSAL_APPLY_EXPIRY_FAULT_INVALID');
+ }
+ const markerIndex = sql.indexOf(SUCCESSOR_PRE_COMMIT_EXPIRY_MARKER);
+ if (markerIndex < 0
+ || sql.indexOf(SUCCESSOR_PRE_COMMIT_EXPIRY_MARKER, markerIndex + 1) >= 0) {
+ fail('LOCAL_REHEARSAL_APPLY_EXPIRY_MARKER_INVALID');
+ }
+ const prefix = sql.slice(0, markerIndex + SUCCESSOR_PRE_COMMIT_EXPIRY_MARKER.length);
+ const suffix = sql.slice(prefix.length);
+ const deadline = `'${expiresAt}'::timestamptz`;
+ const matches = suffix.split(deadline).length - 1;
+ if (matches !== 1) fail('LOCAL_REHEARSAL_APPLY_EXPIRY_MARKER_INVALID');
+ const faultedSuffix = suffix.replace(deadline, `'${expiredAt}'::timestamptz`);
+ return Object.freeze({
+ sql: `${prefix}${faultedSuffix}`,
+ marker: SUCCESSOR_PRE_COMMIT_EXPIRY_MARKER,
+ originalDeadline: expiresAt,
+ injectedDeadline: expiredAt,
+ wrapperOnlyFaultInjection: true,
+ });
+}
+
+function startLockHolder(container) {
+ const args = [
+ '--context', DOCKER_CONTEXT,
+ 'exec', '-i', '-e', `PGPASSWORD=${BOOTSTRAP_PASSWORD}`, container,
+ 'psql', '-XAtq', '-v', 'ON_ERROR_STOP=1', '-h', '127.0.0.1', '-U', 'postgres', '-d', 'postgres',
+ ];
+ const child = spawn('docker', args, { stdio: ['pipe', 'pipe', 'pipe'] });
+ let stdout = '';
+ let stderr = '';
+ let readySettled = false;
+ const ready = new Promise((resolveReady, rejectReady) => {
+ child.on('error', rejectReady);
+ child.stdout.on('data', chunk => {
+ stdout += chunk.toString();
+ if (!readySettled && stdout.includes('LOCK_HELD')) {
+ readySettled = true;
+ resolveReady();
+ }
+ });
+ child.stderr.on('data', chunk => { stderr += chunk.toString(); });
+ child.on('exit', code => {
+ if (!readySettled) rejectReady(new Error(code === 0 ? 'LOCAL_LOCK_OUTPUT_MISSING' : 'LOCAL_LOCK_FAILED'));
+ });
+ });
+ const completion = new Promise((resolveCompletion, rejectCompletion) => {
+ child.on('error', rejectCompletion);
+ child.on('close', code => code === 0 ? resolveCompletion() : rejectCompletion(new Error('LOCAL_LOCK_FAILED')));
+ });
+ child.stdin.end(`BEGIN;\nSELECT pg_advisory_xact_lock(${ADVISORY_LOCK_KEY}::bigint);\nSELECT 'LOCK_HELD';\nSELECT pg_sleep(1.6);\nCOMMIT;\n`);
+ return { child, completion, ready, stderr: () => stderr };
+}
+
+export function parseArguments(argv) {
+ if (argv.length === 0) return Object.freeze({ outputPath: null });
+ if (argv.length !== 2 || argv[0] !== '--output' || !argv[1]) fail('LOCAL_REHEARSAL_ARGUMENTS_INVALID');
+ return Object.freeze({ outputPath: resolve(argv[1]) });
+}
+
+export function publishCurrentProof({ retainedProof, proof, outputPath }, {
+ writeFileImpl = writeFileSync,
+ writeStdoutImpl = value => process.stdout.write(value),
+} = {}) {
+ if (outputPath) {
+ writeFileImpl(outputPath, `${JSON.stringify(proof, null, 2)}\n`, { flag: 'wx', mode: 0o600 });
+ }
+ const envelope = Object.freeze({
+ kind: 'review-media-sql-local-rehearsal',
+ retainedProof,
+ currentProof: proof,
+ currentOutputPath: outputPath,
+ });
+ writeStdoutImpl(`${JSON.stringify(envelope)}\n`);
+ return envelope;
+}
+
+export async function run(argv = process.argv.slice(2)) {
+ const options = parseArguments(argv);
+ if (options.outputPath && existsSync(options.outputPath)) fail('LOCAL_REHEARSAL_CURRENT_OUTPUT_EXISTS');
+ const record = loadSuccessorManifest();
+ const manifestBytes = readFileSync(resolve(REPOSITORY_ROOT, '.github/review-media-sql-successor.v1.json'));
+ const harnessBytes = readFileSync(fileURLToPath(import.meta.url));
+ const bindings = currentBindings(record, manifestBytes, harnessBytes);
+ const retainedProof = inspectRetainedProof({ bindings });
+ const ownership = { attempted: false, created: false, id: null, name: null, token: null };
+ let proof;
+ try {
+ const environment = initializeFixture(ownership);
+ const container = environment.container;
+ if (!environment.engine.startsWith('17.6')) fail('LOCAL_REHEARSAL_ENGINE_INVALID');
+ const stateQuery = buildSuccessorStateQuery(record.manifest);
+
+ const baseline = state(container, 'postgres', stateQuery);
+ const plan = compileSuccessorRehearsalPlan(record, { expiresAt: '9999-12-31T23:59:59.999Z' }, baseline);
+ const replay = psql(container, 'postgres', plan.sql);
+ const stages = parseStages(replay.stdout, record.manifest);
+ const restored = state(container, 'postgres', stateQuery);
+ if (!equal(restored, baseline) || !equal(stages[0], baseline)) fail('LOCAL_REHEARSAL_ROLLBACK_DRIFT');
+
+ const errorBaseline = state(container, 'postgres', stateQuery);
+ const errorPlan = compileSuccessorRehearsalPlan(record, { expiresAt: '9999-12-31T23:59:59.999Z' }, errorBaseline);
+ const forcedErrorSql = errorPlan.sql.replace(
+ /\nROLLBACK;\s*$/,
+ "\nDO $forced$ BEGIN RAISE EXCEPTION 'LOCAL_FORCED_REHEARSAL_ERROR'; END $forced$;\nROLLBACK;",
+ );
+ const forcedError = psql(container, 'postgres', forcedErrorSql, { allowFailure: true });
+ if (forcedError.status === 0 || !/LOCAL_FORCED_REHEARSAL_ERROR/.test(forcedError.stderr)) {
+ fail('LOCAL_REHEARSAL_FORCED_ERROR_MISSING');
+ }
+ if (!equal(state(container, 'postgres', stateQuery), errorBaseline)) fail('LOCAL_REHEARSAL_ERROR_ROLLBACK_DRIFT');
+
+ const lostBaseline = state(container, 'postgres', stateQuery);
+ const lostPlan = compileSuccessorRehearsalPlan(record, { expiresAt: '9999-12-31T23:59:59.999Z' }, lostBaseline);
+ psql(container, 'postgres', lostPlan.sql); // Deliberately discard the complete response once.
+ if (!equal(state(container, 'postgres', stateQuery), lostBaseline)) fail('LOCAL_REHEARSAL_LOST_RESPONSE_DRIFT');
+
+ const expiryBaseline = state(container, 'postgres', stateQuery);
+ const holder = startLockHolder(container);
+ await holder.ready;
+ const expiresAt = new Date(Date.now() + 1_000).toISOString();
+ const expiryPlan = compileSuccessorRehearsalPlan(record, { expiresAt }, expiryBaseline);
+ const expiryResult = psql(container, 'postgres', expiryPlan.sql, { allowFailure: true });
+ await holder.completion;
+ if (expiryResult.status === 0
+ || !/SUCCESSOR_ADMISSION_EXPIRED/.test(expiryResult.stderr)
+ || /lock timeout/i.test(expiryResult.stderr)) {
+ fail('LOCAL_REHEARSAL_CONTENDED_EXPIRY_NOT_REJECTED');
+ }
+ if (!equal(state(container, 'postgres', stateQuery), expiryBaseline)) fail('LOCAL_REHEARSAL_EXPIRY_DRIFT');
+
+ const applyExpiryBaseline = state(container, 'postgres', stateQuery);
+ const applyExpiresAt = '9999-12-31T23:59:59.999Z';
+ const applyPlan = compileSuccessorPlan(record, { expiresAt: applyExpiresAt, stageStates: stages });
+ const applyExpiryFault = injectFinalApplyExpiryFault(applyPlan.sql, applyExpiresAt);
+ const applyExpiryResult = psql(
+ container,
+ 'postgres',
+ `BEGIN;\n${applyExpiryFault.sql}\nCOMMIT;`,
+ { allowFailure: true },
+ );
+ if (applyExpiryResult.status === 0
+ || !/ERROR:\s+(?:[0-9A-Z]{5}:\s*)?SUCCESSOR_ADMISSION_EXPIRED\b/m.test(applyExpiryResult.stderr)
+ || /transaction timeout/i.test(applyExpiryResult.stderr)) {
+ fail('LOCAL_REHEARSAL_APPLY_EXPIRY_NOT_REJECTED');
+ }
+ let applyTerminalReadback;
+ try { applyTerminalReadback = JSON.parse(applyExpiryResult.stdout.trim()); }
+ catch { fail('LOCAL_REHEARSAL_APPLY_TERMINAL_READBACK_INVALID'); }
+ if (!equal(applyTerminalReadback, stages.at(-1))) {
+ fail('LOCAL_REHEARSAL_APPLY_TERMINAL_READBACK_INVALID');
+ }
+ if (!equal(state(container, 'postgres', stateQuery), applyExpiryBaseline)) {
+ fail('LOCAL_REHEARSAL_APPLY_EXPIRY_ROLLBACK_DRIFT');
+ }
+
+ const toolchain = record.manifest.toolchain.map(entry => ({ ...entry }));
+ proof = {
+ schemaVersion: 1,
+ status: 'passed',
+ environment: {
+ engine: environment.engine,
+ image: IMAGE,
+ emptyRowChecks: ['public.profiles', 'public.reviews'],
+ },
+ manifestSha256: sha256(manifestBytes),
+ manifestToolchainRoot: sha256(JSON.stringify(canonical(toolchain))),
+ rehearsalHarnessSha256: sha256(harnessBytes),
+ sourceRoot: record.manifest.sourceRoot,
+ fixtureDumpSha256: environment.dumpSha256,
+ predecessorSources: PREDECESSOR_PATHS.map(path => ({
+ path,
+ sha256: sha256(readFileSync(resolve(REPOSITORY_ROOT, path))),
+ })),
+ migrationCount: record.manifest.migrations.length,
+ statementCount: record.manifest.migrations.reduce((sum, migration) => sum + migration.statementCount, 0),
+ baselineStateSha256: sha256(JSON.stringify(canonical(baseline))),
+ stageStatesSha256: sha256(JSON.stringify(canonical(stages))),
+ terminalStateSha256: sha256(JSON.stringify(canonical(stages.at(-1)))),
+ compiledSqlBytes: Buffer.byteLength(plan.sql),
+ compiledSqlSha256: sha256(plan.sql),
+ applyCompiledSqlSha256: sha256(applyPlan.sql),
+ applyExpiryFaultedSqlSha256: sha256(applyExpiryFault.sql),
+ results: {
+ rollbackStageCount: stages.length,
+ rollbackExact: true,
+ forcedErrorRejected: true,
+ forcedErrorRollbackExact: true,
+ lostResponseMutationAttempts: 1,
+ lostResponseRestorationExact: true,
+ contendedExpiryRejected: true,
+ contendedExpiryRollbackExact: true,
+ applyFinalGuardDeadlineFaultInjected: true,
+ applyTerminalReadbackObserved: true,
+ applyExpiryRejectedBeforeCommit: true,
+ applyExpiryMutationAttempts: 1,
+ applyExpiryRollbackExact: true,
+ },
+ hostedWrites: false,
+ operatingRowsRead: false,
+ rawSqlInProof: false,
+ sourceMutation: false,
+ };
+ } finally {
+ removeOwnedContainer(ownership);
+ }
+ proof.temporaryContainerRemoved = ownership.removed === true;
+ if (!proof.temporaryContainerRemoved) fail('LOCAL_REHEARSAL_CONTAINER_REMOVAL_UNCONFIRMED');
+ publishCurrentProof({ retainedProof, proof, outputPath: options.outputPath });
+ return Object.freeze({ retainedProof, currentProof: proof, currentOutputPath: options.outputPath });
+}
+
+if (process.argv[1] && resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
+ await run();
+}
diff --git a/apps/web/performance/review-db-successor-20261012/README.md b/apps/web/performance/review-db-successor-20261012/README.md
new file mode 100644
index 0000000000..323d2519b2
--- /dev/null
+++ b/apps/web/performance/review-db-successor-20261012/README.md
@@ -0,0 +1,100 @@
+# Review media SQL successor: local PG17 evidence
+
+This directory contains local-only evidence for the held 11-stage review-media
+successor. It does not authorize or prove a hosted database write.
+
+The retained pre-hardening replay cloned the schema-only `ranking_clone`
+template into a fixed database and checked zero profiles and zero reviews.
+Current code uses a process-specific database name with a 128-bit random suffix.
+It preserves a pre-create absence baseline and verifies exact owner/template
+metadata before cleanup, including lost CREATE acknowledgement and first readback. Six already-applied predecessor sources reconstruct
+the relevant pre-state; 86 synthetic ledger rows model the observed predecessor
+cardinality without claiming hosted parity. This harness does not read or prove
+the hosted ledger. A hosted attempt must freshly confirm that the actual ledger
+is still exactly 86 and capture all 12 hosted stage states inside the admitted
+window; this local proof cannot be reused as that rehearsal receipt.
+
+Run with Node 24:
+
+```sh
+/opt/homebrew/opt/node@24/bin/node \
+ apps/web/performance/review-db-successor-20261012/verify-review-media-sql-successor.mjs
+```
+
+The retained historical proof records 11 migrations, 112 source statements, exact stage
+roots, ledger transition 86 to 97, atomic success, forced-failure rollback,
+wrong `finish_review_media_cleanup` preimage rejection, post-lock admission
+expiry rejection under real advisory-lock contention, and independent catalog
+drift rejection for view `security_invoker`, column ACLs, and PostgreSQL 17 role
+membership options. Every rejection preserves the pre-apply database state, and
+the temporary database and local fixture roles are removed before proof output.
+The compiled transaction is 6,492,122 bytes. Two complete runs produced the
+same proof SHA-256, `6f5dc5c5603c8a4d8b601d3edb28009e454e955292a65707747e66f274557b3f`.
+
+The Storage root deliberately excludes bucket creation and update timestamps so
+the same security and behavior configuration has a stable digest across an
+independent replay. It still binds the bucket identity, visibility, ownership,
+limits, MIME types, type, versioning, and lifecycle fields. Therefore this proof
+does **not** establish preservation of timestamp-only changes or distinguish a
+bucket that was deleted and recreated with identical included settings.
+
+The production manifest remains `held`. Hosted execution still requires a
+protected-main source receipt, fresh 12-state rehearsal receipt, current Vercel
+rollback readback, all seven writer-fence receipts, a private admission file,
+and a new journal path.
+
+## Current ownership verification
+
+`verify-fixture-ownership.mjs` creates a separate labelled PostgreSQL 17.6
+container and exercises six real ownership scenarios: normal role/database
+creation, lost creation acknowledgement, lost first readback, and lost role
+deletion acknowledgement. Each scenario attempts CREATE once and confirms
+cleanup. Database predecessor/fixture application is deliberately excluded from
+this ownership check; the separate rollback verifier covers SQL execution.
+
+Use `--output /absolute/new-proof.json` to reproduce without overwriting any
+retained evidence. The verifier refuses existing output before creating a
+container and removes only its exact per-run labelled container.
+
+The first startup attempt used Unix socket readiness too early. Another attempt
+expected the injected error instead of the harness bounded error. Their failure
+records and the interpretation are retained; neither establishes a production
+failure or performance result. Official role privilege semantics are described
+in [PostgreSQL 17 role attributes](https://www.postgresql.org/docs/17/role-attributes.html).
+
+Current ownership proof: `pg17-fixture-ownership-current-20261013.json`
+(SHA-256 `0e7f96493ba9ad63959fa25c775fb6e95d0616d47fa4bdae973e248f5eab0349`).
+The earlier `pg17-fixture-ownership-20261013.json` is retained for the verifier
+before optional exclusive output support. Both establish six isolated scenarios;
+the current proof matches the current helper and verifier bytes.
+
+## Reviewed helper binding — 2026-10-13
+
+Current ownership evidence is `pg17-fixture-ownership-reviewed-20261013.json`.
+It binds the fixture helper, this ownership verifier and the imported rollback
+container helper. All three source byte hashes are checked before/after the run,
+including cleanup. Six scenarios again recorded one CREATE and one confirmed
+cleanup each. Earlier proofs remain immutable historical evidence.
+
+The forward fixture harness now sends the full SQL once. A failed acknowledgement
+is reconciled only by an exact read-only baseline/terminal-state read; it never
+performs a diagnostic mutation replay. Two injected outcome cases cover terminal
+commit acknowledgement loss and a rolled-back baseline. This change has local
+fault tests; no fresh forward-harness replay is claimed.
+
+## Current review-fix replay — 2026-10-14
+
+`pg17-current-review-fixes-20261014-v2.json` binds the current manifest and harness.
+It passed the 11-stage PG17.6 atomic commit, exact rollback, wrong preimage,
+view/column ACL/membership drift, and real advisory-wait expiry rejection. The
+per-run database and fixture roles were removed. The template still records
+1,963 manifest rows and zero profiles/reviews; no hosted state was accessed.
+
+The v1 attempt failed to observe contention in its 700ms observation window.
+Its stdout and stderr are retained. The local fixture now confirms the holder
+is ready before starting the same 1.5-second expiry window, observes an actual
+wait before expiry, and releases the lock just after expiry. Production timeouts
+are unchanged; this is a fixture-order correction, not a performance result.
+The historical proof and old checksum indexes remain available. The current
+retained-proof reader verifies the independently pinned checksum map before
+accepting any entry and still classifies old source bindings as historical.
diff --git a/apps/web/performance/review-db-successor-20261012/SHA256SUMS b/apps/web/performance/review-db-successor-20261012/SHA256SUMS
new file mode 100644
index 0000000000..d57f554868
--- /dev/null
+++ b/apps/web/performance/review-db-successor-20261012/SHA256SUMS
@@ -0,0 +1,25 @@
+295b2354c562ed87102c5e256497a2705f90941587fefa7e9ad8a4ba8e40787f .github/review-media-sql-successor.v1.json
+f7321f607a5bf3a24ba099d51abc67254db52fcc385945692426d1b2dfa6e431 apps/web/performance/review-db-rehearsal-20261012/verify-review-media-sql-rehearsal.mjs
+c592db68bb0da972e91035d42543b0023571d378f14ce6da0771a21624212512 apps/web/performance/review-db-successor-20261012/README.md
+1a3aa2b9a3e1faef9916cce3d7d3b11246683dadce15da97b3a0e3a75fbb9048 apps/web/performance/review-db-successor-20261012/SHA256SUMS-0833ed81-historical
+d19614c3478ddbe90c38b856ebcbdba7a3a4dc9599ce259c2eabd60dcb0337a6 apps/web/performance/review-db-successor-20261012/SHA256SUMS-4ba9c6d2-historical
+fe70a536d2fde48ad6332c76b8e64ee8e03f3784398022f190e45827e1c58983 apps/web/performance/review-db-successor-20261012/SHA256SUMS-4ba9c6d2-historical.sha256
+b9f3fca7e506e195312c2ca3e4da97f04c7767e42b2a0f980aab68dfc6395e09 apps/web/performance/review-db-successor-20261012/SHA256SUMS-4d4ecf60-historical
+dc27a75b296716c612e9e3b9cb35f0fd113129dad5215f72a76f3ea94747ee95 apps/web/performance/review-db-successor-20261012/fixture-ownership-failure-interpretation-20261013.json
+d9cdeba56f0c38a0c1c46f50b48645d827b47df2ba09f3101ff01dcc828cf118 apps/web/performance/review-db-successor-20261012/fixture-ownership-first-attempt-20261013.json
+0a3b893331ff16732dd79c6bfca9f88689f7be3065777e62b0dcf3528cea05df apps/web/performance/review-db-successor-20261012/fixture-ownership-protocol-failure-20261013.json
+e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 apps/web/performance/review-db-successor-20261012/pg17-current-review-fixes-20261014-v1.json
+701edcd5b4d76bfef560aa280ec80b812274058fb358375106f80ba90c526849 apps/web/performance/review-db-successor-20261012/pg17-current-review-fixes-20261014-v1.stderr.log
+5a1aa0fb9f45ad4510377455b82b6940f18fa43b25123a4fbafcdb8fcf235ee6 apps/web/performance/review-db-successor-20261012/pg17-current-review-fixes-20261014-v2.json
+e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 apps/web/performance/review-db-successor-20261012/pg17-current-review-fixes-20261014-v2.stderr.log
+ef0af48a51ee8e3b9a57725eb7504c88401d2b59c3470dec0da11b5257187fbc apps/web/performance/review-db-successor-20261012/pg17-fixture-ownership-20261013.json
+0e7f96493ba9ad63959fa25c775fb6e95d0616d47fa4bdae973e248f5eab0349 apps/web/performance/review-db-successor-20261012/pg17-fixture-ownership-current-20261013.json
+ad6917e416ff20e074eaedd523baa3f0366e3c865f62849a7df6793c2fbd758d apps/web/performance/review-db-successor-20261012/pg17-fixture-ownership-reviewed-20261013.json
+6f5dc5c5603c8a4d8b601d3edb28009e454e955292a65707747e66f274557b3f apps/web/performance/review-db-successor-20261012/pg17-local-replay.json
+7815d90d73d6b3508a355aee06743c22ff802e78ffca46172cda18ca83ed51b4 apps/web/performance/review-db-successor-20261012/review-fix-harness-units-20261014-v2.log
+41972cf6c4ac2b819b42cf9f06b762d54fb9d161f4594192492fccf79cca7418 apps/web/performance/review-db-successor-20261012/verify-fixture-ownership.mjs
+768b8887a30c2b8f496a73a21d373b85a0b64fadaed3a1485034a729ee704d39 apps/web/performance/review-db-successor-20261012/verify-review-media-sql-successor-4ba9c6d2-historical.source.txt
+7e750e9d0126f6f10afe9056d747a2ea854d462161ea4e8f0168e2751d554c0a apps/web/performance/review-db-successor-20261012/verify-review-media-sql-successor.mjs
+4f1b2798e0cfc13db4f11cd6eb4a8653320aa8516eccd71d410fcff6d06a7f24 apps/web/scripts/review-media-sql-successor.mjs
+fcebc20dc4846606dc14bdbe658a07daa32430ad632e52383151f5c22d22efb9 apps/web/scripts/supabase-migration-bundle.mjs
+92651102154fd4d573ca841137bd31c319ef545b2527e7da17398bd2e6b97d47 apps/web/tests-unit/review-media-fixture-ownership-verifier.test.ts
diff --git a/apps/web/performance/review-db-successor-20261012/SHA256SUMS-0833ed81-historical b/apps/web/performance/review-db-successor-20261012/SHA256SUMS-0833ed81-historical
new file mode 100644
index 0000000000..a33aadc18b
--- /dev/null
+++ b/apps/web/performance/review-db-successor-20261012/SHA256SUMS-0833ed81-historical
@@ -0,0 +1,13 @@
+ae764bb760102a6d7d066be669c85248feed1823eafded4bf8c246a062090a8c .github/review-media-sql-successor.v1.json
+0fddfa7996df84c1b2e7463b2be696ffff442d97fd24abb476c2f701a9d3d946 apps/web/performance/review-db-successor-20261012/README.md
+b9f3fca7e506e195312c2ca3e4da97f04c7767e42b2a0f980aab68dfc6395e09 apps/web/performance/review-db-successor-20261012/SHA256SUMS-4d4ecf60-historical
+dc27a75b296716c612e9e3b9cb35f0fd113129dad5215f72a76f3ea94747ee95 apps/web/performance/review-db-successor-20261012/fixture-ownership-failure-interpretation-20261013.json
+d9cdeba56f0c38a0c1c46f50b48645d827b47df2ba09f3101ff01dcc828cf118 apps/web/performance/review-db-successor-20261012/fixture-ownership-first-attempt-20261013.json
+0a3b893331ff16732dd79c6bfca9f88689f7be3065777e62b0dcf3528cea05df apps/web/performance/review-db-successor-20261012/fixture-ownership-protocol-failure-20261013.json
+ef0af48a51ee8e3b9a57725eb7504c88401d2b59c3470dec0da11b5257187fbc apps/web/performance/review-db-successor-20261012/pg17-fixture-ownership-20261013.json
+0e7f96493ba9ad63959fa25c775fb6e95d0616d47fa4bdae973e248f5eab0349 apps/web/performance/review-db-successor-20261012/pg17-fixture-ownership-current-20261013.json
+6f5dc5c5603c8a4d8b601d3edb28009e454e955292a65707747e66f274557b3f apps/web/performance/review-db-successor-20261012/pg17-local-replay.json
+7f6ded6fa4078fb0239b1c4d8cefe87dd8407c174e747ef118c2c98a1b481d9e apps/web/performance/review-db-successor-20261012/verify-fixture-ownership.mjs
+8c3d7df52c281a9df1d6a4bd2acefb59f9a23b60185559fc6a8298cd65fde370 apps/web/performance/review-db-successor-20261012/verify-review-media-sql-successor.mjs
+dcf552697251c2f10d859c7a2f56937a0f6e92ee9467e5b919807e498eff1d49 apps/web/scripts/review-media-sql-successor.mjs
+fcebc20dc4846606dc14bdbe658a07daa32430ad632e52383151f5c22d22efb9 apps/web/scripts/supabase-migration-bundle.mjs
diff --git a/apps/web/performance/review-db-successor-20261012/SHA256SUMS-4ba9c6d2-historical b/apps/web/performance/review-db-successor-20261012/SHA256SUMS-4ba9c6d2-historical
new file mode 100644
index 0000000000..0e1233e64c
--- /dev/null
+++ b/apps/web/performance/review-db-successor-20261012/SHA256SUMS-4ba9c6d2-historical
@@ -0,0 +1,17 @@
+23bf7e7d7e9e5d7962021dd3ec860e87b47d07e119ba0e39de2e423a0a601f4e .github/review-media-sql-successor.v1.json
+f7321f607a5bf3a24ba099d51abc67254db52fcc385945692426d1b2dfa6e431 apps/web/performance/review-db-rehearsal-20261012/verify-review-media-sql-rehearsal.mjs
+830e2f3d6baaf95ee42201b46ad703640b0caea8ae31d5db395adf74d9067f09 apps/web/performance/review-db-successor-20261012/README.md
+1a3aa2b9a3e1faef9916cce3d7d3b11246683dadce15da97b3a0e3a75fbb9048 apps/web/performance/review-db-successor-20261012/SHA256SUMS-0833ed81-historical
+b9f3fca7e506e195312c2ca3e4da97f04c7767e42b2a0f980aab68dfc6395e09 apps/web/performance/review-db-successor-20261012/SHA256SUMS-4d4ecf60-historical
+dc27a75b296716c612e9e3b9cb35f0fd113129dad5215f72a76f3ea94747ee95 apps/web/performance/review-db-successor-20261012/fixture-ownership-failure-interpretation-20261013.json
+d9cdeba56f0c38a0c1c46f50b48645d827b47df2ba09f3101ff01dcc828cf118 apps/web/performance/review-db-successor-20261012/fixture-ownership-first-attempt-20261013.json
+0a3b893331ff16732dd79c6bfca9f88689f7be3065777e62b0dcf3528cea05df apps/web/performance/review-db-successor-20261012/fixture-ownership-protocol-failure-20261013.json
+ef0af48a51ee8e3b9a57725eb7504c88401d2b59c3470dec0da11b5257187fbc apps/web/performance/review-db-successor-20261012/pg17-fixture-ownership-20261013.json
+0e7f96493ba9ad63959fa25c775fb6e95d0616d47fa4bdae973e248f5eab0349 apps/web/performance/review-db-successor-20261012/pg17-fixture-ownership-current-20261013.json
+ad6917e416ff20e074eaedd523baa3f0366e3c865f62849a7df6793c2fbd758d apps/web/performance/review-db-successor-20261012/pg17-fixture-ownership-reviewed-20261013.json
+6f5dc5c5603c8a4d8b601d3edb28009e454e955292a65707747e66f274557b3f apps/web/performance/review-db-successor-20261012/pg17-local-replay.json
+41972cf6c4ac2b819b42cf9f06b762d54fb9d161f4594192492fccf79cca7418 apps/web/performance/review-db-successor-20261012/verify-fixture-ownership.mjs
+768b8887a30c2b8f496a73a21d373b85a0b64fadaed3a1485034a729ee704d39 apps/web/performance/review-db-successor-20261012/verify-review-media-sql-successor.mjs
+5d61808b45661d412937fbfdfd3bfb75b1cf92c7c285e4d8ad061a2f667c3c0b apps/web/scripts/review-media-sql-successor.mjs
+fcebc20dc4846606dc14bdbe658a07daa32430ad632e52383151f5c22d22efb9 apps/web/scripts/supabase-migration-bundle.mjs
+92651102154fd4d573ca841137bd31c319ef545b2527e7da17398bd2e6b97d47 apps/web/tests-unit/review-media-fixture-ownership-verifier.test.ts
diff --git a/apps/web/performance/review-db-successor-20261012/SHA256SUMS-4ba9c6d2-historical.sha256 b/apps/web/performance/review-db-successor-20261012/SHA256SUMS-4ba9c6d2-historical.sha256
new file mode 100644
index 0000000000..c0ba176f4b
--- /dev/null
+++ b/apps/web/performance/review-db-successor-20261012/SHA256SUMS-4ba9c6d2-historical.sha256
@@ -0,0 +1 @@
+d19614c3478ddbe90c38b856ebcbdba7a3a4dc9599ce259c2eabd60dcb0337a6
diff --git a/apps/web/performance/review-db-successor-20261012/SHA256SUMS-4d4ecf60-historical b/apps/web/performance/review-db-successor-20261012/SHA256SUMS-4d4ecf60-historical
new file mode 100644
index 0000000000..1e6628b406
--- /dev/null
+++ b/apps/web/performance/review-db-successor-20261012/SHA256SUMS-4d4ecf60-historical
@@ -0,0 +1,6 @@
+9fac81d6e3ec81207b9c8985ccfdaf9e300bb7d44a7617d95579d4e8f96af19b .github/review-media-sql-successor.v1.json
+928d7b8bd601ab585870d4e04ee468e952f6053df81e403de925e032924f40d1 apps/web/scripts/review-media-sql-successor.mjs
+a374211be376e3a9af8215751accb52eb43943fc93b8651ba4e186e412588ba7 apps/web/scripts/supabase-migration-bundle.mjs
+03e999caeda7b57925126ba8d6bcec4ac5a671fedb2c69e07e988924ccb9f895 apps/web/performance/review-db-successor-20261012/verify-review-media-sql-successor.mjs
+6f5dc5c5603c8a4d8b601d3edb28009e454e955292a65707747e66f274557b3f apps/web/performance/review-db-successor-20261012/pg17-local-replay.json
+05176617c49dbf5343b0b9d0587dbf41ffa32e6f9497321517474fbb347c373e apps/web/performance/review-db-successor-20261012/README.md
diff --git a/apps/web/performance/review-db-successor-20261012/SHA256SUMS.sha256 b/apps/web/performance/review-db-successor-20261012/SHA256SUMS.sha256
new file mode 100644
index 0000000000..28f2abcf82
--- /dev/null
+++ b/apps/web/performance/review-db-successor-20261012/SHA256SUMS.sha256
@@ -0,0 +1 @@
+aff9374a0e6a6964b2d32f6cb5c6069296c5155b94c2ec1015460d2386fb4e88
diff --git a/apps/web/performance/review-db-successor-20261012/fixture-ownership-failure-interpretation-20261013.json b/apps/web/performance/review-db-successor-20261012/fixture-ownership-failure-interpretation-20261013.json
new file mode 100644
index 0000000000..68b48e3921
--- /dev/null
+++ b/apps/web/performance/review-db-successor-20261012/fixture-ownership-failure-interpretation-20261013.json
@@ -0,0 +1,7 @@
+{
+ "schemaVersion": 1,
+ "retainedFailureSha256": "0a3b893331ff16732dd79c6bfca9f88689f7be3065777e62b0dcf3528cea05df",
+ "classification": "fault-injection expectation mismatch",
+ "correction": "The harness intentionally wraps the injected first-readback loss as LOCAL_REHEARSAL_FIXTURE_ROLE_READBACK_FAILED. The verifier must match that bounded result, not the injected implementation error.",
+ "productionClaim": false
+}
diff --git a/apps/web/performance/review-db-successor-20261012/fixture-ownership-first-attempt-20261013.json b/apps/web/performance/review-db-successor-20261012/fixture-ownership-first-attempt-20261013.json
new file mode 100644
index 0000000000..401de6291b
--- /dev/null
+++ b/apps/web/performance/review-db-successor-20261012/fixture-ownership-first-attempt-20261013.json
@@ -0,0 +1,14 @@
+{
+ "schemaVersion": 1,
+ "kind": "failed-local-fixture-verification",
+ "status": {
+ "state": "completed",
+ "runnerPid": 35642,
+ "exitCode": 1,
+ "finishedAtUTC": "2026-10-13T07:06:00.847303+00:00"
+ },
+ "failureCode": "FIXTURE_OWNERSHIP_COMMAND_FAILED",
+ "phase": "server-version query after Unix socket readiness",
+ "roleAndDatabaseScenariosEstablished": false,
+ "correction": "Wait for final TCP server, matching the existing rollback verifier."
+}
diff --git a/apps/web/performance/review-db-successor-20261012/fixture-ownership-protocol-failure-20261013.json b/apps/web/performance/review-db-successor-20261012/fixture-ownership-protocol-failure-20261013.json
new file mode 100644
index 0000000000..6aa7b9c818
--- /dev/null
+++ b/apps/web/performance/review-db-successor-20261012/fixture-ownership-protocol-failure-20261013.json
@@ -0,0 +1,36 @@
+{
+ "schemaVersion": 1,
+ "status": "failed",
+ "code": "LOCAL_REHEARSAL_FIXTURE_ROLE_READBACK_FAILED",
+ "observed": {
+ "actorSuperuser": true,
+ "roles": [
+ {
+ "name": "review_successor_fixture_parent_20261012",
+ "inherit": false,
+ "connlimit": -1,
+ "validuntil": null,
+ "config": null,
+ "markerPresent": true
+ },
+ {
+ "name": "review_successor_fixture_member_20261012",
+ "inherit": true,
+ "connlimit": -1,
+ "validuntil": null,
+ "config": null,
+ "markerPresent": true
+ }
+ ],
+ "memberships": [
+ {
+ "parent": "review_successor_fixture_parent_20261012",
+ "member": "review_successor_fixture_member_20261012",
+ "grantor": "supabase_admin",
+ "admin": false,
+ "inherit": true,
+ "set": true
+ }
+ ]
+ }
+}
diff --git a/apps/web/performance/review-db-successor-20261012/pg17-current-review-fixes-20261014-v1.json b/apps/web/performance/review-db-successor-20261012/pg17-current-review-fixes-20261014-v1.json
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/apps/web/performance/review-db-successor-20261012/pg17-current-review-fixes-20261014-v1.stderr.log b/apps/web/performance/review-db-successor-20261012/pg17-current-review-fixes-20261014-v1.stderr.log
new file mode 100644
index 0000000000..dba3aafb39
--- /dev/null
+++ b/apps/web/performance/review-db-successor-20261012/pg17-current-review-fixes-20261014-v1.stderr.log
@@ -0,0 +1,12 @@
+file:///Users/twoimo/.codex/worktrees/mobile-nav-read-20261012/tzudong/apps/web/performance/review-db-successor-20261012/verify-review-media-sql-successor.mjs:55
+ throw new Error(detail ? `${code}:${detail}` : code);
+ ^
+
+Error: LOCAL_REHEARSAL_ADVISORY_CONTENTION_NOT_OBSERVED
+ at fail (file:///Users/twoimo/.codex/worktrees/mobile-nav-read-20261012/tzudong/apps/web/performance/review-db-successor-20261012/verify-review-media-sql-successor.mjs:55:9)
+ at waitForAdvisoryContention (file:///Users/twoimo/.codex/worktrees/mobile-nav-read-20261012/tzudong/apps/web/performance/review-db-successor-20261012/verify-review-media-sql-successor.mjs:823:3)
+ at async assertContendedExpiryRejected (file:///Users/twoimo/.codex/worktrees/mobile-nav-read-20261012/tzudong/apps/web/performance/review-db-successor-20261012/verify-review-media-sql-successor.mjs:842:34)
+ at async run (file:///Users/twoimo/.codex/worktrees/mobile-nav-read-20261012/tzudong/apps/web/performance/review-db-successor-20261012/verify-review-media-sql-successor.mjs:993:46)
+ at async file:///Users/twoimo/.codex/worktrees/mobile-nav-read-20261012/tzudong/apps/web/performance/review-db-successor-20261012/verify-review-media-sql-successor.mjs:1057:85
+
+Node.js v24.21.0
diff --git a/apps/web/performance/review-db-successor-20261012/pg17-current-review-fixes-20261014-v2.json b/apps/web/performance/review-db-successor-20261012/pg17-current-review-fixes-20261014-v2.json
new file mode 100644
index 0000000000..b7085ca571
--- /dev/null
+++ b/apps/web/performance/review-db-successor-20261012/pg17-current-review-fixes-20261014-v2.json
@@ -0,0 +1 @@
+{"current":{"classification":"current-source-verification","proofSha256":"9a4985689d121979e3d7559c32b8f27c88ae3d1bb82d559fb9bb8ada6e706ac2","bindings":{"manifestSha256":"295b2354c562ed87102c5e256497a2705f90941587fefa7e9ad8a4ba8e40787f","rehearsalHarnessSha256":"7e750e9d0126f6f10afe9056d747a2ea854d462161ea4e8f0168e2751d554c0a","sourceRoot":"61f9d7637c0714a7b0d9108c6f7959ea820175a7870dba681498b570296a1544"},"proof":{"schemaVersion":1,"status":"passed","environment":{"dockerContext":"colima-tzudong-catalog-20261007","dockerContainer":"tzudong-ranking-clone-20261008","engine":"17.6","templateDatabase":"ranking_clone","temporaryDatabase":"review_successor_local_437a74bbfd2f4629d996b901cf050c29"},"input":{"engine":"17.6","ledger":0,"manifest":1963,"profiles":0,"reviews":0},"manifestSha256":"295b2354c562ed87102c5e256497a2705f90941587fefa7e9ad8a4ba8e40787f","rehearsalHarnessSha256":"7e750e9d0126f6f10afe9056d747a2ea854d462161ea4e8f0168e2751d554c0a","sourceRoot":"61f9d7637c0714a7b0d9108c6f7959ea820175a7870dba681498b570296a1544","predecessorSources":[{"path":"backend/supabase/migrations/20261004190259_admin_record_guarded_actions.sql","sha256":"b373b7ea472c0352a33a4d4043cf8d6aa8474c04cc1ca5778805edfa77ac9c95"},{"path":"backend/supabase/migrations/20261004192657_admin_evaluation_raw_warning_groups.sql","sha256":"66eace1d6fb0dd55c1d780776bab855cc37690335ad40b0fdc1294c610e07d3a"},{"path":"backend/supabase/migrations/20261004194715_admin_evaluation_raw_warning_invoker_contract.sql","sha256":"e1c105df82c4f814d3e6adad807ff9d072b8cd42ae770b4b78f75b89a9387020"},{"path":"backend/supabase/migrations/20261009022915_restaurant_review_manual_preview_eligibility.sql","sha256":"8acf6d1428764260ed57dac5fe09711868a82896f0a6d631d502128004c168a3"},{"path":"backend/supabase/migrations/20261009091342_admin_record_private_verification_cleanup.sql","sha256":"b596b200e52c6813a4cfa1b0a2818625f067864549e8854f3497afdcdab706da"},{"path":"backend/supabase/migrations/20261009101645_admin_user_management_rpc_forward.sql","sha256":"2067538f89c9f90d28e784672c7a1288306ba22d5ae92b087c8692503da9b1ae"}],"migrationCount":11,"statementCount":112,"stageStatesSha256":"e62c3e811bceec08cecb1785950e53cb1cd5a22ed7e80df2fdd79b290962ec04","stages":[{"index":0,"ledgerCount":86,"targetCount":0,"prefixRoot":"c975c881e8eb3af88c4b0fc87f1109ac4e1507258f29363de88760f20713dba2","suffixRoot":null,"schemaRoot":"70ef24069d2f6ceeec28db7f715b2bc1e1830e51007df5f97476a64f7583c2be","ledgerRoot":"c975c881e8eb3af88c4b0fc87f1109ac4e1507258f29363de88760f20713dba2","g014Root":"4f3886411c6329ec0a6a61743e7e89de21cf519848bc145be2f3e96f1e685149","storageRoot":"2e4b000d1012f1df1e6e6fd3e19cc66e8718a5be1d8cdc3695677bd2cc3bbda8","finishCleanupBodySha256":null,"ownerAuthorityReady":true,"terminalContractReady":false},{"index":1,"ledgerCount":87,"targetCount":1,"prefixRoot":"c975c881e8eb3af88c4b0fc87f1109ac4e1507258f29363de88760f20713dba2","suffixRoot":null,"schemaRoot":"6660c658b956479a86b85dc5f6c257bdfde7b94d1e6e7e675b989303bc776de4","ledgerRoot":"9dff2943b2fb957d82b7ac325a999fef829c077e5738f178e5bc19da67609701","g014Root":"4f3886411c6329ec0a6a61743e7e89de21cf519848bc145be2f3e96f1e685149","storageRoot":"145e14b4467ccabbbd0d1009df236b3cc734174a9b287811a55a29058257111f","finishCleanupBodySha256":"1a0eb803e2f2ee4098960ad2fbe2b99b363405536c03a09c0465b8a8ffddecc7","ownerAuthorityReady":true,"terminalContractReady":false},{"index":2,"ledgerCount":88,"targetCount":2,"prefixRoot":"c975c881e8eb3af88c4b0fc87f1109ac4e1507258f29363de88760f20713dba2","suffixRoot":null,"schemaRoot":"7574cc0bd42e93dd1112e9068b6538fc2401b5de8d7de3e7ce40762405abbe35","ledgerRoot":"5f3dcac3e6820c2571b2f04221cbdf34ee18085363c434605c445a76ea59061a","g014Root":"4f3886411c6329ec0a6a61743e7e89de21cf519848bc145be2f3e96f1e685149","storageRoot":"98ad8115175e671fce1f853cb48b2e1bb7e430892107fd333bc74df24799cdea","finishCleanupBodySha256":"f7e925a25dedb987a3166c5ca641ba7db1568a7583bdeca3cee04f27d9189f20","ownerAuthorityReady":true,"terminalContractReady":false},{"index":3,"ledgerCount":89,"targetCount":3,"prefixRoot":"c975c881e8eb3af88c4b0fc87f1109ac4e1507258f29363de88760f20713dba2","suffixRoot":null,"schemaRoot":"fe8ca8917b834397b49253154bed7074372bc403ffc2a0e06037a650c7cadfb2","ledgerRoot":"928e46ee03f4f2ed492be89617cf8b455f2931f79fdf3c218adbc92c5fa3d21e","g014Root":"810c5888b257c16ea07881e99914a718eede2e2339cbd805381fe7bf346aef34","storageRoot":"4c1c8a63f3cb8553acfcac1c62c5452ab53568724f662e43291d63037910e34f","finishCleanupBodySha256":"f6c4e71988ed2b789b887484af5fdf2e482bde98f1d1c5b5d4e7ad124c314bd1","ownerAuthorityReady":true,"terminalContractReady":false},{"index":4,"ledgerCount":90,"targetCount":4,"prefixRoot":"c975c881e8eb3af88c4b0fc87f1109ac4e1507258f29363de88760f20713dba2","suffixRoot":null,"schemaRoot":"2085388afbef4445f615ee40e5e00837c526e2f29a2e1c3e0786afe8a569fb22","ledgerRoot":"73b30ad98e4cbace156815cd214091312364302e9403065b24a2d1e86227a2c0","g014Root":"68e78dd7263864584a6b1b4778ef1aa13942b4d54825910fab1101a989025c00","storageRoot":"4c1c8a63f3cb8553acfcac1c62c5452ab53568724f662e43291d63037910e34f","finishCleanupBodySha256":"f6c4e71988ed2b789b887484af5fdf2e482bde98f1d1c5b5d4e7ad124c314bd1","ownerAuthorityReady":true,"terminalContractReady":false},{"index":5,"ledgerCount":91,"targetCount":5,"prefixRoot":"c975c881e8eb3af88c4b0fc87f1109ac4e1507258f29363de88760f20713dba2","suffixRoot":null,"schemaRoot":"0f3f4ed4b7547cc154e5beaeb2621db1c05ff0d4dc7325c47246dcfd4be6fbc5","ledgerRoot":"150bb9f1c5a4b447dd50a86097923fb1c78e522fb5f2bf8ca16b9f08ca6d2b58","g014Root":"68e78dd7263864584a6b1b4778ef1aa13942b4d54825910fab1101a989025c00","storageRoot":"4c1c8a63f3cb8553acfcac1c62c5452ab53568724f662e43291d63037910e34f","finishCleanupBodySha256":"f6c4e71988ed2b789b887484af5fdf2e482bde98f1d1c5b5d4e7ad124c314bd1","ownerAuthorityReady":true,"terminalContractReady":false},{"index":6,"ledgerCount":92,"targetCount":6,"prefixRoot":"c975c881e8eb3af88c4b0fc87f1109ac4e1507258f29363de88760f20713dba2","suffixRoot":null,"schemaRoot":"8aa69a0aa43e60ab1b6680ebb7b3fb4e4f53b07aa8231847883a181375554690","ledgerRoot":"f57304a9d3122d643535464cf72565aa04c450dbc1d8fe0a3a64add17b5c7c97","g014Root":"68e78dd7263864584a6b1b4778ef1aa13942b4d54825910fab1101a989025c00","storageRoot":"4c1c8a63f3cb8553acfcac1c62c5452ab53568724f662e43291d63037910e34f","finishCleanupBodySha256":"f6c4e71988ed2b789b887484af5fdf2e482bde98f1d1c5b5d4e7ad124c314bd1","ownerAuthorityReady":true,"terminalContractReady":false},{"index":7,"ledgerCount":93,"targetCount":7,"prefixRoot":"c975c881e8eb3af88c4b0fc87f1109ac4e1507258f29363de88760f20713dba2","suffixRoot":null,"schemaRoot":"cee1ac952c3bd3aa8fff26a625a4140c5ce2028a08a8510eecdc1e760aaf5166","ledgerRoot":"3da3b76460e0ad7249d50a1f4ddf0cb433edd0ca7b3b6b7a8f6e334974888065","g014Root":"68e78dd7263864584a6b1b4778ef1aa13942b4d54825910fab1101a989025c00","storageRoot":"4c1c8a63f3cb8553acfcac1c62c5452ab53568724f662e43291d63037910e34f","finishCleanupBodySha256":"f6c4e71988ed2b789b887484af5fdf2e482bde98f1d1c5b5d4e7ad124c314bd1","ownerAuthorityReady":true,"terminalContractReady":false},{"index":8,"ledgerCount":94,"targetCount":8,"prefixRoot":"c975c881e8eb3af88c4b0fc87f1109ac4e1507258f29363de88760f20713dba2","suffixRoot":null,"schemaRoot":"ab7f4f4d84c2d759d570669641131f149db2a1a023765fb468e04d7f162348be","ledgerRoot":"c8920bba0db232a705ddf012ee5da809df71d72e3ddfb3f2fbc91d156b87cca2","g014Root":"68e78dd7263864584a6b1b4778ef1aa13942b4d54825910fab1101a989025c00","storageRoot":"4c1c8a63f3cb8553acfcac1c62c5452ab53568724f662e43291d63037910e34f","finishCleanupBodySha256":"f6c4e71988ed2b789b887484af5fdf2e482bde98f1d1c5b5d4e7ad124c314bd1","ownerAuthorityReady":true,"terminalContractReady":false},{"index":9,"ledgerCount":95,"targetCount":9,"prefixRoot":"c975c881e8eb3af88c4b0fc87f1109ac4e1507258f29363de88760f20713dba2","suffixRoot":null,"schemaRoot":"7409744a943050706edaf080c59b069c10bb3b471296728991c589b2ce3d18bb","ledgerRoot":"b6f0aa150d0a5177828767ca5ad31796f6ae4cc034fcd5c943e15cea3d876a9b","g014Root":"68e78dd7263864584a6b1b4778ef1aa13942b4d54825910fab1101a989025c00","storageRoot":"4c1c8a63f3cb8553acfcac1c62c5452ab53568724f662e43291d63037910e34f","finishCleanupBodySha256":"f6c4e71988ed2b789b887484af5fdf2e482bde98f1d1c5b5d4e7ad124c314bd1","ownerAuthorityReady":true,"terminalContractReady":false},{"index":10,"ledgerCount":96,"targetCount":10,"prefixRoot":"c975c881e8eb3af88c4b0fc87f1109ac4e1507258f29363de88760f20713dba2","suffixRoot":null,"schemaRoot":"284d8a28560707a02167e6f9131c2ca4e0658808ace9061b5251d8ff12a236cd","ledgerRoot":"e56731bbe2c7bbdfa826e594d468082c9f16354350f66325e7c58cfdc549fe1a","g014Root":"68e78dd7263864584a6b1b4778ef1aa13942b4d54825910fab1101a989025c00","storageRoot":"6bf06a15d9ef5cc3abbe210657b6a21b3b50f18e0dde58bea9af5ba958b46a60","finishCleanupBodySha256":"f6c4e71988ed2b789b887484af5fdf2e482bde98f1d1c5b5d4e7ad124c314bd1","ownerAuthorityReady":true,"terminalContractReady":false},{"index":11,"ledgerCount":97,"targetCount":11,"prefixRoot":"c975c881e8eb3af88c4b0fc87f1109ac4e1507258f29363de88760f20713dba2","suffixRoot":"1ecd299cb2525a1d66738d2c0adf01a482e47c44f881f6d09ddc2141d469d32b","schemaRoot":"a255fb9f415627e139762649c4e4230c4f6c56e541be3a09d9f986120287ea7e","ledgerRoot":"fa142da48d834923b4702a302d44ea0f9818d7713da76a316d5959b8852065f2","g014Root":"68e78dd7263864584a6b1b4778ef1aa13942b4d54825910fab1101a989025c00","storageRoot":"6bf06a15d9ef5cc3abbe210657b6a21b3b50f18e0dde58bea9af5ba958b46a60","finishCleanupBodySha256":"a6b4211b2d8486d482f0758ce249eec157249e1a5176be286b76af7e7a775ee9","ownerAuthorityReady":true,"terminalContractReady":true}],"results":{"advisoryContentionObserved":true,"atomicCommit":true,"atomicCommitOutcome":"acknowledged","atomicRollbackExact":true,"catalogDriftStatePreserved":true,"columnAclDriftRejected":true,"compiledSqlBytes":6495623,"compiledSqlSha256":"267a09fdcb24ce8183425dfb847281a2fea9e04226aa94de01b13f3544b353fd","contendedAdmissionExpiryRejected":true,"contendedAdmissionExpiryStatePreserved":true,"membershipOptionsBaseline":{"inherit":true,"set":true},"membershipInheritOptionDriftRejected":true,"membershipSetOptionDriftRejected":true,"securityInvokerBaseline":true,"securityInvokerDriftRejected":true,"wrongFinishPreimageRejected":true,"wrongFinishPreimageRollbackExact":true,"terminalLedgerCount":97,"terminalContractReady":true},"sourceMutation":false,"hostedWrites":false,"operatingWrites":false,"temporaryDatabaseDropped":true,"fixtureRolesDropped":true}},"retained":{"present":true,"classification":"retained-historical-evidence","repositoryPath":"apps/web/performance/review-db-successor-20261012/pg17-local-replay.json","expectedSha256":"6f5dc5c5603c8a4d8b601d3edb28009e454e955292a65707747e66f274557b3f","actualSha256":"6f5dc5c5603c8a4d8b601d3edb28009e454e955292a65707747e66f274557b3f","checksumMapSha256":"d19614c3478ddbe90c38b856ebcbdba7a3a4dc9599ce259c2eabd60dcb0337a6","bindings":{"manifestSha256":"9fac81d6e3ec81207b9c8985ccfdaf9e300bb7d44a7617d95579d4e8f96af19b","rehearsalHarnessSha256":"03e999caeda7b57925126ba8d6bcec4ac5a671fedb2c69e07e988924ccb9f895","sourceRoot":"61f9d7637c0714a7b0d9108c6f7959ea820175a7870dba681498b570296a1544"},"currentSourceMatch":false,"bindingDifferences":[{"field":"manifestSha256","retained":"9fac81d6e3ec81207b9c8985ccfdaf9e300bb7d44a7617d95579d4e8f96af19b","current":"295b2354c562ed87102c5e256497a2705f90941587fefa7e9ad8a4ba8e40787f"},{"field":"rehearsalHarnessSha256","retained":"03e999caeda7b57925126ba8d6bcec4ac5a671fedb2c69e07e988924ccb9f895","current":"7e750e9d0126f6f10afe9056d747a2ea854d462161ea4e8f0168e2751d554c0a"}],"usedAsCurrentProof":false}}
diff --git a/apps/web/performance/review-db-successor-20261012/pg17-current-review-fixes-20261014-v2.stderr.log b/apps/web/performance/review-db-successor-20261012/pg17-current-review-fixes-20261014-v2.stderr.log
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/apps/web/performance/review-db-successor-20261012/pg17-fixture-ownership-20261013.json b/apps/web/performance/review-db-successor-20261012/pg17-fixture-ownership-20261013.json
new file mode 100644
index 0000000000..6b3f05be4c
--- /dev/null
+++ b/apps/web/performance/review-db-successor-20261012/pg17-fixture-ownership-20261013.json
@@ -0,0 +1,60 @@
+{
+ "schemaVersion": 1,
+ "kind": "isolated-fixture-ownership-verification",
+ "status": "passed",
+ "recordedAtUTC": "2026-10-13T07:18:48.134Z",
+ "sourceSha256": "8c3d7df52c281a9df1d6a4bd2acefb59f9a23b60185559fc6a8298cd65fde370",
+ "verifierSha256": "14af945a8e2d5d64a33bc6917da06fc6325a8c13af5e948d03c91e7af319d870",
+ "environment": {
+ "engine": "17.6",
+ "image": "supabase/postgres@sha256:fbe6c858a2ea9616aead3c39e132afcc0660decc5cb7dc656208ed145f5331ab"
+ },
+ "results": [
+ {
+ "kind": "roles",
+ "mode": "normal",
+ "createAttempts": 1,
+ "dropAttempts": 1,
+ "cleaned": true
+ },
+ {
+ "kind": "roles",
+ "mode": "lost-create-and-drop-ack",
+ "createAttempts": 1,
+ "dropAttempts": 1,
+ "cleaned": true
+ },
+ {
+ "kind": "roles",
+ "mode": "lost-create-ack-and-first-readback",
+ "createAttempts": 1,
+ "dropAttempts": 1,
+ "cleaned": true
+ },
+ {
+ "kind": "database",
+ "mode": "normal",
+ "createAttempts": 1,
+ "dropAttempts": 1,
+ "cleaned": true
+ },
+ {
+ "kind": "database",
+ "mode": "lost-create-ack",
+ "createAttempts": 1,
+ "dropAttempts": 1,
+ "cleaned": true
+ },
+ {
+ "kind": "database",
+ "mode": "lost-create-ack-and-first-readback",
+ "createAttempts": 1,
+ "dropAttempts": 1,
+ "cleaned": true
+ }
+ ],
+ "temporaryContainerRemoved": true,
+ "sourceContainerMutation": false,
+ "hostedWrites": false,
+ "predecessorAndFixtureSqlExercised": false
+}
diff --git a/apps/web/performance/review-db-successor-20261012/pg17-fixture-ownership-current-20261013.json b/apps/web/performance/review-db-successor-20261012/pg17-fixture-ownership-current-20261013.json
new file mode 100644
index 0000000000..0d0a7759c0
--- /dev/null
+++ b/apps/web/performance/review-db-successor-20261012/pg17-fixture-ownership-current-20261013.json
@@ -0,0 +1,60 @@
+{
+ "schemaVersion": 1,
+ "kind": "isolated-fixture-ownership-verification",
+ "status": "passed",
+ "recordedAtUTC": "2026-10-13T07:39:57.597Z",
+ "sourceSha256": "8c3d7df52c281a9df1d6a4bd2acefb59f9a23b60185559fc6a8298cd65fde370",
+ "verifierSha256": "7f6ded6fa4078fb0239b1c4d8cefe87dd8407c174e747ef118c2c98a1b481d9e",
+ "environment": {
+ "engine": "17.6",
+ "image": "supabase/postgres@sha256:fbe6c858a2ea9616aead3c39e132afcc0660decc5cb7dc656208ed145f5331ab"
+ },
+ "results": [
+ {
+ "kind": "roles",
+ "mode": "normal",
+ "createAttempts": 1,
+ "dropAttempts": 1,
+ "cleaned": true
+ },
+ {
+ "kind": "roles",
+ "mode": "lost-create-and-drop-ack",
+ "createAttempts": 1,
+ "dropAttempts": 1,
+ "cleaned": true
+ },
+ {
+ "kind": "roles",
+ "mode": "lost-create-ack-and-first-readback",
+ "createAttempts": 1,
+ "dropAttempts": 1,
+ "cleaned": true
+ },
+ {
+ "kind": "database",
+ "mode": "normal",
+ "createAttempts": 1,
+ "dropAttempts": 1,
+ "cleaned": true
+ },
+ {
+ "kind": "database",
+ "mode": "lost-create-ack",
+ "createAttempts": 1,
+ "dropAttempts": 1,
+ "cleaned": true
+ },
+ {
+ "kind": "database",
+ "mode": "lost-create-ack-and-first-readback",
+ "createAttempts": 1,
+ "dropAttempts": 1,
+ "cleaned": true
+ }
+ ],
+ "temporaryContainerRemoved": true,
+ "sourceContainerMutation": false,
+ "hostedWrites": false,
+ "predecessorAndFixtureSqlExercised": false
+}
diff --git a/apps/web/performance/review-db-successor-20261012/pg17-fixture-ownership-reviewed-20261013.json b/apps/web/performance/review-db-successor-20261012/pg17-fixture-ownership-reviewed-20261013.json
new file mode 100644
index 0000000000..68409043f7
--- /dev/null
+++ b/apps/web/performance/review-db-successor-20261012/pg17-fixture-ownership-reviewed-20261013.json
@@ -0,0 +1,61 @@
+{
+ "schemaVersion": 1,
+ "kind": "isolated-fixture-ownership-verification",
+ "status": "passed",
+ "recordedAtUTC": "2026-10-13T08:25:50.698Z",
+ "sourceSha256": "768b8887a30c2b8f496a73a21d373b85a0b64fadaed3a1485034a729ee704d39",
+ "containerHelperSha256": "f7321f607a5bf3a24ba099d51abc67254db52fcc385945692426d1b2dfa6e431",
+ "verifierSha256": "41972cf6c4ac2b819b42cf9f06b762d54fb9d161f4594192492fccf79cca7418",
+ "environment": {
+ "engine": "17.6",
+ "image": "supabase/postgres@sha256:fbe6c858a2ea9616aead3c39e132afcc0660decc5cb7dc656208ed145f5331ab"
+ },
+ "results": [
+ {
+ "kind": "roles",
+ "mode": "normal",
+ "createAttempts": 1,
+ "dropAttempts": 1,
+ "cleaned": true
+ },
+ {
+ "kind": "roles",
+ "mode": "lost-create-and-drop-ack",
+ "createAttempts": 1,
+ "dropAttempts": 1,
+ "cleaned": true
+ },
+ {
+ "kind": "roles",
+ "mode": "lost-create-ack-and-first-readback",
+ "createAttempts": 1,
+ "dropAttempts": 1,
+ "cleaned": true
+ },
+ {
+ "kind": "database",
+ "mode": "normal",
+ "createAttempts": 1,
+ "dropAttempts": 1,
+ "cleaned": true
+ },
+ {
+ "kind": "database",
+ "mode": "lost-create-ack",
+ "createAttempts": 1,
+ "dropAttempts": 1,
+ "cleaned": true
+ },
+ {
+ "kind": "database",
+ "mode": "lost-create-ack-and-first-readback",
+ "createAttempts": 1,
+ "dropAttempts": 1,
+ "cleaned": true
+ }
+ ],
+ "temporaryContainerRemoved": true,
+ "sourceContainerMutation": false,
+ "hostedWrites": false,
+ "predecessorAndFixtureSqlExercised": false
+}
diff --git a/apps/web/performance/review-db-successor-20261012/pg17-local-replay.json b/apps/web/performance/review-db-successor-20261012/pg17-local-replay.json
new file mode 100644
index 0000000000..66fadc8fda
--- /dev/null
+++ b/apps/web/performance/review-db-successor-20261012/pg17-local-replay.json
@@ -0,0 +1,247 @@
+{
+ "schemaVersion": 1,
+ "status": "passed",
+ "environment": {
+ "dockerContext": "colima-tzudong-catalog-20261007",
+ "dockerContainer": "tzudong-ranking-clone-20261008",
+ "engine": "17.6",
+ "templateDatabase": "ranking_clone",
+ "temporaryDatabase": "review_successor_local_20261012_v1"
+ },
+ "input": {
+ "engine": "17.6",
+ "ledger": 0,
+ "manifest": 1963,
+ "profiles": 0,
+ "reviews": 0
+ },
+ "manifestSha256": "9fac81d6e3ec81207b9c8985ccfdaf9e300bb7d44a7617d95579d4e8f96af19b",
+ "rehearsalHarnessSha256": "03e999caeda7b57925126ba8d6bcec4ac5a671fedb2c69e07e988924ccb9f895",
+ "sourceRoot": "61f9d7637c0714a7b0d9108c6f7959ea820175a7870dba681498b570296a1544",
+ "predecessorSources": [
+ {
+ "path": "backend/supabase/migrations/20261004190259_admin_record_guarded_actions.sql",
+ "sha256": "b373b7ea472c0352a33a4d4043cf8d6aa8474c04cc1ca5778805edfa77ac9c95"
+ },
+ {
+ "path": "backend/supabase/migrations/20261004192657_admin_evaluation_raw_warning_groups.sql",
+ "sha256": "66eace1d6fb0dd55c1d780776bab855cc37690335ad40b0fdc1294c610e07d3a"
+ },
+ {
+ "path": "backend/supabase/migrations/20261004194715_admin_evaluation_raw_warning_invoker_contract.sql",
+ "sha256": "e1c105df82c4f814d3e6adad807ff9d072b8cd42ae770b4b78f75b89a9387020"
+ },
+ {
+ "path": "backend/supabase/migrations/20261009022915_restaurant_review_manual_preview_eligibility.sql",
+ "sha256": "8acf6d1428764260ed57dac5fe09711868a82896f0a6d631d502128004c168a3"
+ },
+ {
+ "path": "backend/supabase/migrations/20261009091342_admin_record_private_verification_cleanup.sql",
+ "sha256": "b596b200e52c6813a4cfa1b0a2818625f067864549e8854f3497afdcdab706da"
+ },
+ {
+ "path": "backend/supabase/migrations/20261009101645_admin_user_management_rpc_forward.sql",
+ "sha256": "2067538f89c9f90d28e784672c7a1288306ba22d5ae92b087c8692503da9b1ae"
+ }
+ ],
+ "migrationCount": 11,
+ "statementCount": 112,
+ "stageStatesSha256": "9b6ab856ce82bfba9b8eb52fdf40943eac645b5d52af6ea9c4c25bcd4b82ad23",
+ "stages": [
+ {
+ "index": 0,
+ "ledgerCount": 86,
+ "targetCount": 0,
+ "prefixRoot": "c975c881e8eb3af88c4b0fc87f1109ac4e1507258f29363de88760f20713dba2",
+ "suffixRoot": null,
+ "schemaRoot": "70ef24069d2f6ceeec28db7f715b2bc1e1830e51007df5f97476a64f7583c2be",
+ "ledgerRoot": "c975c881e8eb3af88c4b0fc87f1109ac4e1507258f29363de88760f20713dba2",
+ "g014Root": "4f3886411c6329ec0a6a61743e7e89de21cf519848bc145be2f3e96f1e685149",
+ "storageRoot": "2e4b000d1012f1df1e6e6fd3e19cc66e8718a5be1d8cdc3695677bd2cc3bbda8",
+ "finishCleanupBodySha256": null,
+ "ownerAuthorityReady": true,
+ "terminalContractReady": false
+ },
+ {
+ "index": 1,
+ "ledgerCount": 87,
+ "targetCount": 1,
+ "prefixRoot": "c975c881e8eb3af88c4b0fc87f1109ac4e1507258f29363de88760f20713dba2",
+ "suffixRoot": null,
+ "schemaRoot": "6660c658b956479a86b85dc5f6c257bdfde7b94d1e6e7e675b989303bc776de4",
+ "ledgerRoot": "9dff2943b2fb957d82b7ac325a999fef829c077e5738f178e5bc19da67609701",
+ "g014Root": "4f3886411c6329ec0a6a61743e7e89de21cf519848bc145be2f3e96f1e685149",
+ "storageRoot": "145e14b4467ccabbbd0d1009df236b3cc734174a9b287811a55a29058257111f",
+ "finishCleanupBodySha256": "1a0eb803e2f2ee4098960ad2fbe2b99b363405536c03a09c0465b8a8ffddecc7",
+ "ownerAuthorityReady": true,
+ "terminalContractReady": false
+ },
+ {
+ "index": 2,
+ "ledgerCount": 88,
+ "targetCount": 2,
+ "prefixRoot": "c975c881e8eb3af88c4b0fc87f1109ac4e1507258f29363de88760f20713dba2",
+ "suffixRoot": null,
+ "schemaRoot": "7574cc0bd42e93dd1112e9068b6538fc2401b5de8d7de3e7ce40762405abbe35",
+ "ledgerRoot": "5f3dcac3e6820c2571b2f04221cbdf34ee18085363c434605c445a76ea59061a",
+ "g014Root": "4f3886411c6329ec0a6a61743e7e89de21cf519848bc145be2f3e96f1e685149",
+ "storageRoot": "98ad8115175e671fce1f853cb48b2e1bb7e430892107fd333bc74df24799cdea",
+ "finishCleanupBodySha256": "f7e925a25dedb987a3166c5ca641ba7db1568a7583bdeca3cee04f27d9189f20",
+ "ownerAuthorityReady": true,
+ "terminalContractReady": false
+ },
+ {
+ "index": 3,
+ "ledgerCount": 89,
+ "targetCount": 3,
+ "prefixRoot": "c975c881e8eb3af88c4b0fc87f1109ac4e1507258f29363de88760f20713dba2",
+ "suffixRoot": null,
+ "schemaRoot": "fe8ca8917b834397b49253154bed7074372bc403ffc2a0e06037a650c7cadfb2",
+ "ledgerRoot": "928e46ee03f4f2ed492be89617cf8b455f2931f79fdf3c218adbc92c5fa3d21e",
+ "g014Root": "810c5888b257c16ea07881e99914a718eede2e2339cbd805381fe7bf346aef34",
+ "storageRoot": "4c1c8a63f3cb8553acfcac1c62c5452ab53568724f662e43291d63037910e34f",
+ "finishCleanupBodySha256": "f6c4e71988ed2b789b887484af5fdf2e482bde98f1d1c5b5d4e7ad124c314bd1",
+ "ownerAuthorityReady": true,
+ "terminalContractReady": false
+ },
+ {
+ "index": 4,
+ "ledgerCount": 90,
+ "targetCount": 4,
+ "prefixRoot": "c975c881e8eb3af88c4b0fc87f1109ac4e1507258f29363de88760f20713dba2",
+ "suffixRoot": null,
+ "schemaRoot": "2085388afbef4445f615ee40e5e00837c526e2f29a2e1c3e0786afe8a569fb22",
+ "ledgerRoot": "73b30ad98e4cbace156815cd214091312364302e9403065b24a2d1e86227a2c0",
+ "g014Root": "68e78dd7263864584a6b1b4778ef1aa13942b4d54825910fab1101a989025c00",
+ "storageRoot": "4c1c8a63f3cb8553acfcac1c62c5452ab53568724f662e43291d63037910e34f",
+ "finishCleanupBodySha256": "f6c4e71988ed2b789b887484af5fdf2e482bde98f1d1c5b5d4e7ad124c314bd1",
+ "ownerAuthorityReady": true,
+ "terminalContractReady": false
+ },
+ {
+ "index": 5,
+ "ledgerCount": 91,
+ "targetCount": 5,
+ "prefixRoot": "c975c881e8eb3af88c4b0fc87f1109ac4e1507258f29363de88760f20713dba2",
+ "suffixRoot": null,
+ "schemaRoot": "0f3f4ed4b7547cc154e5beaeb2621db1c05ff0d4dc7325c47246dcfd4be6fbc5",
+ "ledgerRoot": "150bb9f1c5a4b447dd50a86097923fb1c78e522fb5f2bf8ca16b9f08ca6d2b58",
+ "g014Root": "68e78dd7263864584a6b1b4778ef1aa13942b4d54825910fab1101a989025c00",
+ "storageRoot": "4c1c8a63f3cb8553acfcac1c62c5452ab53568724f662e43291d63037910e34f",
+ "finishCleanupBodySha256": "f6c4e71988ed2b789b887484af5fdf2e482bde98f1d1c5b5d4e7ad124c314bd1",
+ "ownerAuthorityReady": true,
+ "terminalContractReady": false
+ },
+ {
+ "index": 6,
+ "ledgerCount": 92,
+ "targetCount": 6,
+ "prefixRoot": "c975c881e8eb3af88c4b0fc87f1109ac4e1507258f29363de88760f20713dba2",
+ "suffixRoot": null,
+ "schemaRoot": "8aa69a0aa43e60ab1b6680ebb7b3fb4e4f53b07aa8231847883a181375554690",
+ "ledgerRoot": "f57304a9d3122d643535464cf72565aa04c450dbc1d8fe0a3a64add17b5c7c97",
+ "g014Root": "68e78dd7263864584a6b1b4778ef1aa13942b4d54825910fab1101a989025c00",
+ "storageRoot": "4c1c8a63f3cb8553acfcac1c62c5452ab53568724f662e43291d63037910e34f",
+ "finishCleanupBodySha256": "f6c4e71988ed2b789b887484af5fdf2e482bde98f1d1c5b5d4e7ad124c314bd1",
+ "ownerAuthorityReady": true,
+ "terminalContractReady": false
+ },
+ {
+ "index": 7,
+ "ledgerCount": 93,
+ "targetCount": 7,
+ "prefixRoot": "c975c881e8eb3af88c4b0fc87f1109ac4e1507258f29363de88760f20713dba2",
+ "suffixRoot": null,
+ "schemaRoot": "cee1ac952c3bd3aa8fff26a625a4140c5ce2028a08a8510eecdc1e760aaf5166",
+ "ledgerRoot": "3da3b76460e0ad7249d50a1f4ddf0cb433edd0ca7b3b6b7a8f6e334974888065",
+ "g014Root": "68e78dd7263864584a6b1b4778ef1aa13942b4d54825910fab1101a989025c00",
+ "storageRoot": "4c1c8a63f3cb8553acfcac1c62c5452ab53568724f662e43291d63037910e34f",
+ "finishCleanupBodySha256": "f6c4e71988ed2b789b887484af5fdf2e482bde98f1d1c5b5d4e7ad124c314bd1",
+ "ownerAuthorityReady": true,
+ "terminalContractReady": false
+ },
+ {
+ "index": 8,
+ "ledgerCount": 94,
+ "targetCount": 8,
+ "prefixRoot": "c975c881e8eb3af88c4b0fc87f1109ac4e1507258f29363de88760f20713dba2",
+ "suffixRoot": null,
+ "schemaRoot": "ab7f4f4d84c2d759d570669641131f149db2a1a023765fb468e04d7f162348be",
+ "ledgerRoot": "c8920bba0db232a705ddf012ee5da809df71d72e3ddfb3f2fbc91d156b87cca2",
+ "g014Root": "68e78dd7263864584a6b1b4778ef1aa13942b4d54825910fab1101a989025c00",
+ "storageRoot": "4c1c8a63f3cb8553acfcac1c62c5452ab53568724f662e43291d63037910e34f",
+ "finishCleanupBodySha256": "f6c4e71988ed2b789b887484af5fdf2e482bde98f1d1c5b5d4e7ad124c314bd1",
+ "ownerAuthorityReady": true,
+ "terminalContractReady": false
+ },
+ {
+ "index": 9,
+ "ledgerCount": 95,
+ "targetCount": 9,
+ "prefixRoot": "c975c881e8eb3af88c4b0fc87f1109ac4e1507258f29363de88760f20713dba2",
+ "suffixRoot": null,
+ "schemaRoot": "7409744a943050706edaf080c59b069c10bb3b471296728991c589b2ce3d18bb",
+ "ledgerRoot": "b6f0aa150d0a5177828767ca5ad31796f6ae4cc034fcd5c943e15cea3d876a9b",
+ "g014Root": "68e78dd7263864584a6b1b4778ef1aa13942b4d54825910fab1101a989025c00",
+ "storageRoot": "4c1c8a63f3cb8553acfcac1c62c5452ab53568724f662e43291d63037910e34f",
+ "finishCleanupBodySha256": "f6c4e71988ed2b789b887484af5fdf2e482bde98f1d1c5b5d4e7ad124c314bd1",
+ "ownerAuthorityReady": true,
+ "terminalContractReady": false
+ },
+ {
+ "index": 10,
+ "ledgerCount": 96,
+ "targetCount": 10,
+ "prefixRoot": "c975c881e8eb3af88c4b0fc87f1109ac4e1507258f29363de88760f20713dba2",
+ "suffixRoot": null,
+ "schemaRoot": "284d8a28560707a02167e6f9131c2ca4e0658808ace9061b5251d8ff12a236cd",
+ "ledgerRoot": "e56731bbe2c7bbdfa826e594d468082c9f16354350f66325e7c58cfdc549fe1a",
+ "g014Root": "68e78dd7263864584a6b1b4778ef1aa13942b4d54825910fab1101a989025c00",
+ "storageRoot": "6bf06a15d9ef5cc3abbe210657b6a21b3b50f18e0dde58bea9af5ba958b46a60",
+ "finishCleanupBodySha256": "f6c4e71988ed2b789b887484af5fdf2e482bde98f1d1c5b5d4e7ad124c314bd1",
+ "ownerAuthorityReady": true,
+ "terminalContractReady": false
+ },
+ {
+ "index": 11,
+ "ledgerCount": 97,
+ "targetCount": 11,
+ "prefixRoot": "c975c881e8eb3af88c4b0fc87f1109ac4e1507258f29363de88760f20713dba2",
+ "suffixRoot": "1ecd299cb2525a1d66738d2c0adf01a482e47c44f881f6d09ddc2141d469d32b",
+ "schemaRoot": "a255fb9f415627e139762649c4e4230c4f6c56e541be3a09d9f986120287ea7e",
+ "ledgerRoot": "fa142da48d834923b4702a302d44ea0f9818d7713da76a316d5959b8852065f2",
+ "g014Root": "68e78dd7263864584a6b1b4778ef1aa13942b4d54825910fab1101a989025c00",
+ "storageRoot": "6bf06a15d9ef5cc3abbe210657b6a21b3b50f18e0dde58bea9af5ba958b46a60",
+ "finishCleanupBodySha256": "a6b4211b2d8486d482f0758ce249eec157249e1a5176be286b76af7e7a775ee9",
+ "ownerAuthorityReady": true,
+ "terminalContractReady": true
+ }
+ ],
+ "results": {
+ "advisoryContentionObserved": true,
+ "atomicCommit": true,
+ "atomicRollbackExact": true,
+ "catalogDriftStatePreserved": true,
+ "columnAclDriftRejected": true,
+ "compiledSqlBytes": 6492122,
+ "compiledSqlSha256": "706084abff22426a012f11ccbc08f7b1bb7d7c16b2f961cf2d04ac2b7914e142",
+ "contendedAdmissionExpiryRejected": true,
+ "contendedAdmissionExpiryStatePreserved": true,
+ "membershipOptionsBaseline": {
+ "inherit": true,
+ "set": true
+ },
+ "membershipInheritOptionDriftRejected": true,
+ "membershipSetOptionDriftRejected": true,
+ "securityInvokerBaseline": true,
+ "securityInvokerDriftRejected": true,
+ "wrongFinishPreimageRejected": true,
+ "wrongFinishPreimageRollbackExact": true,
+ "terminalLedgerCount": 97,
+ "terminalContractReady": true
+ },
+ "sourceMutation": false,
+ "hostedWrites": false,
+ "operatingWrites": false,
+ "temporaryDatabaseDropped": true
+}
diff --git a/apps/web/performance/review-db-successor-20261012/review-fix-harness-units-20261014-v2.log b/apps/web/performance/review-db-successor-20261012/review-fix-harness-units-20261014-v2.log
new file mode 100644
index 0000000000..30a3f294b0
--- /dev/null
+++ b/apps/web/performance/review-db-successor-20261012/review-fix-harness-units-20261014-v2.log
@@ -0,0 +1,28 @@
+bun test v1.4.0 (34cbb9a40)
+
+tests-unit/review-media-sql-successor-harness.test.ts:
+(pass) fixture observes delayed advisory contention before expiry without the old 700ms cutoff [0.18ms]
+(pass) fixture rejects contention returned by a slow observation after expiry [0.22ms]
+(pass) successor acknowledgement loss reconciles a committed terminal state without SQL replay [0.24ms]
+(pass) successor failure at the baseline is classified without diagnostic SQL replay [0.06ms]
+(pass) retained proof is checksum-verified and distinguished from the current harness source [1.73ms]
+(pass) retained proof with bytes not authorized by SHA256SUMS is rejected [1.26ms]
+(pass) jointly altered retained proof and checksum map are rejected by the independent digest [1.29ms]
+(pass) owned database is cleaned after predecessor SQL fails [0.55ms]
+(pass) owned database is cleaned after fixture SQL fails [0.05ms]
+(pass) pre-existing database is never acquired or dropped [0.06ms]
+(pass) database CREATE acknowledgement loss is reconciled without replay and cleaned by exact ownership [0.16ms]
+(pass) cleanup re-reads and removes the exact owned database after the first CREATE readback fails [0.10ms]
+(pass) cleanup refuses a database whose exact owner metadata does not match [0.12ms]
+(pass) fixture role creation acknowledgement loss is reconciled and exact owned roles are cleaned [0.38ms]
+(pass) cleanup re-reads exact owned roles when the first post-create readback fails [0.10ms]
+(pass) fixture role creation failure without owned readback never authorizes cleanup [0.05ms]
+(pass) pre-existing fixture role collision is preserved and never acquired for cleanup [0.06ms]
+(pass) uncertain create collision with another owner is not guessed or removed [0.05ms]
+(pass) cleanup refuses roles whose ownership marker changed after creation [0.26ms]
+(pass) cleanup refuses to remove owned roles after an out-of-scope membership is added [0.07ms]
+
+ 20 pass
+ 0 fail
+ 98 expect() calls
+Ran 20 tests across 1 file. [161.00ms]
diff --git a/apps/web/performance/review-db-successor-20261012/verify-fixture-ownership.mjs b/apps/web/performance/review-db-successor-20261012/verify-fixture-ownership.mjs
new file mode 100644
index 0000000000..8b6a58d21d
--- /dev/null
+++ b/apps/web/performance/review-db-successor-20261012/verify-fixture-ownership.mjs
@@ -0,0 +1,180 @@
+#!/usr/bin/env node
+/** Exercise fixture ownership on a new, labelled PostgreSQL 17.6 container. */
+import { spawnSync } from 'node:child_process';
+import { createHash } from 'node:crypto';
+import { readFileSync, existsSync, writeFileSync } from 'node:fs';
+import { dirname, resolve } from 'node:path';
+import { fileURLToPath } from 'node:url';
+import { createOwnedContainer, removeOwnedContainer } from '../review-db-rehearsal-20261012/verify-review-media-sql-rehearsal.mjs';
+import { createFixtureMembership, dropFixtureMembership, createOwnedDatabase, dropOwnedDatabase } from './verify-review-media-sql-successor.mjs';
+
+const here = dirname(fileURLToPath(import.meta.url));
+const root = resolve(here, '../../../..');
+const context = process.env.TZUDONG_REHEARSAL_DOCKER_CONTEXT || 'colima-tzudong-catalog-20261007';
+const hash = bytes => createHash('sha256').update(bytes).digest('hex');
+const bindingPaths = Object.freeze({
+ sourceSha256: resolve(here, 'verify-review-media-sql-successor.mjs'),
+ containerHelperSha256: resolve(here, '../review-db-rehearsal-20261012/verify-review-media-sql-rehearsal.mjs'),
+ verifierSha256: fileURLToPath(import.meta.url),
+});
+
+export function fixtureOwnershipBindings({ readFileImpl = readFileSync } = {}) {
+ return Object.freeze(Object.fromEntries(Object.entries(bindingPaths)
+ .map(([key, path]) => [key, hash(readFileImpl(path))])));
+}
+
+export function assertFixtureOwnershipBindings(expected, dependencies = {}) {
+ const actual = fixtureOwnershipBindings(dependencies);
+ if (Object.keys(bindingPaths).some(key => expected?.[key] !== actual[key])) {
+ throw Object.assign(new Error('FIXTURE_OWNERSHIP_SOURCE_CHANGED'), {
+ code: 'FIXTURE_OWNERSHIP_SOURCE_CHANGED',
+ });
+ }
+ return actual;
+}
+
+let password;
+const fail = code => { throw Object.assign(new Error(code), { code }); };
+const check = value => { if (!value) fail('FIXTURE_OWNERSHIP_ASSERTION_FAILED'); };
+
+function docker(args, options = {}) {
+ if (args[0] === 'run') password = args.find(value => value.startsWith('POSTGRES_PASSWORD='))?.slice(18);
+ const result = spawnSync('docker', ['--context', context, ...args], {
+ input: options.input, encoding: 'utf8', maxBuffer: 4 * 1024 * 1024,
+ });
+ if (!options.allowFailure && (result.error || result.status !== 0)) fail('FIXTURE_OWNERSHIP_COMMAND_FAILED');
+ return result;
+}
+
+const container = { created: false, id: null, name: null, token: null };
+function sql(database, statement, actor = 'supabase_admin') {
+ return docker(['exec', '-i', '-e', `PGPASSWORD=${password}`, container.id,
+ 'psql', '-XAtq', '-v', 'ON_ERROR_STOP=1', '-h', '127.0.0.1', '-U', actor, '-d', database, '--file=-'],
+ { input: String(statement) }).stdout.trim();
+}
+
+export function runFixtureOwnershipVerification(args = process.argv.slice(2)) {
+ if (args.length !== 0 && (args.length !== 2 || args[0] !== '--output'
+ || !args[1] || args[1].startsWith('--'))) fail('FIXTURE_OWNERSHIP_ARGUMENT_INVALID');
+ const output = args.length === 0
+ ? resolve(here, 'pg17-fixture-ownership-20261013.json') : resolve(args[1]);
+ const sourceBindings = fixtureOwnershipBindings();
+ const results = [];
+ try {
+ if (existsSync(output)) fail('FIXTURE_OWNERSHIP_OUTPUT_EXISTS');
+ createOwnedContainer(container, { dockerImpl: docker });
+ let ready = false;
+ for (let attempt = 0; attempt < 60; attempt += 1) {
+ const result = docker(['exec', '-e', `PGPASSWORD=${password}`, container.id,
+ 'pg_isready', '-h', '127.0.0.1', '-U', 'postgres', '-d', 'postgres'], { allowFailure: true });
+ if (result.status === 0) { ready = true; break; }
+ Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, 250);
+ }
+ check(ready && sql('postgres', 'SHOW server_version;').startsWith('17.6'));
+
+ for (const mode of ['normal', 'lost-create-and-drop-ack', 'lost-create-ack-and-first-readback']) {
+ let created = false;
+ let lostReadback = false;
+ let createAttempts = 0;
+ let dropAttempts = 0;
+ const ownership = { database: false, fixtureMembership: false, fixtureMembershipAttempted: false,
+ fixtureMembershipToken: null, fixtureMembershipBaseline: null };
+ const dependencies = {
+ scalarImpl: (database, statement, actor) => {
+ if (mode.endsWith('first-readback') && created && !lostReadback) {
+ lostReadback = true; fail('FIXTURE_FAULT_READBACK_LOSS');
+ }
+ return sql(database, statement, actor);
+ },
+ psqlImpl: (database, statement, options) => {
+ const text = String(statement);
+ sql(database, text, options.actor);
+ if (text.includes('CREATE ROLE')) {
+ created = true; createAttempts += 1;
+ if (mode !== 'normal') fail('FIXTURE_FAULT_CREATE_ACK_LOSS');
+ } else if (text.includes('DROP ROLE')) {
+ dropAttempts += 1;
+ if (mode.includes('drop-ack')) fail('FIXTURE_FAULT_DROP_ACK_LOSS');
+ }
+ },
+ };
+ try { createFixtureMembership(ownership, dependencies); }
+ catch (error) {
+ if (!(mode.endsWith('first-readback') && lostReadback
+ && error.message === 'LOCAL_REHEARSAL_FIXTURE_ROLE_READBACK_FAILED')) {
+ const code = /^[A-Z][A-Z0-9_]{3,96}/.exec(String(error.message))?.[0] || 'UNKNOWN';
+ const observed = JSON.parse(sql('postgres', `SELECT json_build_object(
+ 'actorSuperuser',(SELECT rolsuper FROM pg_roles WHERE rolname='supabase_admin'),
+ 'roles',(SELECT json_agg(json_build_object('name',rolname,'inherit',rolinherit,
+ 'connlimit',rolconnlimit,'validuntil',rolvaliduntil,'config',rolconfig,
+ 'markerPresent',shobj_description(oid,'pg_authid') IS NOT NULL)) FROM pg_roles
+ WHERE rolname IN('review_successor_fixture_parent_20261012','review_successor_fixture_member_20261012')),
+ 'memberships',(SELECT json_agg(json_build_object('parent',p.rolname,'member',m.rolname,
+ 'grantor',g.rolname,'admin',a.admin_option,'inherit',a.inherit_option,'set',a.set_option))
+ FROM pg_auth_members a JOIN pg_roles p ON p.oid=a.roleid
+ JOIN pg_roles m ON m.oid=a.member JOIN pg_roles g ON g.oid=a.grantor
+ WHERE p.rolname IN('review_successor_fixture_parent_20261012','review_successor_fixture_member_20261012'))
+ )::text;`));
+ writeFileSync(`${output}.failure.json`,
+ `${JSON.stringify({ schemaVersion: 1, status: 'failed', code, observed }, null, 2)}\n`, { flag: 'wx', mode: 0o600 });
+ fail('FIXTURE_OWNERSHIP_ROLE_PROTOCOL_FAILED');
+ }
+ }
+ check(dropFixtureMembership(ownership, dependencies));
+ check(!ownership.fixtureMembership && !ownership.fixtureMembershipAttempted && createAttempts === 1 && dropAttempts === 1);
+ results.push({ kind: 'roles', mode, createAttempts, dropAttempts, cleaned: true });
+ }
+
+ sql('postgres', 'CREATE DATABASE ranking_clone OWNER postgres TEMPLATE template0;');
+ for (const mode of ['normal', 'lost-create-ack', 'lost-create-ack-and-first-readback']) {
+ let created = false;
+ let lostReadback = false;
+ let createAttempts = 0;
+ let dropAttempts = 0;
+ const ownership = { database: false };
+ const dependencies = {
+ readFileImpl: () => Buffer.from('-- ownership-only fixture: schema application excluded'),
+ scalarImpl: (database, statement, actor) => {
+ if (mode.endsWith('first-readback') && created && !lostReadback) {
+ lostReadback = true; fail('FIXTURE_FAULT_READBACK_LOSS');
+ }
+ return sql(database, statement, actor);
+ },
+ psqlImpl: (database, statement, options = {}) => {
+ // The ownership protocol is exercised against PG; predecessor/fixture SQL is outside this check.
+ if (database !== 'postgres') return {};
+ const text = String(statement);
+ sql(database, text, options.actor);
+ if (/CREATE DATABASE/.test(text)) {
+ created = true; createAttempts += 1;
+ if (mode !== 'normal') fail('FIXTURE_FAULT_CREATE_ACK_LOSS');
+ } else if (/DROP DATABASE/.test(text)) dropAttempts += 1;
+ return {};
+ },
+ };
+ try { createOwnedDatabase(ownership, dependencies); }
+ catch (error) { check(mode.endsWith('first-readback') && created && lostReadback
+ && error.message === 'LOCAL_REHEARSAL_DATABASE_READBACK_FAILED'); }
+ dropOwnedDatabase(ownership, dependencies);
+ check(!ownership.database && !ownership.databaseAttempted && createAttempts === 1 && dropAttempts === 1);
+ results.push({ kind: 'database', mode, createAttempts, dropAttempts, cleaned: true });
+ }
+ assertFixtureOwnershipBindings(sourceBindings);
+ } finally {
+ removeOwnedContainer(container, { dockerImpl: docker });
+ }
+
+ check(container.removed === true);
+ assertFixtureOwnershipBindings(sourceBindings);
+ const proof = { schemaVersion: 1, kind: 'isolated-fixture-ownership-verification', status: 'passed',
+ recordedAtUTC: new Date().toISOString(), ...sourceBindings,
+ environment: { engine: '17.6', image: 'supabase/postgres@sha256:fbe6c858a2ea9616aead3c39e132afcc0660decc5cb7dc656208ed145f5331ab' },
+ results, temporaryContainerRemoved: true, sourceContainerMutation: false, hostedWrites: false,
+ predecessorAndFixtureSqlExercised: false };
+ writeFileSync(output, `${JSON.stringify(proof, null, 2)}\n`, { flag: 'wx', mode: 0o600 });
+ console.log(JSON.stringify({ status: proof.status, scenarios: results.length, output: output.slice(root.length + 1) }));
+}
+
+if (process.argv[1] && resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
+ runFixtureOwnershipVerification();
+}
diff --git a/apps/web/performance/review-db-successor-20261012/verify-review-media-sql-successor-4ba9c6d2-historical.source.txt b/apps/web/performance/review-db-successor-20261012/verify-review-media-sql-successor-4ba9c6d2-historical.source.txt
new file mode 100644
index 0000000000..779cf90ec7
--- /dev/null
+++ b/apps/web/performance/review-db-successor-20261012/verify-review-media-sql-successor-4ba9c6d2-historical.source.txt
@@ -0,0 +1,1050 @@
+#!/usr/bin/env node
+
+/**
+ * Local-only PostgreSQL 17.6 rehearsal for the held eleven-stage successor.
+ *
+ * The harness clones the existing schema-only ranking_clone template into one
+ * per-process, owned database name. It refuses a reused target, never connects
+ * to a hosted endpoint, and drops the owned database before emitting its
+ * current-source result.
+ */
+import { createHash, randomBytes } from 'node:crypto';
+import { existsSync, readFileSync } from 'node:fs';
+import { dirname, resolve } from 'node:path';
+import { fileURLToPath } from 'node:url';
+import { spawn, spawnSync } from 'node:child_process';
+
+import {
+ buildSuccessorStateQuery,
+ compileSuccessorPlan,
+ loadSuccessorManifest,
+} from '../../scripts/review-media-sql-successor.mjs';
+
+const HERE = dirname(fileURLToPath(import.meta.url));
+const REPOSITORY_ROOT = resolve(HERE, '../../../..');
+const OUTPUT_PATH = resolve(HERE, 'pg17-local-replay.json');
+const CHECKSUMS_PATH = resolve(HERE, 'SHA256SUMS');
+const RETAINED_PROOF_REPOSITORY_PATH = 'apps/web/performance/review-db-successor-20261012/pg17-local-replay.json';
+const DOCKER_CONTEXT = process.env.TZUDONG_REHEARSAL_DOCKER_CONTEXT || 'colima-tzudong-catalog-20261007';
+const DOCKER_CONTAINER = process.env.TZUDONG_REHEARSAL_DOCKER_CONTAINER || 'tzudong-ranking-clone-20261008';
+const TEMPLATE_DATABASE = 'ranking_clone';
+const DATABASE = `review_successor_local_${randomBytes(16).toString('hex')}`;
+const FIXTURE_PASSWORD = 'fixture-only';
+const FIXTURE_PARENT_ROLE = 'review_successor_fixture_parent_20261012';
+const FIXTURE_MEMBER_ROLE = 'review_successor_fixture_member_20261012';
+const FIXTURE_OWNER_PREFIX = 'tzudong-review-successor-fixture';
+const EXPIRY_APPLICATION_NAME = 'tzudong-review-successor-expiry-fixture';
+const PREDECESSOR_PATHS = Object.freeze([
+ 'backend/supabase/migrations/20261004190259_admin_record_guarded_actions.sql',
+ 'backend/supabase/migrations/20261004192657_admin_evaluation_raw_warning_groups.sql',
+ 'backend/supabase/migrations/20261004194715_admin_evaluation_raw_warning_invoker_contract.sql',
+ 'backend/supabase/migrations/20261009022915_restaurant_review_manual_preview_eligibility.sql',
+ 'backend/supabase/migrations/20261009091342_admin_record_private_verification_cleanup.sql',
+ 'backend/supabase/migrations/20261009101645_admin_user_management_rpc_forward.sql',
+]);
+const sha256 = value => createHash('sha256').update(value).digest('hex');
+const quote = value => `'${String(value).replaceAll("'", "''")}'`;
+const canonical = value => Array.isArray(value)
+ ? value.map(canonical)
+ : value && typeof value === 'object'
+ ? Object.fromEntries(Object.keys(value).sort().map(key => [key, canonical(value[key])]))
+ : value;
+const equal = (left, right) => JSON.stringify(canonical(left)) === JSON.stringify(canonical(right));
+
+function fail(code, detail = '') {
+ throw new Error(detail ? `${code}:${detail}` : code);
+}
+
+function docker(args, { input, allowFailure = false } = {}) {
+ const result = spawnSync('docker', ['--context', DOCKER_CONTEXT, ...args], {
+ encoding: 'utf8',
+ input,
+ maxBuffer: 64 * 1024 * 1024,
+ });
+ if (!allowFailure && (result.error || result.status !== 0)) {
+ const sqlstates = [...(result.stderr || '').matchAll(/(?:ERROR|FATAL):\s+([0-9A-Z]{5}):/g)]
+ .map(match => match[1]);
+ const diagnostic = (result.stderr || '').split(/\r?\n/)
+ .find(line => /(?:ERROR|FATAL):/.test(line))
+ ?.replace(/\s+/g, ' ').slice(0, 240) || '';
+ fail('LOCAL_REHEARSAL_COMMAND_FAILED', [sqlstates.join(',') || String(result.status), diagnostic]
+ .filter(Boolean).join(':'));
+ }
+ return result;
+}
+
+function psql(database, sql, { actor = 'postgres', singleTransaction = false, allowFailure = false } = {}) {
+ const options = [
+ 'exec', '-i', '-e', `PGPASSWORD=${FIXTURE_PASSWORD}`, DOCKER_CONTAINER,
+ 'psql', '-XAtq', '-v', 'ON_ERROR_STOP=1', '-v', 'VERBOSITY=terse',
+ ];
+ if (singleTransaction) options.push('--single-transaction');
+ options.push('-h', '127.0.0.1', '-U', actor, '-d', database, '--file=-');
+ return docker(options, { input: sql, allowFailure });
+}
+
+function scalar(database, sql, actor = 'postgres') {
+ return psql(database, `${sql}\n`, { actor }).stdout.trim();
+}
+
+function readState(database, stateQuery) {
+ const output = scalar(database, `${stateQuery};`);
+ const lines = output.split(/\r?\n/).filter(Boolean);
+ if (lines.length !== 1) fail('LOCAL_REHEARSAL_STATE_SHAPE_INVALID', String(lines.length));
+ try {
+ return JSON.parse(lines[0]);
+ } catch {
+ fail('LOCAL_REHEARSAL_STATE_JSON_INVALID');
+ }
+}
+
+export function applySuccessorPlanOnce({
+ database,
+ sql,
+ stateQuery,
+ baselineState,
+ terminalState,
+ psqlImpl = psql,
+ readStateImpl = readState,
+}) {
+ try {
+ return Object.freeze({
+ outcome: 'acknowledged',
+ result: psqlImpl(database, sql, { singleTransaction: true }),
+ });
+ } catch {
+ let observedState;
+ try {
+ observedState = readStateImpl(database, stateQuery);
+ } catch {
+ fail('LOCAL_REHEARSAL_APPLICATION_OUTCOME_UNCERTAIN');
+ }
+ if (equal(observedState, terminalState)) {
+ return Object.freeze({
+ outcome: 'committed-after-acknowledgement-loss',
+ result: Object.freeze({ stdout: `${JSON.stringify(observedState)}\n` }),
+ });
+ }
+ if (equal(observedState, baselineState)) {
+ fail('LOCAL_REHEARSAL_APPLICATION_ROLLED_BACK');
+ }
+ fail('LOCAL_REHEARSAL_APPLICATION_STATE_AMBIGUOUS');
+ }
+}
+
+function ledgerInsert(migration) {
+ const vector = JSON.stringify(migration.originalStatementVector);
+ const tag = `$vector_${sha256(vector).slice(0, 20)}$`;
+ return `INSERT INTO supabase_migrations.schema_migrations(version,statements,name)
+SELECT ${quote(migration.version)},ARRAY(SELECT jsonb_array_elements_text(${tag}${vector}${tag}::jsonb)),${quote(migration.name)};`;
+}
+
+function fixtureSql() {
+ return `
+DO $fixture$
+BEGIN
+ IF (SELECT count(*) FROM supabase_migrations.schema_migrations)<>0 THEN
+ RAISE EXCEPTION 'LOCAL_REHEARSAL_LEDGER_NOT_EMPTY';
+ END IF;
+ IF (SELECT count(*) FROM public.profiles)<>0 OR (SELECT count(*) FROM public.reviews)<>0 THEN
+ RAISE EXCEPTION 'LOCAL_REHEARSAL_PERSONAL_ROWS_PRESENT';
+ END IF;
+END $fixture$;
+INSERT INTO supabase_migrations.schema_migrations(version,statements,name)
+SELECT lpad(value::text,14,'0'),ARRAY['local schema-only predecessor fixture'],format('local_predecessor_%s',value)
+FROM generate_series(1,86) value;
+INSERT INTO pipeline_control.restaurant_review_policy(singleton,enabled)
+VALUES(true,false)
+ON CONFLICT(singleton) DO UPDATE SET enabled=false,operator_id=NULL,last_run_at=NULL;
+INSERT INTO pipeline_control.admin_evaluation_catalog_revision(singleton,revision)
+VALUES(true,1)
+ON CONFLICT(singleton) DO NOTHING;
+`;
+}
+
+function databaseMetadataSql(databaseName) {
+ const projection = alias => `jsonb_build_object(
+ 'name',${alias}.datname,
+ 'owner',pg_get_userbyid(${alias}.datdba),
+ 'encoding',pg_encoding_to_char(${alias}.encoding),
+ 'collate',${alias}.datcollate,
+ 'ctype',${alias}.datctype,
+ 'localeProvider',${alias}.datlocprovider::text,
+ 'allowConnections',${alias}.datallowconn,
+ 'isTemplate',${alias}.datistemplate
+ )`;
+ return `SELECT jsonb_build_object(
+ 'target',(SELECT ${projection('target_database')} FROM pg_database target_database
+ WHERE target_database.datname=${quote(databaseName)}),
+ 'template',(SELECT ${projection('template_database')} FROM pg_database template_database
+ WHERE template_database.datname=${quote(TEMPLATE_DATABASE)})
+)::text;`;
+}
+
+function inspectDatabaseOwnership(databaseName, { scalarImpl = scalar } = {}) {
+ let state;
+ try {
+ state = JSON.parse(scalarImpl('postgres', databaseMetadataSql(databaseName), 'supabase_admin'));
+ } catch {
+ fail('LOCAL_REHEARSAL_DATABASE_STATE_INVALID');
+ }
+ if (!state
+ || typeof state !== 'object'
+ || !Object.hasOwn(state, 'target')
+ || !databaseMetadataValid(state.template)
+ || state.template.name !== TEMPLATE_DATABASE
+ || (state.target !== null && !databaseMetadataValid(state.target))) {
+ fail('LOCAL_REHEARSAL_DATABASE_STATE_INVALID');
+ }
+ return state;
+}
+
+function databaseMetadataValid(metadata) {
+ return metadata
+ && typeof metadata === 'object'
+ && ['name', 'owner', 'encoding', 'collate', 'ctype', 'localeProvider']
+ .every(field => typeof metadata[field] === 'string' && metadata[field].length > 0)
+ && typeof metadata.allowConnections === 'boolean'
+ && typeof metadata.isTemplate === 'boolean';
+}
+
+function databaseTargetAbsent(state, databaseName) {
+ return state?.target === null && state?.template?.name === TEMPLATE_DATABASE && databaseName === DATABASE;
+}
+
+function databaseTargetOwned(state, baseline, databaseName) {
+ const target = state?.target;
+ const template = state?.template;
+ return databaseTargetAbsent(baseline, databaseName)
+ && target?.name === databaseName
+ && target.owner === 'postgres'
+ && target.allowConnections === true
+ && target.isTemplate === false
+ && template?.name === TEMPLATE_DATABASE
+ && equal(template, baseline.template)
+ && target.encoding === template.encoding
+ && target.collate === template.collate
+ && target.ctype === template.ctype
+ && target.localeProvider === template.localeProvider;
+}
+
+export function createOwnedDatabase(ownership, {
+ scalarImpl = scalar,
+ psqlImpl = psql,
+ readFileImpl = readFileSync,
+} = {}) {
+ if (!ownership
+ || ownership.database !== false
+ || ownership.databaseAttempted
+ || ownership.databaseName
+ || ownership.databaseBaseline) {
+ fail('LOCAL_REHEARSAL_OWNERSHIP_STATE_INVALID');
+ }
+ const baseline = inspectDatabaseOwnership(DATABASE, { scalarImpl });
+ if (!databaseTargetAbsent(baseline, DATABASE)) fail('LOCAL_REHEARSAL_TARGET_ALREADY_EXISTS');
+ ownership.databaseName = DATABASE;
+ ownership.databaseBaseline = baseline;
+ ownership.databaseAttempted = true;
+ let creationAcknowledged = false;
+ try {
+ psqlImpl('postgres', `CREATE DATABASE ${DATABASE} TEMPLATE ${TEMPLATE_DATABASE} OWNER postgres;`, { actor: 'supabase_admin' });
+ creationAcknowledged = true;
+ } catch {
+ // CREATE is intentionally never replayed. The exact catalog readback below
+ // distinguishes a committed CREATE from a failure before mutation.
+ }
+ let observed;
+ try {
+ observed = inspectDatabaseOwnership(DATABASE, { scalarImpl });
+ } catch {
+ fail('LOCAL_REHEARSAL_DATABASE_READBACK_FAILED');
+ }
+ if (!databaseTargetOwned(observed, baseline, DATABASE)) {
+ if (databaseTargetAbsent(observed, DATABASE)) {
+ ownership.databaseAttempted = false;
+ ownership.databaseName = null;
+ ownership.databaseBaseline = null;
+ fail('LOCAL_REHEARSAL_DATABASE_CREATION_UNCONFIRMED');
+ }
+ fail('LOCAL_REHEARSAL_DATABASE_OWNERSHIP_UNPROVEN');
+ }
+ ownership.database = true;
+ ownership.databaseCreationAcknowledged = creationAcknowledged;
+ ownership.databaseCreationReconciled = !creationAcknowledged;
+ PREDECESSOR_PATHS.forEach((path, index) => {
+ try {
+ psqlImpl(DATABASE, readFileImpl(resolve(REPOSITORY_ROOT, path)));
+ } catch (error) {
+ fail('LOCAL_REHEARSAL_PREDECESSOR_FAILED', `${index + 1}:${error.message}`);
+ }
+ });
+ psqlImpl(DATABASE, fixtureSql());
+ return true;
+}
+
+export function dropOwnedDatabase(ownership, { scalarImpl = scalar, psqlImpl = psql } = {}) {
+ if (!ownership?.database && !ownership?.databaseAttempted) return false;
+ if (ownership.databaseName !== DATABASE
+ || !ownership.databaseBaseline
+ || !databaseTargetAbsent(ownership.databaseBaseline, DATABASE)) {
+ fail('LOCAL_REHEARSAL_DATABASE_OWNERSHIP_STATE_INVALID');
+ }
+ let before;
+ try {
+ before = inspectDatabaseOwnership(DATABASE, { scalarImpl });
+ } catch {
+ fail('LOCAL_REHEARSAL_DATABASE_CLEANUP_READBACK_FAILED');
+ }
+ if (databaseTargetAbsent(before, DATABASE)) {
+ const wasOwned = ownership.database === true;
+ ownership.database = false;
+ ownership.databaseAttempted = false;
+ ownership.databaseDropped = wasOwned;
+ ownership.databaseCleanupReconciled = true;
+ ownership.databaseName = null;
+ ownership.databaseBaseline = null;
+ return false;
+ }
+ if (!databaseTargetOwned(before, ownership.databaseBaseline, DATABASE)) {
+ fail('LOCAL_REHEARSAL_DATABASE_OWNERSHIP_UNPROVEN');
+ }
+ ownership.database = true;
+ let removalAcknowledged = false;
+ try {
+ psqlImpl('postgres', `DROP DATABASE ${DATABASE} WITH (FORCE);`, { actor: 'supabase_admin' });
+ removalAcknowledged = true;
+ } catch {
+ // A lost DROP acknowledgement is reconciled once; DROP is not replayed.
+ }
+ let after;
+ try {
+ after = inspectDatabaseOwnership(DATABASE, { scalarImpl });
+ } catch {
+ fail('LOCAL_REHEARSAL_DATABASE_CLEANUP_READBACK_FAILED');
+ }
+ if (!databaseTargetAbsent(after, DATABASE)) {
+ if (!databaseTargetOwned(after, ownership.databaseBaseline, DATABASE)) {
+ fail('LOCAL_REHEARSAL_DATABASE_OWNERSHIP_UNPROVEN');
+ }
+ fail('LOCAL_REHEARSAL_DATABASE_DROP_UNCONFIRMED');
+ }
+ ownership.database = false;
+ ownership.databaseAttempted = false;
+ ownership.databaseDropped = true;
+ ownership.databaseRemovalAcknowledged = removalAcknowledged;
+ ownership.databaseCleanupReconciled = !removalAcknowledged;
+ ownership.databaseName = null;
+ ownership.databaseBaseline = null;
+ return true;
+}
+
+function resetOwnedDatabase(ownership) {
+ dropOwnedDatabase(ownership);
+ createOwnedDatabase(ownership);
+}
+
+function fixtureRoleStateQuery() {
+ return `WITH fixture_roles AS (
+ SELECT fixture_role.rolname,
+ fixture_role.rolcanlogin,
+ fixture_role.rolsuper,
+ fixture_role.rolcreatedb,
+ fixture_role.rolcreaterole,
+ fixture_role.rolinherit,
+ fixture_role.rolreplication,
+ fixture_role.rolbypassrls,
+ fixture_role.rolconnlimit,
+ fixture_role.rolvaliduntil,
+ fixture_role.rolconfig,
+ coalesce(shobj_description(fixture_role.oid,'pg_authid'),'') AS owner_marker
+ FROM pg_roles fixture_role
+ WHERE fixture_role.rolname IN(${quote(FIXTURE_PARENT_ROLE)},${quote(FIXTURE_MEMBER_ROLE)})
+), fixture_memberships AS (
+ SELECT parent.rolname AS parent,
+ member.rolname AS member,
+ grantor.rolname AS grantor,
+ membership.admin_option,
+ membership.inherit_option,
+ membership.set_option
+ FROM pg_auth_members membership
+ JOIN pg_roles parent ON parent.oid=membership.roleid
+ JOIN pg_roles member ON member.oid=membership.member
+ JOIN pg_roles grantor ON grantor.oid=membership.grantor
+ WHERE parent.rolname IN(${quote(FIXTURE_PARENT_ROLE)},${quote(FIXTURE_MEMBER_ROLE)})
+ OR member.rolname IN(${quote(FIXTURE_PARENT_ROLE)},${quote(FIXTURE_MEMBER_ROLE)})
+)
+SELECT jsonb_build_object(
+ 'roles',coalesce((SELECT jsonb_agg(to_jsonb(fixture_role) ORDER BY fixture_role.rolname) FROM fixture_roles fixture_role),'[]'::jsonb),
+ 'memberships',coalesce((SELECT jsonb_agg(to_jsonb(fixture_membership) ORDER BY fixture_membership.parent,fixture_membership.member,fixture_membership.grantor) FROM fixture_memberships fixture_membership),'[]'::jsonb)
+)::text;`;
+}
+
+function inspectFixtureMembership({ scalarImpl = scalar } = {}) {
+ let output;
+ try {
+ output = scalarImpl('postgres', fixtureRoleStateQuery(), 'supabase_admin');
+ } catch {
+ fail('LOCAL_REHEARSAL_FIXTURE_ROLE_READBACK_FAILED');
+ }
+ let state;
+ try {
+ state = JSON.parse(output);
+ } catch {
+ fail('LOCAL_REHEARSAL_FIXTURE_ROLE_READBACK_INVALID');
+ }
+ if (!Array.isArray(state?.roles) || !Array.isArray(state?.memberships)) {
+ fail('LOCAL_REHEARSAL_FIXTURE_ROLE_READBACK_INVALID');
+ }
+ return Object.freeze({
+ roles: Object.freeze(state.roles.map(role => Object.freeze({ ...role }))),
+ memberships: Object.freeze(state.memberships.map(membership => Object.freeze({ ...membership }))),
+ });
+}
+
+function fixtureMembershipAbsent(state) {
+ return state.roles.length === 0 && state.memberships.length === 0;
+}
+
+function fixtureMembershipOwned(state, token) {
+ const roles = new Map(state.roles.map(role => [role.rolname, role]));
+ const parent = roles.get(FIXTURE_PARENT_ROLE);
+ const member = roles.get(FIXTURE_MEMBER_ROLE);
+ const membership = state.memberships[0];
+ const sharedRoleShape = role => role
+ && role.rolcanlogin === false
+ && role.rolsuper === false
+ && role.rolcreatedb === false
+ && role.rolcreaterole === false
+ && role.rolreplication === false
+ && role.rolbypassrls === false
+ && role.rolconnlimit === -1
+ && role.rolvaliduntil === null
+ && role.rolconfig === null;
+ return state.roles.length === 2
+ && sharedRoleShape(parent)
+ && parent.rolinherit === false
+ && parent.owner_marker === `${FIXTURE_OWNER_PREFIX}:${token}:parent`
+ && sharedRoleShape(member)
+ && member.rolinherit === true
+ && member.owner_marker === `${FIXTURE_OWNER_PREFIX}:${token}:member`
+ && state.memberships.length === 1
+ && membership.parent === FIXTURE_PARENT_ROLE
+ && membership.member === FIXTURE_MEMBER_ROLE
+ && membership.grantor === 'supabase_admin'
+ && membership.admin_option === false
+ && membership.inherit_option === true
+ && membership.set_option === true;
+}
+
+export function createFixtureMembership(ownership, {
+ scalarImpl = scalar,
+ psqlImpl = psql,
+ generateToken = () => randomBytes(16).toString('hex'),
+} = {}) {
+ if (!ownership
+ || ownership.fixtureMembership !== false
+ || ownership.fixtureMembershipAttempted
+ || ownership.fixtureMembershipToken
+ || ownership.fixtureMembershipBaseline) {
+ fail('LOCAL_REHEARSAL_FIXTURE_ROLE_OWNERSHIP_STATE_INVALID');
+ }
+ const baseline = inspectFixtureMembership({ scalarImpl });
+ ownership.fixtureMembershipBaseline = baseline;
+ if (!fixtureMembershipAbsent(baseline)) fail('LOCAL_REHEARSAL_FIXTURE_ROLE_EXISTS');
+ const token = generateToken();
+ if (!/^[0-9a-f]{32}$/.test(token)) fail('LOCAL_REHEARSAL_FIXTURE_ROLE_TOKEN_INVALID');
+ ownership.fixtureMembershipToken = token;
+ ownership.fixtureMembershipAttempted = true;
+ let creationAcknowledged = false;
+ try {
+ psqlImpl('postgres', `
+BEGIN;
+CREATE ROLE ${FIXTURE_PARENT_ROLE} NOLOGIN NOSUPERUSER NOCREATEDB NOCREATEROLE NOINHERIT NOREPLICATION NOBYPASSRLS;
+CREATE ROLE ${FIXTURE_MEMBER_ROLE} NOLOGIN NOSUPERUSER NOCREATEDB NOCREATEROLE INHERIT NOREPLICATION NOBYPASSRLS;
+COMMENT ON ROLE ${FIXTURE_PARENT_ROLE} IS ${quote(`${FIXTURE_OWNER_PREFIX}:${token}:parent`)};
+COMMENT ON ROLE ${FIXTURE_MEMBER_ROLE} IS ${quote(`${FIXTURE_OWNER_PREFIX}:${token}:member`)};
+GRANT ${FIXTURE_PARENT_ROLE} TO ${FIXTURE_MEMBER_ROLE}
+ WITH ADMIN FALSE, INHERIT TRUE, SET TRUE GRANTED BY supabase_admin;
+COMMIT;
+`, { actor: 'supabase_admin' });
+ creationAcknowledged = true;
+ } catch {}
+ const observed = inspectFixtureMembership({ scalarImpl });
+ if (!fixtureMembershipOwned(observed, token)) {
+ ownership.fixtureMembershipAttempted = false;
+ if (fixtureMembershipAbsent(observed)) fail('LOCAL_REHEARSAL_FIXTURE_ROLE_CREATION_UNCONFIRMED');
+ fail('LOCAL_REHEARSAL_FIXTURE_ROLE_OWNERSHIP_UNPROVEN');
+ }
+ ownership.fixtureMembership = true;
+ ownership.fixtureMembershipCreationAcknowledged = creationAcknowledged;
+ ownership.fixtureMembershipCreationReconciled = !creationAcknowledged;
+ return true;
+}
+
+export function dropFixtureMembership(ownership, {
+ scalarImpl = scalar,
+ psqlImpl = psql,
+} = {}) {
+ if (!ownership?.fixtureMembership && !ownership?.fixtureMembershipAttempted) return false;
+ const token = ownership.fixtureMembershipToken;
+ if (!/^[0-9a-f]{32}$/.test(token || '')
+ || !ownership.fixtureMembershipBaseline
+ || !fixtureMembershipAbsent(ownership.fixtureMembershipBaseline)) {
+ fail('LOCAL_REHEARSAL_FIXTURE_ROLE_OWNERSHIP_STATE_INVALID');
+ }
+ const before = inspectFixtureMembership({ scalarImpl });
+ if (fixtureMembershipAbsent(before)) {
+ const wasOwned = ownership.fixtureMembership === true;
+ ownership.fixtureMembership = false;
+ ownership.fixtureMembershipAttempted = false;
+ ownership.fixtureMembershipDropped = wasOwned;
+ ownership.fixtureMembershipCleanupReconciled = true;
+ return wasOwned;
+ }
+ if (!fixtureMembershipOwned(before, token)) {
+ fail('LOCAL_REHEARSAL_FIXTURE_ROLE_OWNERSHIP_UNPROVEN');
+ }
+ // An earlier creation readback can fail after COMMIT. Exact token, shape,
+ // membership, and empty preimage establish this run's cleanup authority.
+ ownership.fixtureMembership = true;
+ let removalAcknowledged = false;
+ try {
+ psqlImpl('postgres', `
+BEGIN;
+REVOKE ${FIXTURE_PARENT_ROLE} FROM ${FIXTURE_MEMBER_ROLE} GRANTED BY supabase_admin;
+DROP ROLE ${FIXTURE_MEMBER_ROLE};
+DROP ROLE ${FIXTURE_PARENT_ROLE};
+COMMIT;
+`, { actor: 'supabase_admin' });
+ removalAcknowledged = true;
+ } catch {}
+ const after = inspectFixtureMembership({ scalarImpl });
+ if (!fixtureMembershipAbsent(after)) {
+ if (!fixtureMembershipOwned(after, token)) {
+ fail('LOCAL_REHEARSAL_FIXTURE_ROLE_OWNERSHIP_UNPROVEN');
+ }
+ fail('LOCAL_REHEARSAL_FIXTURE_ROLE_REMOVAL_UNCONFIRMED');
+ }
+ ownership.fixtureMembership = false;
+ ownership.fixtureMembershipAttempted = false;
+ ownership.fixtureMembershipDropped = true;
+ ownership.fixtureMembershipRemovalAcknowledged = removalAcknowledged;
+ return true;
+}
+
+function cleanupOwnedFixtures(ownership) {
+ const failures = [];
+ try {
+ dropOwnedDatabase(ownership);
+ } catch (error) {
+ failures.push(`database:${error.message}`);
+ }
+ try {
+ dropFixtureMembership(ownership);
+ } catch (error) {
+ failures.push(`roles:${error.message}`);
+ }
+ if (failures.length > 0) fail('LOCAL_REHEARSAL_CLEANUP_FAILED', failures.join(','));
+}
+
+export function inspectRetainedProof({
+ outputPath = OUTPUT_PATH,
+ checksumsPath = CHECKSUMS_PATH,
+ currentBindings,
+ existsImpl = existsSync,
+ readFileImpl = readFileSync,
+} = {}) {
+ if (!existsImpl(outputPath)) return Object.freeze({ present: false });
+ if (!currentBindings || typeof currentBindings !== 'object') {
+ fail('LOCAL_REHEARSAL_CURRENT_BINDINGS_INVALID');
+ }
+ const checksumLines = readFileImpl(checksumsPath, 'utf8').split(/\r?\n/).filter(Boolean);
+ const matchingLines = checksumLines.filter(line => line.endsWith(` ${RETAINED_PROOF_REPOSITORY_PATH}`));
+ if (matchingLines.length !== 1) fail('LOCAL_REHEARSAL_RETAINED_CHECKSUM_MISSING');
+ const checksumMatch = matchingLines[0].match(/^([0-9a-f]{64}) (.+)$/);
+ if (!checksumMatch || checksumMatch[2] !== RETAINED_PROOF_REPOSITORY_PATH) {
+ fail('LOCAL_REHEARSAL_RETAINED_CHECKSUM_INVALID');
+ }
+ const bytes = readFileImpl(outputPath);
+ const actualSha256 = sha256(bytes);
+ if (actualSha256 !== checksumMatch[1]) fail('LOCAL_REHEARSAL_RETAINED_SHA256_MISMATCH');
+ let retained;
+ try {
+ retained = JSON.parse(bytes.toString('utf8'));
+ } catch {
+ fail('LOCAL_REHEARSAL_RETAINED_JSON_INVALID');
+ }
+ const retainedBindings = {
+ manifestSha256: retained.manifestSha256,
+ rehearsalHarnessSha256: retained.rehearsalHarnessSha256,
+ sourceRoot: retained.sourceRoot,
+ };
+ if (retained.status !== 'passed'
+ || retained.temporaryDatabaseDropped !== true
+ || retained.hostedWrites !== false
+ || retained.operatingWrites !== false
+ || Object.values(retainedBindings).some(value => !/^[0-9a-f]{64}$/.test(value))) {
+ fail('LOCAL_REHEARSAL_RETAINED_SHAPE_INVALID');
+ }
+ const bindingDifferences = Object.keys(retainedBindings)
+ .filter(key => retainedBindings[key] !== currentBindings[key])
+ .map(key => ({
+ field: key,
+ retained: retainedBindings[key],
+ current: currentBindings[key],
+ }));
+ return Object.freeze({
+ present: true,
+ classification: 'retained-historical-evidence',
+ repositoryPath: RETAINED_PROOF_REPOSITORY_PATH,
+ expectedSha256: checksumMatch[1],
+ actualSha256,
+ bindings: Object.freeze(retainedBindings),
+ currentSourceMatch: bindingDifferences.length === 0,
+ bindingDifferences: Object.freeze(bindingDifferences),
+ usedAsCurrentProof: false,
+ });
+}
+
+function applyRawMigration(record, index) {
+ const migration = record.manifest.migrations[index];
+ const material = record.materials[index];
+ try {
+ psql(DATABASE, material.bytes);
+ psql(DATABASE, `${ledgerInsert(migration)}\n`);
+ } catch (error) {
+ fail('LOCAL_REHEARSAL_MIGRATION_FAILED', `${index + 1}:${error.message}`);
+ }
+}
+
+function collectStageStates(record, stateQuery) {
+ const states = [readState(DATABASE, stateQuery)];
+ for (let index = 0; index < record.manifest.migrations.length; index += 1) {
+ applyRawMigration(record, index);
+ states.push(readState(DATABASE, stateQuery));
+ }
+ return states;
+}
+
+function summarizeStage(stage, index) {
+ return {
+ index,
+ ledgerCount: stage.bundle.ledgerCount,
+ targetCount: stage.bundle.targetCount,
+ prefixRoot: stage.bundle.prefixRoot,
+ suffixRoot: stage.bundle.suffixRoot,
+ schemaRoot: stage.state.schemaRoot,
+ ledgerRoot: stage.state.ledgerRoot,
+ g014Root: stage.state.g014Root,
+ storageRoot: stage.state.storageRoot,
+ finishCleanupBodySha256: stage.state.finishCleanupBodySha256,
+ ownerAuthorityReady: stage.state.ownerAuthorityReady,
+ terminalContractReady: stage.state.terminalContractReady,
+ };
+}
+
+function assertRehearsalStages(record, stages) {
+ const { manifest } = record;
+ if (stages.length !== 12) fail('LOCAL_REHEARSAL_STAGE_COUNT_INVALID');
+ const prefixRoot = stages[0].bundle.prefixRoot;
+ stages.forEach((stage, index) => {
+ if (stage.bundle.ledgerCount !== 86 + index
+ || stage.bundle.priorCount !== 86
+ || stage.bundle.prefixRoot !== prefixRoot
+ || stage.bundle.targetCount !== index
+ || stage.state.activeReviewItems !== 0
+ || stage.state.reviewItems !== 0
+ || stage.state.reviewRuns !== 0
+ || stage.state.reviewPolicyEnabled !== false
+ || stage.state.currentUser !== 'postgres'
+ || stage.state.sessionUser !== 'postgres'
+ || stage.state.databaseName !== DATABASE
+ || stage.state.serverMajor !== 17
+ || stage.state.ownerAuthorityReady !== true
+ || stage.state.terminalContractReady !== (index === 11)) {
+ fail('LOCAL_REHEARSAL_STAGE_INVALID', String(index));
+ }
+ });
+ if (stages[10].state.finishCleanupBodySha256 !== manifest.terminalContract.finishPreimageSha256
+ || stages[11].state.finishCleanupBodySha256 !== manifest.terminalContract.finishPostimageSha256) {
+ fail('LOCAL_REHEARSAL_FINISH_PREIMAGE_INVALID');
+ }
+ if (!equal(stages.slice(0, 3).map(stage => stage.state.g014Root), Array(3).fill(stages[0].state.g014Root))
+ || !equal(stages.slice(4).map(stage => stage.state.g014Root), Array(8).fill(stages[4].state.g014Root))) {
+ fail('LOCAL_REHEARSAL_G014_TRANSITION_INVALID');
+ }
+}
+
+function tamperFinishCleanupPreimage() {
+ psql(DATABASE, `
+BEGIN;
+DO $guard$ BEGIN
+ IF EXISTS(SELECT 1 FROM pg_auth_members
+ WHERE roleid='privacy_workflow_owner'::regrole AND member='postgres'::regrole AND grantor='postgres'::regrole)
+ THEN RAISE EXCEPTION 'LOCAL_REHEARSAL_SELF_GRANT_EXISTS'; END IF;
+END $guard$;
+GRANT privacy_workflow_owner TO postgres WITH ADMIN FALSE,INHERIT FALSE,SET TRUE GRANTED BY postgres;
+SET LOCAL ROLE privacy_workflow_owner;
+DO $tamper$
+DECLARE definition text; source text;
+BEGIN
+ SELECT pg_get_functiondef(oid),prosrc INTO definition,source
+ FROM pg_proc WHERE oid='public.finish_review_media_cleanup()'::regprocedure;
+ EXECUTE replace(definition,source,source || E'\\n-- local-only preimage drift');
+END $tamper$;
+RESET ROLE;
+REVOKE privacy_workflow_owner FROM postgres GRANTED BY postgres;
+COMMIT;
+`);
+}
+
+function assertCatalogDriftRejected({ label, ownership, plan, stages, stateQuery, tamperSql, restoreSql = null }) {
+ resetOwnedDatabase(ownership);
+ const baseline = readState(DATABASE, stateQuery);
+ if (!equal(baseline, stages[0])) fail('LOCAL_REHEARSAL_DRIFT_BASELINE_INVALID', label);
+ psql(DATABASE, tamperSql, { actor: 'supabase_admin' });
+ const driftedBefore = readState(DATABASE, stateQuery);
+ if (driftedBefore.state.schemaRoot === baseline.state.schemaRoot
+ || driftedBefore.state.g014Root !== baseline.state.g014Root
+ || driftedBefore.state.storageRoot !== baseline.state.storageRoot
+ || driftedBefore.state.ledgerRoot !== baseline.state.ledgerRoot) {
+ fail('LOCAL_REHEARSAL_DRIFT_PROJECTION_INVALID', label);
+ }
+ const rejected = psql(DATABASE, plan.sql, { singleTransaction: true, allowFailure: true });
+ if (rejected.status === 0 || !/MIGRATION_PRIOR_STATE_MISMATCH/.test(rejected.stderr)) {
+ fail('LOCAL_REHEARSAL_CATALOG_DRIFT_NOT_REJECTED', label);
+ }
+ const driftedAfter = readState(DATABASE, stateQuery);
+ if (!equal(driftedBefore, driftedAfter)
+ || driftedAfter.bundle.targetCount !== 0
+ || driftedAfter.bundle.ledgerCount !== 86) {
+ fail('LOCAL_REHEARSAL_CATALOG_DRIFT_STATE_CHANGED', label);
+ }
+ if (restoreSql) {
+ psql(DATABASE, restoreSql, { actor: 'supabase_admin' });
+ if (!equal(readState(DATABASE, stateQuery), baseline)) {
+ fail('LOCAL_REHEARSAL_CATALOG_DRIFT_RESTORE_FAILED', label);
+ }
+ }
+ return true;
+}
+
+function startAdvisoryLockHolder() {
+ const args = [
+ '--context', DOCKER_CONTEXT,
+ 'exec', '-i', '-e', `PGPASSWORD=${FIXTURE_PASSWORD}`, DOCKER_CONTAINER,
+ 'psql', '-XAtq', '-v', 'ON_ERROR_STOP=1', '-h', '127.0.0.1', '-U', 'postgres', '-d', DATABASE,
+ ];
+ const child = spawn('docker', args, { stdio: ['pipe', 'pipe', 'pipe'] });
+ let stdout = '';
+ let stderr = '';
+ let settled = false;
+ const ready = new Promise((resolveReady, rejectReady) => {
+ const reject = detail => {
+ if (!settled) {
+ settled = true;
+ rejectReady(new Error(`LOCAL_REHEARSAL_LOCK_HOLDER_FAILED:${detail}`));
+ }
+ };
+ child.on('error', error => reject(error.code || 'SPAWN'));
+ child.stdout.on('data', chunk => {
+ stdout += chunk.toString();
+ if (!settled && stdout.includes('LOCK_HELD')) {
+ settled = true;
+ resolveReady();
+ }
+ });
+ child.stderr.on('data', chunk => { stderr += chunk.toString(); });
+ child.on('exit', code => {
+ if (!settled) reject(`${code}:${stderr.replace(/\s+/g, ' ').slice(0, 120)}`);
+ });
+ });
+ const completion = new Promise((resolveCompletion, rejectCompletion) => {
+ child.on('error', rejectCompletion);
+ child.on('close', code => {
+ if (code === 0) resolveCompletion();
+ else rejectCompletion(new Error(`LOCAL_REHEARSAL_LOCK_HOLDER_FAILED:${code}:${stderr.replace(/\s+/g, ' ').slice(0, 120)}`));
+ });
+ });
+ child.stdin.end(`BEGIN;\nSELECT pg_advisory_xact_lock(7311754282260211::bigint);\nSELECT 'LOCK_HELD';\nSELECT pg_sleep(1.55);\nCOMMIT;\n`);
+ return { child, completion, ready };
+}
+
+function startPsqlProcess(database, sql, { applicationName, singleTransaction = false } = {}) {
+ const args = [
+ '--context', DOCKER_CONTEXT,
+ 'exec', '-i', '-e', `PGPASSWORD=${FIXTURE_PASSWORD}`,
+ ];
+ if (applicationName) args.push('-e', `PGAPPNAME=${applicationName}`);
+ args.push(
+ DOCKER_CONTAINER,
+ 'psql', '-XAtq', '-v', 'ON_ERROR_STOP=1', '-v', 'VERBOSITY=terse',
+ );
+ if (singleTransaction) args.push('--single-transaction');
+ args.push('-h', '127.0.0.1', '-U', 'postgres', '-d', database, '--file=-');
+ const child = spawn('docker', args, { stdio: ['pipe', 'pipe', 'pipe'] });
+ let stdout = '';
+ let stderr = '';
+ let inputError = null;
+ child.stdout.on('data', chunk => { stdout += chunk.toString(); });
+ child.stderr.on('data', chunk => { stderr += chunk.toString(); });
+ child.stdin.on('error', error => {
+ if (error.code !== 'EPIPE') inputError = error;
+ });
+ const completion = new Promise((resolveCompletion, rejectCompletion) => {
+ child.on('error', rejectCompletion);
+ child.on('close', status => {
+ if (inputError) rejectCompletion(inputError);
+ else resolveCompletion({ status, stderr, stdout });
+ });
+ });
+ child.stdin.end(sql);
+ return { child, completion };
+}
+
+async function waitForAdvisoryContention(expiresAt) {
+ const deadline = Math.min(expiresAt - 25, Date.now() + 700);
+ while (Date.now() < deadline) {
+ const waiting = scalar(DATABASE, `SELECT count(*) FROM pg_stat_activity a
+ JOIN pg_locks l ON l.pid=a.pid
+ WHERE a.application_name=${quote(EXPIRY_APPLICATION_NAME)}
+ AND l.locktype='advisory' AND NOT l.granted;`, 'supabase_admin');
+ if (waiting === '1') return Date.now();
+ if (waiting !== '0') fail('LOCAL_REHEARSAL_CONTENTION_CARDINALITY_INVALID');
+ await new Promise(resolveDelay => setTimeout(resolveDelay, 20));
+ }
+ fail('LOCAL_REHEARSAL_ADVISORY_CONTENTION_NOT_OBSERVED');
+}
+
+async function assertContendedExpiryRejected(record, stages, stateQuery, ownership) {
+ resetOwnedDatabase(ownership);
+ const baseline = readState(DATABASE, stateQuery);
+ if (!equal(baseline, stages[0])) fail('LOCAL_REHEARSAL_EXPIRY_BASELINE_INVALID');
+ const expiresAt = Date.now() + 1500;
+ const expiry = new Date(expiresAt).toISOString();
+ const expiringPlan = compileSuccessorPlan(record, { expiresAt: expiry, stageStates: stages });
+ const holder = startAdvisoryLockHolder();
+ let apply = null;
+ try {
+ await holder.ready;
+ if (Date.now() >= expiresAt) fail('LOCAL_REHEARSAL_EXPIRY_WINDOW_TOO_SHORT');
+ apply = startPsqlProcess(DATABASE, expiringPlan.sql, {
+ applicationName: EXPIRY_APPLICATION_NAME,
+ singleTransaction: true,
+ });
+ const contentionObservedAt = await waitForAdvisoryContention(expiresAt);
+ if (contentionObservedAt >= expiresAt) fail('LOCAL_REHEARSAL_CONTENTION_OBSERVED_AFTER_EXPIRY');
+ const [rejected] = await Promise.all([apply.completion, holder.completion]);
+ if (rejected.status === 0
+ || !/SUCCESSOR_ADMISSION_EXPIRED/.test(rejected.stderr)
+ || /lock timeout/.test(rejected.stderr)) {
+ fail('LOCAL_REHEARSAL_CONTENDED_EXPIRY_NOT_REJECTED');
+ }
+ } finally {
+ if (apply?.child.exitCode === null) apply.child.kill('SIGTERM');
+ if (holder.child.exitCode === null) holder.child.kill('SIGTERM');
+ }
+ const after = readState(DATABASE, stateQuery);
+ if (!equal(after, baseline) || after.bundle.targetCount !== 0 || after.bundle.ledgerCount !== 86) {
+ fail('LOCAL_REHEARSAL_CONTENDED_EXPIRY_STATE_CHANGED');
+ }
+ return true;
+}
+
+export async function run() {
+ const manifestBytes = readFileSync(resolve(REPOSITORY_ROOT, '.github/review-media-sql-successor.v1.json'));
+ const record = loadSuccessorManifest();
+ const currentBindings = Object.freeze({
+ manifestSha256: sha256(manifestBytes),
+ rehearsalHarnessSha256: sha256(readFileSync(fileURLToPath(import.meta.url))),
+ sourceRoot: record.manifest.sourceRoot,
+ });
+ const retainedProof = inspectRetainedProof({ currentBindings });
+ const stateQuery = buildSuccessorStateQuery(record.manifest);
+ const template = JSON.parse(scalar(TEMPLATE_DATABASE, `SELECT json_build_object(
+ 'engine',current_setting('server_version'),
+ 'ledger',(SELECT count(*) FROM supabase_migrations.schema_migrations),
+ 'manifest',(SELECT count(*) FROM privacy_retention.g014_catalog_contract_manifest),
+ 'profiles',(SELECT count(*) FROM public.profiles),
+ 'reviews',(SELECT count(*) FROM public.reviews)
+ )::text;`, 'supabase_admin'));
+ if (!String(template.engine).startsWith('17.6')
+ || template.ledger !== 0
+ || template.profiles !== 0
+ || template.reviews !== 0) fail('LOCAL_REHEARSAL_TEMPLATE_INVALID');
+
+ const ownership = {
+ database: false,
+ databaseAttempted: false,
+ databaseName: null,
+ databaseBaseline: null,
+ fixtureMembership: false,
+ fixtureMembershipAttempted: false,
+ fixtureMembershipToken: null,
+ fixtureMembershipBaseline: null,
+ };
+ let proof;
+ try {
+ createFixtureMembership(ownership);
+ createOwnedDatabase(ownership);
+ if (scalar(DATABASE, `SELECT coalesce('security_invoker=true'=ANY(c.reloptions),false)
+ FROM pg_class c WHERE c.oid='public.admin_evaluation_related_rows'::regclass;`) !== 't') {
+ fail('LOCAL_REHEARSAL_SECURITY_INVOKER_BASELINE_INVALID');
+ }
+ if (scalar(DATABASE, `SELECT m.inherit_option::text || ':' || m.set_option::text
+ FROM pg_auth_members m
+ WHERE m.roleid=${quote(FIXTURE_PARENT_ROLE)}::regrole
+ AND m.member=${quote(FIXTURE_MEMBER_ROLE)}::regrole
+ AND m.grantor='supabase_admin'::regrole;`, 'supabase_admin') !== 'true:true') {
+ fail('LOCAL_REHEARSAL_MEMBERSHIP_OPTIONS_BASELINE_INVALID');
+ }
+ const stages = collectStageStates(record, stateQuery);
+ assertRehearsalStages(record, stages);
+ const plan = compileSuccessorPlan(record, {
+ expiresAt: '9999-12-31T23:59:59.999Z',
+ stageStates: stages,
+ });
+
+ resetOwnedDatabase(ownership);
+ const application = applySuccessorPlanOnce({
+ database: DATABASE,
+ sql: plan.sql,
+ stateQuery,
+ baselineState: stages[0],
+ terminalState: stages[11],
+ });
+ const success = application.result;
+ const successLines = success.stdout.trim().split(/\r?\n/).filter(Boolean);
+ const successReadback = JSON.parse(successLines.at(-1));
+ const committed = readState(DATABASE, stateQuery);
+ if (!equal(successReadback, stages[11]) || !equal(committed, stages[11])) {
+ fail('LOCAL_REHEARSAL_COMMIT_READBACK_INVALID');
+ }
+
+ resetOwnedDatabase(ownership);
+ const rollback = psql(DATABASE, `${plan.sql}\nDO $forced$ BEGIN RAISE EXCEPTION 'LOCAL_FORCED_ROLLBACK'; END $forced$;\n`, {
+ singleTransaction: true,
+ allowFailure: true,
+ });
+ if (rollback.status === 0 || !/LOCAL_FORCED_ROLLBACK/.test(rollback.stderr)) {
+ fail('LOCAL_REHEARSAL_FORCED_ROLLBACK_MISSING');
+ }
+ const rolledBack = readState(DATABASE, stateQuery);
+ if (!equal(rolledBack, stages[0])) fail('LOCAL_REHEARSAL_ROLLBACK_DRIFT');
+
+ resetOwnedDatabase(ownership);
+ for (let index = 0; index < 10; index += 1) applyRawMigration(record, index);
+ const cleanPreimage = readState(DATABASE, stateQuery);
+ if (!equal(cleanPreimage, stages[10])) fail('LOCAL_REHEARSAL_STAGE10_REPLAY_DRIFT');
+ tamperFinishCleanupPreimage();
+ const tamperedBefore = readState(DATABASE, stateQuery);
+ const wrongPreimage = psql(DATABASE, record.materials[10].bytes, { allowFailure: true });
+ if (wrongPreimage.status === 0 || !/REVIEW_SHARED_SOURCE_DRIFT/.test(wrongPreimage.stderr)) {
+ fail('LOCAL_REHEARSAL_WRONG_PREIMAGE_NOT_REJECTED');
+ }
+ const tamperedAfter = readState(DATABASE, stateQuery);
+ if (!equal(tamperedBefore, tamperedAfter)
+ || tamperedAfter.bundle.targetCount !== 10
+ || tamperedAfter.bundle.ledgerCount !== 96) {
+ fail('LOCAL_REHEARSAL_WRONG_PREIMAGE_ROLLBACK_DRIFT');
+ }
+
+ const securityInvokerDriftRejected = assertCatalogDriftRejected({
+ label: 'security-invoker',
+ ownership,
+ plan,
+ stages,
+ stateQuery,
+ tamperSql: `ALTER VIEW public.admin_evaluation_related_rows SET (security_invoker=false);`,
+ });
+ const columnAclDriftRejected = assertCatalogDriftRejected({
+ label: 'column-acl',
+ ownership,
+ plan,
+ stages,
+ stateQuery,
+ tamperSql: `GRANT UPDATE(approved_name) ON TABLE public.restaurants TO anon;`,
+ });
+ const membershipInheritOptionDriftRejected = assertCatalogDriftRejected({
+ label: 'membership-inherit-option',
+ ownership,
+ plan,
+ stages,
+ stateQuery,
+ tamperSql: `GRANT ${FIXTURE_PARENT_ROLE} TO ${FIXTURE_MEMBER_ROLE} WITH ADMIN FALSE, INHERIT FALSE, SET TRUE GRANTED BY supabase_admin;`,
+ restoreSql: `GRANT ${FIXTURE_PARENT_ROLE} TO ${FIXTURE_MEMBER_ROLE} WITH ADMIN FALSE, INHERIT TRUE, SET TRUE GRANTED BY supabase_admin;`,
+ });
+ const membershipSetOptionDriftRejected = assertCatalogDriftRejected({
+ label: 'membership-set-option',
+ ownership,
+ plan,
+ stages,
+ stateQuery,
+ tamperSql: `GRANT ${FIXTURE_PARENT_ROLE} TO ${FIXTURE_MEMBER_ROLE} WITH ADMIN FALSE, INHERIT TRUE, SET FALSE GRANTED BY supabase_admin;`,
+ restoreSql: `GRANT ${FIXTURE_PARENT_ROLE} TO ${FIXTURE_MEMBER_ROLE} WITH ADMIN FALSE, INHERIT TRUE, SET TRUE GRANTED BY supabase_admin;`,
+ });
+ const contendedAdmissionExpiryRejected = await assertContendedExpiryRejected(record, stages, stateQuery, ownership);
+
+ proof = {
+ schemaVersion: 1,
+ status: 'passed',
+ environment: {
+ dockerContext: DOCKER_CONTEXT,
+ dockerContainer: DOCKER_CONTAINER,
+ engine: template.engine,
+ templateDatabase: TEMPLATE_DATABASE,
+ temporaryDatabase: DATABASE,
+ },
+ input: template,
+ ...currentBindings,
+ predecessorSources: PREDECESSOR_PATHS.map(path => ({
+ path,
+ sha256: sha256(readFileSync(resolve(REPOSITORY_ROOT, path))),
+ })),
+ migrationCount: record.manifest.migrations.length,
+ statementCount: record.manifest.migrations.reduce((sum, migration) => sum + migration.statementCount, 0),
+ stageStatesSha256: sha256(JSON.stringify(canonical(stages))),
+ stages: stages.map(summarizeStage),
+ results: {
+ advisoryContentionObserved: true,
+ atomicCommit: true,
+ atomicCommitOutcome: application.outcome,
+ atomicRollbackExact: true,
+ catalogDriftStatePreserved: true,
+ columnAclDriftRejected,
+ compiledSqlBytes: Buffer.byteLength(plan.sql),
+ compiledSqlSha256: sha256(plan.sql),
+ contendedAdmissionExpiryRejected,
+ contendedAdmissionExpiryStatePreserved: true,
+ membershipOptionsBaseline: { inherit: true, set: true },
+ membershipInheritOptionDriftRejected,
+ membershipSetOptionDriftRejected,
+ securityInvokerBaseline: true,
+ securityInvokerDriftRejected,
+ wrongFinishPreimageRejected: true,
+ wrongFinishPreimageRollbackExact: true,
+ terminalLedgerCount: committed.bundle.ledgerCount,
+ terminalContractReady: committed.state.terminalContractReady,
+ },
+ sourceMutation: false,
+ hostedWrites: false,
+ operatingWrites: false,
+ };
+ } finally {
+ cleanupOwnedFixtures(ownership);
+ }
+ proof.temporaryDatabaseDropped = true;
+ proof.fixtureRolesDropped = true;
+ const currentProofBytes = Buffer.from(`${JSON.stringify(canonical(proof))}\n`);
+ process.stdout.write(`${JSON.stringify({
+ current: {
+ classification: 'current-source-verification',
+ proofSha256: sha256(currentProofBytes),
+ bindings: currentBindings,
+ proof,
+ },
+ retained: retainedProof,
+ })}\n`);
+}
+
+if (process.argv[1] && resolve(process.argv[1]) === fileURLToPath(import.meta.url)) await run();
diff --git a/apps/web/performance/review-db-successor-20261012/verify-review-media-sql-successor.mjs b/apps/web/performance/review-db-successor-20261012/verify-review-media-sql-successor.mjs
new file mode 100644
index 0000000000..6f7c1560ba
--- /dev/null
+++ b/apps/web/performance/review-db-successor-20261012/verify-review-media-sql-successor.mjs
@@ -0,0 +1,1074 @@
+#!/usr/bin/env node
+
+/**
+ * Local-only PostgreSQL 17.6 rehearsal for the held eleven-stage successor.
+ *
+ * The harness clones the existing schema-only ranking_clone template into one
+ * per-process, owned database name. It refuses a reused target, never connects
+ * to a hosted endpoint, and drops the owned database before emitting its
+ * current-source result.
+ */
+import { createHash, randomBytes } from 'node:crypto';
+import { existsSync, readFileSync } from 'node:fs';
+import { dirname, resolve } from 'node:path';
+import { fileURLToPath } from 'node:url';
+import { spawn, spawnSync } from 'node:child_process';
+
+import {
+ buildSuccessorStateQuery,
+ compileSuccessorPlan,
+ loadSuccessorManifest,
+} from '../../scripts/review-media-sql-successor.mjs';
+
+const HERE = dirname(fileURLToPath(import.meta.url));
+const REPOSITORY_ROOT = resolve(HERE, '../../../..');
+const OUTPUT_PATH = resolve(HERE, 'pg17-local-replay.json');
+const CHECKSUMS_PATH = resolve(HERE, 'SHA256SUMS');
+const RETAINED_PROOF_REPOSITORY_PATH = 'apps/web/performance/review-db-successor-20261012/pg17-local-replay.json';
+const DOCKER_CONTEXT = process.env.TZUDONG_REHEARSAL_DOCKER_CONTEXT || 'colima-tzudong-catalog-20261007';
+const DOCKER_CONTAINER = process.env.TZUDONG_REHEARSAL_DOCKER_CONTAINER || 'tzudong-ranking-clone-20261008';
+const TEMPLATE_DATABASE = 'ranking_clone';
+const DATABASE = `review_successor_local_${randomBytes(16).toString('hex')}`;
+const FIXTURE_PASSWORD = 'fixture-only';
+const FIXTURE_PARENT_ROLE = 'review_successor_fixture_parent_20261012';
+const FIXTURE_MEMBER_ROLE = 'review_successor_fixture_member_20261012';
+const FIXTURE_OWNER_PREFIX = 'tzudong-review-successor-fixture';
+const EXPIRY_APPLICATION_NAME = 'tzudong-review-successor-expiry-fixture';
+const PREDECESSOR_PATHS = Object.freeze([
+ 'backend/supabase/migrations/20261004190259_admin_record_guarded_actions.sql',
+ 'backend/supabase/migrations/20261004192657_admin_evaluation_raw_warning_groups.sql',
+ 'backend/supabase/migrations/20261004194715_admin_evaluation_raw_warning_invoker_contract.sql',
+ 'backend/supabase/migrations/20261009022915_restaurant_review_manual_preview_eligibility.sql',
+ 'backend/supabase/migrations/20261009091342_admin_record_private_verification_cleanup.sql',
+ 'backend/supabase/migrations/20261009101645_admin_user_management_rpc_forward.sql',
+]);
+const sha256 = value => createHash('sha256').update(value).digest('hex');
+const quote = value => `'${String(value).replaceAll("'", "''")}'`;
+const canonical = value => Array.isArray(value)
+ ? value.map(canonical)
+ : value && typeof value === 'object'
+ ? Object.fromEntries(Object.keys(value).sort().map(key => [key, canonical(value[key])]))
+ : value;
+const equal = (left, right) => JSON.stringify(canonical(left)) === JSON.stringify(canonical(right));
+
+function fail(code, detail = '') {
+ throw new Error(detail ? `${code}:${detail}` : code);
+}
+
+function docker(args, { input, allowFailure = false } = {}) {
+ const result = spawnSync('docker', ['--context', DOCKER_CONTEXT, ...args], {
+ encoding: 'utf8',
+ input,
+ maxBuffer: 64 * 1024 * 1024,
+ });
+ if (!allowFailure && (result.error || result.status !== 0)) {
+ const sqlstates = [...(result.stderr || '').matchAll(/(?:ERROR|FATAL):\s+([0-9A-Z]{5}):/g)]
+ .map(match => match[1]);
+ const diagnostic = (result.stderr || '').split(/\r?\n/)
+ .find(line => /(?:ERROR|FATAL):/.test(line))
+ ?.replace(/\s+/g, ' ').slice(0, 240) || '';
+ fail('LOCAL_REHEARSAL_COMMAND_FAILED', [sqlstates.join(',') || String(result.status), diagnostic]
+ .filter(Boolean).join(':'));
+ }
+ return result;
+}
+
+function psql(database, sql, { actor = 'postgres', singleTransaction = false, allowFailure = false } = {}) {
+ const options = [
+ 'exec', '-i', '-e', `PGPASSWORD=${FIXTURE_PASSWORD}`, DOCKER_CONTAINER,
+ 'psql', '-XAtq', '-v', 'ON_ERROR_STOP=1', '-v', 'VERBOSITY=terse',
+ ];
+ if (singleTransaction) options.push('--single-transaction');
+ options.push('-h', '127.0.0.1', '-U', actor, '-d', database, '--file=-');
+ return docker(options, { input: sql, allowFailure });
+}
+
+function scalar(database, sql, actor = 'postgres') {
+ return psql(database, `${sql}\n`, { actor }).stdout.trim();
+}
+
+function readState(database, stateQuery) {
+ const output = scalar(database, `${stateQuery};`);
+ const lines = output.split(/\r?\n/).filter(Boolean);
+ if (lines.length !== 1) fail('LOCAL_REHEARSAL_STATE_SHAPE_INVALID', String(lines.length));
+ try {
+ return JSON.parse(lines[0]);
+ } catch {
+ fail('LOCAL_REHEARSAL_STATE_JSON_INVALID');
+ }
+}
+
+export function applySuccessorPlanOnce({
+ database,
+ sql,
+ stateQuery,
+ baselineState,
+ terminalState,
+ psqlImpl = psql,
+ readStateImpl = readState,
+}) {
+ try {
+ return Object.freeze({
+ outcome: 'acknowledged',
+ result: psqlImpl(database, sql, { singleTransaction: true }),
+ });
+ } catch {
+ let observedState;
+ try {
+ observedState = readStateImpl(database, stateQuery);
+ } catch {
+ fail('LOCAL_REHEARSAL_APPLICATION_OUTCOME_UNCERTAIN');
+ }
+ if (equal(observedState, terminalState)) {
+ return Object.freeze({
+ outcome: 'committed-after-acknowledgement-loss',
+ result: Object.freeze({ stdout: `${JSON.stringify(observedState)}\n` }),
+ });
+ }
+ if (equal(observedState, baselineState)) {
+ fail('LOCAL_REHEARSAL_APPLICATION_ROLLED_BACK');
+ }
+ fail('LOCAL_REHEARSAL_APPLICATION_STATE_AMBIGUOUS');
+ }
+}
+
+function ledgerInsert(migration) {
+ const vector = JSON.stringify(migration.originalStatementVector);
+ const tag = `$vector_${sha256(vector).slice(0, 20)}$`;
+ return `INSERT INTO supabase_migrations.schema_migrations(version,statements,name)
+SELECT ${quote(migration.version)},ARRAY(SELECT jsonb_array_elements_text(${tag}${vector}${tag}::jsonb)),${quote(migration.name)};`;
+}
+
+function fixtureSql() {
+ return `
+DO $fixture$
+BEGIN
+ IF (SELECT count(*) FROM supabase_migrations.schema_migrations)<>0 THEN
+ RAISE EXCEPTION 'LOCAL_REHEARSAL_LEDGER_NOT_EMPTY';
+ END IF;
+ IF (SELECT count(*) FROM public.profiles)<>0 OR (SELECT count(*) FROM public.reviews)<>0 THEN
+ RAISE EXCEPTION 'LOCAL_REHEARSAL_PERSONAL_ROWS_PRESENT';
+ END IF;
+END $fixture$;
+INSERT INTO supabase_migrations.schema_migrations(version,statements,name)
+SELECT lpad(value::text,14,'0'),ARRAY['local schema-only predecessor fixture'],format('local_predecessor_%s',value)
+FROM generate_series(1,86) value;
+INSERT INTO pipeline_control.restaurant_review_policy(singleton,enabled)
+VALUES(true,false)
+ON CONFLICT(singleton) DO UPDATE SET enabled=false,operator_id=NULL,last_run_at=NULL;
+INSERT INTO pipeline_control.admin_evaluation_catalog_revision(singleton,revision)
+VALUES(true,1)
+ON CONFLICT(singleton) DO NOTHING;
+`;
+}
+
+function databaseMetadataSql(databaseName) {
+ const projection = alias => `jsonb_build_object(
+ 'name',${alias}.datname,
+ 'owner',pg_get_userbyid(${alias}.datdba),
+ 'encoding',pg_encoding_to_char(${alias}.encoding),
+ 'collate',${alias}.datcollate,
+ 'ctype',${alias}.datctype,
+ 'localeProvider',${alias}.datlocprovider::text,
+ 'allowConnections',${alias}.datallowconn,
+ 'isTemplate',${alias}.datistemplate
+ )`;
+ return `SELECT jsonb_build_object(
+ 'target',(SELECT ${projection('target_database')} FROM pg_database target_database
+ WHERE target_database.datname=${quote(databaseName)}),
+ 'template',(SELECT ${projection('template_database')} FROM pg_database template_database
+ WHERE template_database.datname=${quote(TEMPLATE_DATABASE)})
+)::text;`;
+}
+
+function inspectDatabaseOwnership(databaseName, { scalarImpl = scalar } = {}) {
+ let state;
+ try {
+ state = JSON.parse(scalarImpl('postgres', databaseMetadataSql(databaseName), 'supabase_admin'));
+ } catch {
+ fail('LOCAL_REHEARSAL_DATABASE_STATE_INVALID');
+ }
+ if (!state
+ || typeof state !== 'object'
+ || !Object.hasOwn(state, 'target')
+ || !databaseMetadataValid(state.template)
+ || state.template.name !== TEMPLATE_DATABASE
+ || (state.target !== null && !databaseMetadataValid(state.target))) {
+ fail('LOCAL_REHEARSAL_DATABASE_STATE_INVALID');
+ }
+ return state;
+}
+
+function databaseMetadataValid(metadata) {
+ return metadata
+ && typeof metadata === 'object'
+ && ['name', 'owner', 'encoding', 'collate', 'ctype', 'localeProvider']
+ .every(field => typeof metadata[field] === 'string' && metadata[field].length > 0)
+ && typeof metadata.allowConnections === 'boolean'
+ && typeof metadata.isTemplate === 'boolean';
+}
+
+function databaseTargetAbsent(state, databaseName) {
+ return state?.target === null && state?.template?.name === TEMPLATE_DATABASE && databaseName === DATABASE;
+}
+
+function databaseTargetOwned(state, baseline, databaseName) {
+ const target = state?.target;
+ const template = state?.template;
+ return databaseTargetAbsent(baseline, databaseName)
+ && target?.name === databaseName
+ && target.owner === 'postgres'
+ && target.allowConnections === true
+ && target.isTemplate === false
+ && template?.name === TEMPLATE_DATABASE
+ && equal(template, baseline.template)
+ && target.encoding === template.encoding
+ && target.collate === template.collate
+ && target.ctype === template.ctype
+ && target.localeProvider === template.localeProvider;
+}
+
+export function createOwnedDatabase(ownership, {
+ scalarImpl = scalar,
+ psqlImpl = psql,
+ readFileImpl = readFileSync,
+} = {}) {
+ if (!ownership
+ || ownership.database !== false
+ || ownership.databaseAttempted
+ || ownership.databaseName
+ || ownership.databaseBaseline) {
+ fail('LOCAL_REHEARSAL_OWNERSHIP_STATE_INVALID');
+ }
+ const baseline = inspectDatabaseOwnership(DATABASE, { scalarImpl });
+ if (!databaseTargetAbsent(baseline, DATABASE)) fail('LOCAL_REHEARSAL_TARGET_ALREADY_EXISTS');
+ ownership.databaseName = DATABASE;
+ ownership.databaseBaseline = baseline;
+ ownership.databaseAttempted = true;
+ let creationAcknowledged = false;
+ try {
+ psqlImpl('postgres', `CREATE DATABASE ${DATABASE} TEMPLATE ${TEMPLATE_DATABASE} OWNER postgres;`, { actor: 'supabase_admin' });
+ creationAcknowledged = true;
+ } catch {
+ // CREATE is intentionally never replayed. The exact catalog readback below
+ // distinguishes a committed CREATE from a failure before mutation.
+ }
+ let observed;
+ try {
+ observed = inspectDatabaseOwnership(DATABASE, { scalarImpl });
+ } catch {
+ fail('LOCAL_REHEARSAL_DATABASE_READBACK_FAILED');
+ }
+ if (!databaseTargetOwned(observed, baseline, DATABASE)) {
+ if (databaseTargetAbsent(observed, DATABASE)) {
+ ownership.databaseAttempted = false;
+ ownership.databaseName = null;
+ ownership.databaseBaseline = null;
+ fail('LOCAL_REHEARSAL_DATABASE_CREATION_UNCONFIRMED');
+ }
+ fail('LOCAL_REHEARSAL_DATABASE_OWNERSHIP_UNPROVEN');
+ }
+ ownership.database = true;
+ ownership.databaseCreationAcknowledged = creationAcknowledged;
+ ownership.databaseCreationReconciled = !creationAcknowledged;
+ PREDECESSOR_PATHS.forEach((path, index) => {
+ try {
+ psqlImpl(DATABASE, readFileImpl(resolve(REPOSITORY_ROOT, path)));
+ } catch (error) {
+ fail('LOCAL_REHEARSAL_PREDECESSOR_FAILED', `${index + 1}:${error.message}`);
+ }
+ });
+ psqlImpl(DATABASE, fixtureSql());
+ return true;
+}
+
+export function dropOwnedDatabase(ownership, { scalarImpl = scalar, psqlImpl = psql } = {}) {
+ if (!ownership?.database && !ownership?.databaseAttempted) return false;
+ if (ownership.databaseName !== DATABASE
+ || !ownership.databaseBaseline
+ || !databaseTargetAbsent(ownership.databaseBaseline, DATABASE)) {
+ fail('LOCAL_REHEARSAL_DATABASE_OWNERSHIP_STATE_INVALID');
+ }
+ let before;
+ try {
+ before = inspectDatabaseOwnership(DATABASE, { scalarImpl });
+ } catch {
+ fail('LOCAL_REHEARSAL_DATABASE_CLEANUP_READBACK_FAILED');
+ }
+ if (databaseTargetAbsent(before, DATABASE)) {
+ const wasOwned = ownership.database === true;
+ ownership.database = false;
+ ownership.databaseAttempted = false;
+ ownership.databaseDropped = wasOwned;
+ ownership.databaseCleanupReconciled = true;
+ ownership.databaseName = null;
+ ownership.databaseBaseline = null;
+ return false;
+ }
+ if (!databaseTargetOwned(before, ownership.databaseBaseline, DATABASE)) {
+ fail('LOCAL_REHEARSAL_DATABASE_OWNERSHIP_UNPROVEN');
+ }
+ ownership.database = true;
+ let removalAcknowledged = false;
+ try {
+ psqlImpl('postgres', `DROP DATABASE ${DATABASE} WITH (FORCE);`, { actor: 'supabase_admin' });
+ removalAcknowledged = true;
+ } catch {
+ // A lost DROP acknowledgement is reconciled once; DROP is not replayed.
+ }
+ let after;
+ try {
+ after = inspectDatabaseOwnership(DATABASE, { scalarImpl });
+ } catch {
+ fail('LOCAL_REHEARSAL_DATABASE_CLEANUP_READBACK_FAILED');
+ }
+ if (!databaseTargetAbsent(after, DATABASE)) {
+ if (!databaseTargetOwned(after, ownership.databaseBaseline, DATABASE)) {
+ fail('LOCAL_REHEARSAL_DATABASE_OWNERSHIP_UNPROVEN');
+ }
+ fail('LOCAL_REHEARSAL_DATABASE_DROP_UNCONFIRMED');
+ }
+ ownership.database = false;
+ ownership.databaseAttempted = false;
+ ownership.databaseDropped = true;
+ ownership.databaseRemovalAcknowledged = removalAcknowledged;
+ ownership.databaseCleanupReconciled = !removalAcknowledged;
+ ownership.databaseName = null;
+ ownership.databaseBaseline = null;
+ return true;
+}
+
+function resetOwnedDatabase(ownership) {
+ dropOwnedDatabase(ownership);
+ createOwnedDatabase(ownership);
+}
+
+function fixtureRoleStateQuery() {
+ return `WITH fixture_roles AS (
+ SELECT fixture_role.rolname,
+ fixture_role.rolcanlogin,
+ fixture_role.rolsuper,
+ fixture_role.rolcreatedb,
+ fixture_role.rolcreaterole,
+ fixture_role.rolinherit,
+ fixture_role.rolreplication,
+ fixture_role.rolbypassrls,
+ fixture_role.rolconnlimit,
+ fixture_role.rolvaliduntil,
+ fixture_role.rolconfig,
+ coalesce(shobj_description(fixture_role.oid,'pg_authid'),'') AS owner_marker
+ FROM pg_roles fixture_role
+ WHERE fixture_role.rolname IN(${quote(FIXTURE_PARENT_ROLE)},${quote(FIXTURE_MEMBER_ROLE)})
+), fixture_memberships AS (
+ SELECT parent.rolname AS parent,
+ member.rolname AS member,
+ grantor.rolname AS grantor,
+ membership.admin_option,
+ membership.inherit_option,
+ membership.set_option
+ FROM pg_auth_members membership
+ JOIN pg_roles parent ON parent.oid=membership.roleid
+ JOIN pg_roles member ON member.oid=membership.member
+ JOIN pg_roles grantor ON grantor.oid=membership.grantor
+ WHERE parent.rolname IN(${quote(FIXTURE_PARENT_ROLE)},${quote(FIXTURE_MEMBER_ROLE)})
+ OR member.rolname IN(${quote(FIXTURE_PARENT_ROLE)},${quote(FIXTURE_MEMBER_ROLE)})
+)
+SELECT jsonb_build_object(
+ 'roles',coalesce((SELECT jsonb_agg(to_jsonb(fixture_role) ORDER BY fixture_role.rolname) FROM fixture_roles fixture_role),'[]'::jsonb),
+ 'memberships',coalesce((SELECT jsonb_agg(to_jsonb(fixture_membership) ORDER BY fixture_membership.parent,fixture_membership.member,fixture_membership.grantor) FROM fixture_memberships fixture_membership),'[]'::jsonb)
+)::text;`;
+}
+
+function inspectFixtureMembership({ scalarImpl = scalar } = {}) {
+ let output;
+ try {
+ output = scalarImpl('postgres', fixtureRoleStateQuery(), 'supabase_admin');
+ } catch {
+ fail('LOCAL_REHEARSAL_FIXTURE_ROLE_READBACK_FAILED');
+ }
+ let state;
+ try {
+ state = JSON.parse(output);
+ } catch {
+ fail('LOCAL_REHEARSAL_FIXTURE_ROLE_READBACK_INVALID');
+ }
+ if (!Array.isArray(state?.roles) || !Array.isArray(state?.memberships)) {
+ fail('LOCAL_REHEARSAL_FIXTURE_ROLE_READBACK_INVALID');
+ }
+ return Object.freeze({
+ roles: Object.freeze(state.roles.map(role => Object.freeze({ ...role }))),
+ memberships: Object.freeze(state.memberships.map(membership => Object.freeze({ ...membership }))),
+ });
+}
+
+function fixtureMembershipAbsent(state) {
+ return state.roles.length === 0 && state.memberships.length === 0;
+}
+
+function fixtureMembershipOwned(state, token) {
+ const roles = new Map(state.roles.map(role => [role.rolname, role]));
+ const parent = roles.get(FIXTURE_PARENT_ROLE);
+ const member = roles.get(FIXTURE_MEMBER_ROLE);
+ const membership = state.memberships[0];
+ const sharedRoleShape = role => role
+ && role.rolcanlogin === false
+ && role.rolsuper === false
+ && role.rolcreatedb === false
+ && role.rolcreaterole === false
+ && role.rolreplication === false
+ && role.rolbypassrls === false
+ && role.rolconnlimit === -1
+ && role.rolvaliduntil === null
+ && role.rolconfig === null;
+ return state.roles.length === 2
+ && sharedRoleShape(parent)
+ && parent.rolinherit === false
+ && parent.owner_marker === `${FIXTURE_OWNER_PREFIX}:${token}:parent`
+ && sharedRoleShape(member)
+ && member.rolinherit === true
+ && member.owner_marker === `${FIXTURE_OWNER_PREFIX}:${token}:member`
+ && state.memberships.length === 1
+ && membership.parent === FIXTURE_PARENT_ROLE
+ && membership.member === FIXTURE_MEMBER_ROLE
+ && membership.grantor === 'supabase_admin'
+ && membership.admin_option === false
+ && membership.inherit_option === true
+ && membership.set_option === true;
+}
+
+export function createFixtureMembership(ownership, {
+ scalarImpl = scalar,
+ psqlImpl = psql,
+ generateToken = () => randomBytes(16).toString('hex'),
+} = {}) {
+ if (!ownership
+ || ownership.fixtureMembership !== false
+ || ownership.fixtureMembershipAttempted
+ || ownership.fixtureMembershipToken
+ || ownership.fixtureMembershipBaseline) {
+ fail('LOCAL_REHEARSAL_FIXTURE_ROLE_OWNERSHIP_STATE_INVALID');
+ }
+ const baseline = inspectFixtureMembership({ scalarImpl });
+ ownership.fixtureMembershipBaseline = baseline;
+ if (!fixtureMembershipAbsent(baseline)) fail('LOCAL_REHEARSAL_FIXTURE_ROLE_EXISTS');
+ const token = generateToken();
+ if (!/^[0-9a-f]{32}$/.test(token)) fail('LOCAL_REHEARSAL_FIXTURE_ROLE_TOKEN_INVALID');
+ ownership.fixtureMembershipToken = token;
+ ownership.fixtureMembershipAttempted = true;
+ let creationAcknowledged = false;
+ try {
+ psqlImpl('postgres', `
+BEGIN;
+CREATE ROLE ${FIXTURE_PARENT_ROLE} NOLOGIN NOSUPERUSER NOCREATEDB NOCREATEROLE NOINHERIT NOREPLICATION NOBYPASSRLS;
+CREATE ROLE ${FIXTURE_MEMBER_ROLE} NOLOGIN NOSUPERUSER NOCREATEDB NOCREATEROLE INHERIT NOREPLICATION NOBYPASSRLS;
+COMMENT ON ROLE ${FIXTURE_PARENT_ROLE} IS ${quote(`${FIXTURE_OWNER_PREFIX}:${token}:parent`)};
+COMMENT ON ROLE ${FIXTURE_MEMBER_ROLE} IS ${quote(`${FIXTURE_OWNER_PREFIX}:${token}:member`)};
+GRANT ${FIXTURE_PARENT_ROLE} TO ${FIXTURE_MEMBER_ROLE}
+ WITH ADMIN FALSE, INHERIT TRUE, SET TRUE GRANTED BY supabase_admin;
+COMMIT;
+`, { actor: 'supabase_admin' });
+ creationAcknowledged = true;
+ } catch {}
+ const observed = inspectFixtureMembership({ scalarImpl });
+ if (!fixtureMembershipOwned(observed, token)) {
+ ownership.fixtureMembershipAttempted = false;
+ if (fixtureMembershipAbsent(observed)) fail('LOCAL_REHEARSAL_FIXTURE_ROLE_CREATION_UNCONFIRMED');
+ fail('LOCAL_REHEARSAL_FIXTURE_ROLE_OWNERSHIP_UNPROVEN');
+ }
+ ownership.fixtureMembership = true;
+ ownership.fixtureMembershipCreationAcknowledged = creationAcknowledged;
+ ownership.fixtureMembershipCreationReconciled = !creationAcknowledged;
+ return true;
+}
+
+export function dropFixtureMembership(ownership, {
+ scalarImpl = scalar,
+ psqlImpl = psql,
+} = {}) {
+ if (!ownership?.fixtureMembership && !ownership?.fixtureMembershipAttempted) return false;
+ const token = ownership.fixtureMembershipToken;
+ if (!/^[0-9a-f]{32}$/.test(token || '')
+ || !ownership.fixtureMembershipBaseline
+ || !fixtureMembershipAbsent(ownership.fixtureMembershipBaseline)) {
+ fail('LOCAL_REHEARSAL_FIXTURE_ROLE_OWNERSHIP_STATE_INVALID');
+ }
+ const before = inspectFixtureMembership({ scalarImpl });
+ if (fixtureMembershipAbsent(before)) {
+ const wasOwned = ownership.fixtureMembership === true;
+ ownership.fixtureMembership = false;
+ ownership.fixtureMembershipAttempted = false;
+ ownership.fixtureMembershipDropped = wasOwned;
+ ownership.fixtureMembershipCleanupReconciled = true;
+ return wasOwned;
+ }
+ if (!fixtureMembershipOwned(before, token)) {
+ fail('LOCAL_REHEARSAL_FIXTURE_ROLE_OWNERSHIP_UNPROVEN');
+ }
+ // An earlier creation readback can fail after COMMIT. Exact token, shape,
+ // membership, and empty preimage establish this run's cleanup authority.
+ ownership.fixtureMembership = true;
+ let removalAcknowledged = false;
+ try {
+ psqlImpl('postgres', `
+BEGIN;
+REVOKE ${FIXTURE_PARENT_ROLE} FROM ${FIXTURE_MEMBER_ROLE} GRANTED BY supabase_admin;
+DROP ROLE ${FIXTURE_MEMBER_ROLE};
+DROP ROLE ${FIXTURE_PARENT_ROLE};
+COMMIT;
+`, { actor: 'supabase_admin' });
+ removalAcknowledged = true;
+ } catch {}
+ const after = inspectFixtureMembership({ scalarImpl });
+ if (!fixtureMembershipAbsent(after)) {
+ if (!fixtureMembershipOwned(after, token)) {
+ fail('LOCAL_REHEARSAL_FIXTURE_ROLE_OWNERSHIP_UNPROVEN');
+ }
+ fail('LOCAL_REHEARSAL_FIXTURE_ROLE_REMOVAL_UNCONFIRMED');
+ }
+ ownership.fixtureMembership = false;
+ ownership.fixtureMembershipAttempted = false;
+ ownership.fixtureMembershipDropped = true;
+ ownership.fixtureMembershipRemovalAcknowledged = removalAcknowledged;
+ return true;
+}
+
+function cleanupOwnedFixtures(ownership) {
+ const failures = [];
+ try {
+ dropOwnedDatabase(ownership);
+ } catch (error) {
+ failures.push(`database:${error.message}`);
+ }
+ try {
+ dropFixtureMembership(ownership);
+ } catch (error) {
+ failures.push(`roles:${error.message}`);
+ }
+ if (failures.length > 0) fail('LOCAL_REHEARSAL_CLEANUP_FAILED', failures.join(','));
+}
+
+export function inspectRetainedProof({
+ outputPath = OUTPUT_PATH,
+ checksumsPath = CHECKSUMS_PATH,
+ checksumsDigestPath = `${checksumsPath}.sha256`,
+ currentBindings,
+ existsImpl = existsSync,
+ readFileImpl = readFileSync,
+} = {}) {
+ if (!existsImpl(outputPath)) return Object.freeze({ present: false });
+ if (!currentBindings || typeof currentBindings !== 'object') {
+ fail('LOCAL_REHEARSAL_CURRENT_BINDINGS_INVALID');
+ }
+ if (!existsImpl(checksumsDigestPath)) fail('LOCAL_REHEARSAL_CHECKSUMS_DIGEST_MISSING');
+ const checksumBytes = readFileImpl(checksumsPath);
+ const checksumDigest = readFileImpl(checksumsDigestPath, 'utf8').trim();
+ if (!/^[a-f0-9]{64}$/.test(checksumDigest)) fail('LOCAL_REHEARSAL_CHECKSUMS_DIGEST_INVALID');
+ if (sha256(checksumBytes) !== checksumDigest) fail('LOCAL_REHEARSAL_CHECKSUMS_SHA256_MISMATCH');
+ const checksumLines = checksumBytes.toString('utf8').split(/\r?\n/).filter(Boolean);
+ const matchingLines = checksumLines.filter(line => line.endsWith(` ${RETAINED_PROOF_REPOSITORY_PATH}`));
+ if (matchingLines.length !== 1) fail('LOCAL_REHEARSAL_RETAINED_CHECKSUM_MISSING');
+ const checksumMatch = matchingLines[0].match(/^([0-9a-f]{64}) (.+)$/);
+ if (!checksumMatch || checksumMatch[2] !== RETAINED_PROOF_REPOSITORY_PATH) {
+ fail('LOCAL_REHEARSAL_RETAINED_CHECKSUM_INVALID');
+ }
+ const bytes = readFileImpl(outputPath);
+ const actualSha256 = sha256(bytes);
+ if (actualSha256 !== checksumMatch[1]) fail('LOCAL_REHEARSAL_RETAINED_SHA256_MISMATCH');
+ let retained;
+ try {
+ retained = JSON.parse(bytes.toString('utf8'));
+ } catch {
+ fail('LOCAL_REHEARSAL_RETAINED_JSON_INVALID');
+ }
+ const retainedBindings = {
+ manifestSha256: retained.manifestSha256,
+ rehearsalHarnessSha256: retained.rehearsalHarnessSha256,
+ sourceRoot: retained.sourceRoot,
+ };
+ if (retained.status !== 'passed'
+ || retained.temporaryDatabaseDropped !== true
+ || retained.hostedWrites !== false
+ || retained.operatingWrites !== false
+ || Object.values(retainedBindings).some(value => !/^[0-9a-f]{64}$/.test(value))) {
+ fail('LOCAL_REHEARSAL_RETAINED_SHAPE_INVALID');
+ }
+ const bindingDifferences = Object.keys(retainedBindings)
+ .filter(key => retainedBindings[key] !== currentBindings[key])
+ .map(key => ({
+ field: key,
+ retained: retainedBindings[key],
+ current: currentBindings[key],
+ }));
+ return Object.freeze({
+ present: true,
+ classification: 'retained-historical-evidence',
+ repositoryPath: RETAINED_PROOF_REPOSITORY_PATH,
+ expectedSha256: checksumMatch[1],
+ actualSha256,
+ checksumMapSha256: checksumDigest,
+ bindings: Object.freeze(retainedBindings),
+ currentSourceMatch: bindingDifferences.length === 0,
+ bindingDifferences: Object.freeze(bindingDifferences),
+ usedAsCurrentProof: false,
+ });
+}
+
+function applyRawMigration(record, index) {
+ const migration = record.manifest.migrations[index];
+ const material = record.materials[index];
+ try {
+ psql(DATABASE, material.bytes);
+ psql(DATABASE, `${ledgerInsert(migration)}\n`);
+ } catch (error) {
+ fail('LOCAL_REHEARSAL_MIGRATION_FAILED', `${index + 1}:${error.message}`);
+ }
+}
+
+function collectStageStates(record, stateQuery) {
+ const states = [readState(DATABASE, stateQuery)];
+ for (let index = 0; index < record.manifest.migrations.length; index += 1) {
+ applyRawMigration(record, index);
+ states.push(readState(DATABASE, stateQuery));
+ }
+ return states;
+}
+
+function summarizeStage(stage, index) {
+ return {
+ index,
+ ledgerCount: stage.bundle.ledgerCount,
+ targetCount: stage.bundle.targetCount,
+ prefixRoot: stage.bundle.prefixRoot,
+ suffixRoot: stage.bundle.suffixRoot,
+ schemaRoot: stage.state.schemaRoot,
+ ledgerRoot: stage.state.ledgerRoot,
+ g014Root: stage.state.g014Root,
+ storageRoot: stage.state.storageRoot,
+ finishCleanupBodySha256: stage.state.finishCleanupBodySha256,
+ ownerAuthorityReady: stage.state.ownerAuthorityReady,
+ terminalContractReady: stage.state.terminalContractReady,
+ };
+}
+
+function assertRehearsalStages(record, stages) {
+ const { manifest } = record;
+ if (stages.length !== 12) fail('LOCAL_REHEARSAL_STAGE_COUNT_INVALID');
+ const prefixRoot = stages[0].bundle.prefixRoot;
+ stages.forEach((stage, index) => {
+ if (stage.bundle.ledgerCount !== 86 + index
+ || stage.bundle.priorCount !== 86
+ || stage.bundle.prefixRoot !== prefixRoot
+ || stage.bundle.targetCount !== index
+ || stage.state.activeReviewItems !== 0
+ || stage.state.reviewItems !== 0
+ || stage.state.reviewRuns !== 0
+ || stage.state.reviewPolicyEnabled !== false
+ || stage.state.currentUser !== 'postgres'
+ || stage.state.sessionUser !== 'postgres'
+ || stage.state.databaseName !== DATABASE
+ || stage.state.serverMajor !== 17
+ || stage.state.ownerAuthorityReady !== true
+ || stage.state.terminalContractReady !== (index === 11)) {
+ fail('LOCAL_REHEARSAL_STAGE_INVALID', String(index));
+ }
+ });
+ if (stages[10].state.finishCleanupBodySha256 !== manifest.terminalContract.finishPreimageSha256
+ || stages[11].state.finishCleanupBodySha256 !== manifest.terminalContract.finishPostimageSha256) {
+ fail('LOCAL_REHEARSAL_FINISH_PREIMAGE_INVALID');
+ }
+ if (!equal(stages.slice(0, 3).map(stage => stage.state.g014Root), Array(3).fill(stages[0].state.g014Root))
+ || !equal(stages.slice(4).map(stage => stage.state.g014Root), Array(8).fill(stages[4].state.g014Root))) {
+ fail('LOCAL_REHEARSAL_G014_TRANSITION_INVALID');
+ }
+}
+
+function tamperFinishCleanupPreimage() {
+ psql(DATABASE, `
+BEGIN;
+DO $guard$ BEGIN
+ IF EXISTS(SELECT 1 FROM pg_auth_members
+ WHERE roleid='privacy_workflow_owner'::regrole AND member='postgres'::regrole AND grantor='postgres'::regrole)
+ THEN RAISE EXCEPTION 'LOCAL_REHEARSAL_SELF_GRANT_EXISTS'; END IF;
+END $guard$;
+GRANT privacy_workflow_owner TO postgres WITH ADMIN FALSE,INHERIT FALSE,SET TRUE GRANTED BY postgres;
+SET LOCAL ROLE privacy_workflow_owner;
+DO $tamper$
+DECLARE definition text; source text;
+BEGIN
+ SELECT pg_get_functiondef(oid),prosrc INTO definition,source
+ FROM pg_proc WHERE oid='public.finish_review_media_cleanup()'::regprocedure;
+ EXECUTE replace(definition,source,source || E'\\n-- local-only preimage drift');
+END $tamper$;
+RESET ROLE;
+REVOKE privacy_workflow_owner FROM postgres GRANTED BY postgres;
+COMMIT;
+`);
+}
+
+function assertCatalogDriftRejected({ label, ownership, plan, stages, stateQuery, tamperSql, restoreSql = null }) {
+ resetOwnedDatabase(ownership);
+ const baseline = readState(DATABASE, stateQuery);
+ if (!equal(baseline, stages[0])) fail('LOCAL_REHEARSAL_DRIFT_BASELINE_INVALID', label);
+ psql(DATABASE, tamperSql, { actor: 'supabase_admin' });
+ const driftedBefore = readState(DATABASE, stateQuery);
+ if (driftedBefore.state.schemaRoot === baseline.state.schemaRoot
+ || driftedBefore.state.g014Root !== baseline.state.g014Root
+ || driftedBefore.state.storageRoot !== baseline.state.storageRoot
+ || driftedBefore.state.ledgerRoot !== baseline.state.ledgerRoot) {
+ fail('LOCAL_REHEARSAL_DRIFT_PROJECTION_INVALID', label);
+ }
+ const rejected = psql(DATABASE, plan.sql, { singleTransaction: true, allowFailure: true });
+ if (rejected.status === 0 || !/MIGRATION_PRIOR_STATE_MISMATCH/.test(rejected.stderr)) {
+ fail('LOCAL_REHEARSAL_CATALOG_DRIFT_NOT_REJECTED', label);
+ }
+ const driftedAfter = readState(DATABASE, stateQuery);
+ if (!equal(driftedBefore, driftedAfter)
+ || driftedAfter.bundle.targetCount !== 0
+ || driftedAfter.bundle.ledgerCount !== 86) {
+ fail('LOCAL_REHEARSAL_CATALOG_DRIFT_STATE_CHANGED', label);
+ }
+ if (restoreSql) {
+ psql(DATABASE, restoreSql, { actor: 'supabase_admin' });
+ if (!equal(readState(DATABASE, stateQuery), baseline)) {
+ fail('LOCAL_REHEARSAL_CATALOG_DRIFT_RESTORE_FAILED', label);
+ }
+ }
+ return true;
+}
+
+function startAdvisoryLockHolder() {
+ const args = [
+ '--context', DOCKER_CONTEXT,
+ 'exec', '-i', '-e', `PGPASSWORD=${FIXTURE_PASSWORD}`, DOCKER_CONTAINER,
+ 'psql', '-XAtq', '-v', 'ON_ERROR_STOP=1', '-h', '127.0.0.1', '-U', 'postgres', '-d', DATABASE,
+ ];
+ const child = spawn('docker', args, { stdio: ['pipe', 'pipe', 'pipe'] });
+ let stdout = '';
+ let stderr = '';
+ let settled = false;
+ const ready = new Promise((resolveReady, rejectReady) => {
+ const reject = detail => {
+ if (!settled) {
+ settled = true;
+ rejectReady(new Error(`LOCAL_REHEARSAL_LOCK_HOLDER_FAILED:${detail}`));
+ }
+ };
+ child.on('error', error => reject(error.code || 'SPAWN'));
+ child.stdout.on('data', chunk => {
+ stdout += chunk.toString();
+ if (!settled && stdout.includes('LOCK_HELD')) {
+ settled = true;
+ resolveReady();
+ }
+ });
+ child.stderr.on('data', chunk => { stderr += chunk.toString(); });
+ child.on('exit', code => {
+ if (!settled) reject(`${code}:${stderr.replace(/\s+/g, ' ').slice(0, 120)}`);
+ });
+ });
+ const completion = new Promise((resolveCompletion, rejectCompletion) => {
+ child.on('error', rejectCompletion);
+ child.on('close', code => {
+ if (code === 0) resolveCompletion();
+ else rejectCompletion(new Error(`LOCAL_REHEARSAL_LOCK_HOLDER_FAILED:${code}:${stderr.replace(/\s+/g, ' ').slice(0, 120)}`));
+ });
+ });
+ child.stdin.write(`BEGIN;\nSET LOCAL statement_timeout='10s';\nSET LOCAL idle_in_transaction_session_timeout='10s';\nSELECT pg_advisory_xact_lock(7311754282260211::bigint);\nSELECT 'LOCK_HELD';\n`);
+ let released = false;
+ const release = () => {
+ if (!released) {
+ released = true;
+ child.stdin.end('COMMIT;\n');
+ }
+ };
+ return { child, completion, ready, release };
+}
+
+function startPsqlProcess(database, sql, { applicationName, singleTransaction = false } = {}) {
+ const args = [
+ '--context', DOCKER_CONTEXT,
+ 'exec', '-i', '-e', `PGPASSWORD=${FIXTURE_PASSWORD}`,
+ ];
+ if (applicationName) args.push('-e', `PGAPPNAME=${applicationName}`);
+ args.push(
+ DOCKER_CONTAINER,
+ 'psql', '-XAtq', '-v', 'ON_ERROR_STOP=1', '-v', 'VERBOSITY=terse',
+ );
+ if (singleTransaction) args.push('--single-transaction');
+ args.push('-h', '127.0.0.1', '-U', 'postgres', '-d', database, '--file=-');
+ const child = spawn('docker', args, { stdio: ['pipe', 'pipe', 'pipe'] });
+ let stdout = '';
+ let stderr = '';
+ let inputError = null;
+ child.stdout.on('data', chunk => { stdout += chunk.toString(); });
+ child.stderr.on('data', chunk => { stderr += chunk.toString(); });
+ child.stdin.on('error', error => {
+ if (error.code !== 'EPIPE') inputError = error;
+ });
+ const completion = new Promise((resolveCompletion, rejectCompletion) => {
+ child.on('error', rejectCompletion);
+ child.on('close', status => {
+ if (inputError) rejectCompletion(inputError);
+ else resolveCompletion({ status, stderr, stdout });
+ });
+ });
+ child.stdin.end(sql);
+ return { child, completion };
+}
+
+export async function waitForAdvisoryContention(expiresAt, {
+ nowMs = Date.now,
+ readWaiting = () => scalar(DATABASE, `SELECT count(*) FROM pg_stat_activity a
+ JOIN pg_locks l ON l.pid=a.pid
+ WHERE a.application_name=${quote(EXPIRY_APPLICATION_NAME)}
+ AND l.locktype='advisory' AND NOT l.granted;`, 'supabase_admin'),
+ delayImpl = ms => new Promise(resolveDelay => setTimeout(resolveDelay, ms)),
+} = {}) {
+ const deadline = Math.min(expiresAt - 25, nowMs() + 2000);
+ while (nowMs() < deadline) {
+ const waiting = readWaiting();
+ if (waiting === '1') {
+ if (nowMs() >= expiresAt) fail('LOCAL_REHEARSAL_CONTENTION_OBSERVED_AFTER_EXPIRY');
+ return nowMs();
+ }
+ if (waiting !== '0') fail('LOCAL_REHEARSAL_CONTENTION_CARDINALITY_INVALID');
+ await delayImpl(20);
+ }
+ fail('LOCAL_REHEARSAL_ADVISORY_CONTENTION_NOT_OBSERVED');
+}
+
+async function assertContendedExpiryRejected(record, stages, stateQuery, ownership) {
+ resetOwnedDatabase(ownership);
+ const baseline = readState(DATABASE, stateQuery);
+ if (!equal(baseline, stages[0])) fail('LOCAL_REHEARSAL_EXPIRY_BASELINE_INVALID');
+ const holder = startAdvisoryLockHolder();
+ let apply = null;
+ try {
+ await holder.ready;
+ // Fixture setup and Docker startup must not consume the admitted lock-wait window.
+ const expiresAt = Date.now() + 1500;
+ const expiry = new Date(expiresAt).toISOString();
+ const expiringPlan = compileSuccessorPlan(record, { expiresAt: expiry, stageStates: stages });
+ if (Date.now() >= expiresAt) fail('LOCAL_REHEARSAL_EXPIRY_WINDOW_TOO_SHORT');
+ apply = startPsqlProcess(DATABASE, expiringPlan.sql, {
+ applicationName: EXPIRY_APPLICATION_NAME,
+ singleTransaction: true,
+ });
+ const contentionObservedAt = await waitForAdvisoryContention(expiresAt);
+ if (contentionObservedAt >= expiresAt) fail('LOCAL_REHEARSAL_CONTENTION_OBSERVED_AFTER_EXPIRY');
+ await new Promise(resolveDelay => setTimeout(resolveDelay, Math.max(0, expiresAt - Date.now() + 25)));
+ holder.release();
+ const [rejected] = await Promise.all([apply.completion, holder.completion]);
+ if (rejected.status === 0
+ || !/SUCCESSOR_ADMISSION_EXPIRED/.test(rejected.stderr)
+ || /lock timeout/.test(rejected.stderr)) {
+ fail('LOCAL_REHEARSAL_CONTENDED_EXPIRY_NOT_REJECTED');
+ }
+ } finally {
+ if (apply?.child.exitCode === null) apply.child.kill('SIGTERM');
+ if (holder.child.exitCode === null) holder.child.kill('SIGTERM');
+ }
+ const after = readState(DATABASE, stateQuery);
+ if (!equal(after, baseline) || after.bundle.targetCount !== 0 || after.bundle.ledgerCount !== 86) {
+ fail('LOCAL_REHEARSAL_CONTENDED_EXPIRY_STATE_CHANGED');
+ }
+ return true;
+}
+
+export async function run() {
+ const manifestBytes = readFileSync(resolve(REPOSITORY_ROOT, '.github/review-media-sql-successor.v1.json'));
+ const record = loadSuccessorManifest();
+ const currentBindings = Object.freeze({
+ manifestSha256: sha256(manifestBytes),
+ rehearsalHarnessSha256: sha256(readFileSync(fileURLToPath(import.meta.url))),
+ sourceRoot: record.manifest.sourceRoot,
+ });
+ const retainedProof = inspectRetainedProof({ currentBindings });
+ const stateQuery = buildSuccessorStateQuery(record.manifest);
+ const template = JSON.parse(scalar(TEMPLATE_DATABASE, `SELECT json_build_object(
+ 'engine',current_setting('server_version'),
+ 'ledger',(SELECT count(*) FROM supabase_migrations.schema_migrations),
+ 'manifest',(SELECT count(*) FROM privacy_retention.g014_catalog_contract_manifest),
+ 'profiles',(SELECT count(*) FROM public.profiles),
+ 'reviews',(SELECT count(*) FROM public.reviews)
+ )::text;`, 'supabase_admin'));
+ if (!String(template.engine).startsWith('17.6')
+ || template.ledger !== 0
+ || template.profiles !== 0
+ || template.reviews !== 0) fail('LOCAL_REHEARSAL_TEMPLATE_INVALID');
+
+ const ownership = {
+ database: false,
+ databaseAttempted: false,
+ databaseName: null,
+ databaseBaseline: null,
+ fixtureMembership: false,
+ fixtureMembershipAttempted: false,
+ fixtureMembershipToken: null,
+ fixtureMembershipBaseline: null,
+ };
+ let proof;
+ try {
+ createFixtureMembership(ownership);
+ createOwnedDatabase(ownership);
+ if (scalar(DATABASE, `SELECT coalesce('security_invoker=true'=ANY(c.reloptions),false)
+ FROM pg_class c WHERE c.oid='public.admin_evaluation_related_rows'::regclass;`) !== 't') {
+ fail('LOCAL_REHEARSAL_SECURITY_INVOKER_BASELINE_INVALID');
+ }
+ if (scalar(DATABASE, `SELECT m.inherit_option::text || ':' || m.set_option::text
+ FROM pg_auth_members m
+ WHERE m.roleid=${quote(FIXTURE_PARENT_ROLE)}::regrole
+ AND m.member=${quote(FIXTURE_MEMBER_ROLE)}::regrole
+ AND m.grantor='supabase_admin'::regrole;`, 'supabase_admin') !== 'true:true') {
+ fail('LOCAL_REHEARSAL_MEMBERSHIP_OPTIONS_BASELINE_INVALID');
+ }
+ const stages = collectStageStates(record, stateQuery);
+ assertRehearsalStages(record, stages);
+ const plan = compileSuccessorPlan(record, {
+ expiresAt: '9999-12-31T23:59:59.999Z',
+ stageStates: stages,
+ });
+
+ resetOwnedDatabase(ownership);
+ const application = applySuccessorPlanOnce({
+ database: DATABASE,
+ sql: plan.sql,
+ stateQuery,
+ baselineState: stages[0],
+ terminalState: stages[11],
+ });
+ const success = application.result;
+ const successLines = success.stdout.trim().split(/\r?\n/).filter(Boolean);
+ const successReadback = JSON.parse(successLines.at(-1));
+ const committed = readState(DATABASE, stateQuery);
+ if (!equal(successReadback, stages[11]) || !equal(committed, stages[11])) {
+ fail('LOCAL_REHEARSAL_COMMIT_READBACK_INVALID');
+ }
+
+ resetOwnedDatabase(ownership);
+ const rollback = psql(DATABASE, `${plan.sql}\nDO $forced$ BEGIN RAISE EXCEPTION 'LOCAL_FORCED_ROLLBACK'; END $forced$;\n`, {
+ singleTransaction: true,
+ allowFailure: true,
+ });
+ if (rollback.status === 0 || !/LOCAL_FORCED_ROLLBACK/.test(rollback.stderr)) {
+ fail('LOCAL_REHEARSAL_FORCED_ROLLBACK_MISSING');
+ }
+ const rolledBack = readState(DATABASE, stateQuery);
+ if (!equal(rolledBack, stages[0])) fail('LOCAL_REHEARSAL_ROLLBACK_DRIFT');
+
+ resetOwnedDatabase(ownership);
+ for (let index = 0; index < 10; index += 1) applyRawMigration(record, index);
+ const cleanPreimage = readState(DATABASE, stateQuery);
+ if (!equal(cleanPreimage, stages[10])) fail('LOCAL_REHEARSAL_STAGE10_REPLAY_DRIFT');
+ tamperFinishCleanupPreimage();
+ const tamperedBefore = readState(DATABASE, stateQuery);
+ const wrongPreimage = psql(DATABASE, record.materials[10].bytes, { allowFailure: true });
+ if (wrongPreimage.status === 0 || !/REVIEW_SHARED_SOURCE_DRIFT/.test(wrongPreimage.stderr)) {
+ fail('LOCAL_REHEARSAL_WRONG_PREIMAGE_NOT_REJECTED');
+ }
+ const tamperedAfter = readState(DATABASE, stateQuery);
+ if (!equal(tamperedBefore, tamperedAfter)
+ || tamperedAfter.bundle.targetCount !== 10
+ || tamperedAfter.bundle.ledgerCount !== 96) {
+ fail('LOCAL_REHEARSAL_WRONG_PREIMAGE_ROLLBACK_DRIFT');
+ }
+
+ const securityInvokerDriftRejected = assertCatalogDriftRejected({
+ label: 'security-invoker',
+ ownership,
+ plan,
+ stages,
+ stateQuery,
+ tamperSql: `ALTER VIEW public.admin_evaluation_related_rows SET (security_invoker=false);`,
+ });
+ const columnAclDriftRejected = assertCatalogDriftRejected({
+ label: 'column-acl',
+ ownership,
+ plan,
+ stages,
+ stateQuery,
+ tamperSql: `GRANT UPDATE(approved_name) ON TABLE public.restaurants TO anon;`,
+ });
+ const membershipInheritOptionDriftRejected = assertCatalogDriftRejected({
+ label: 'membership-inherit-option',
+ ownership,
+ plan,
+ stages,
+ stateQuery,
+ tamperSql: `GRANT ${FIXTURE_PARENT_ROLE} TO ${FIXTURE_MEMBER_ROLE} WITH ADMIN FALSE, INHERIT FALSE, SET TRUE GRANTED BY supabase_admin;`,
+ restoreSql: `GRANT ${FIXTURE_PARENT_ROLE} TO ${FIXTURE_MEMBER_ROLE} WITH ADMIN FALSE, INHERIT TRUE, SET TRUE GRANTED BY supabase_admin;`,
+ });
+ const membershipSetOptionDriftRejected = assertCatalogDriftRejected({
+ label: 'membership-set-option',
+ ownership,
+ plan,
+ stages,
+ stateQuery,
+ tamperSql: `GRANT ${FIXTURE_PARENT_ROLE} TO ${FIXTURE_MEMBER_ROLE} WITH ADMIN FALSE, INHERIT TRUE, SET FALSE GRANTED BY supabase_admin;`,
+ restoreSql: `GRANT ${FIXTURE_PARENT_ROLE} TO ${FIXTURE_MEMBER_ROLE} WITH ADMIN FALSE, INHERIT TRUE, SET TRUE GRANTED BY supabase_admin;`,
+ });
+ const contendedAdmissionExpiryRejected = await assertContendedExpiryRejected(record, stages, stateQuery, ownership);
+
+ proof = {
+ schemaVersion: 1,
+ status: 'passed',
+ environment: {
+ dockerContext: DOCKER_CONTEXT,
+ dockerContainer: DOCKER_CONTAINER,
+ engine: template.engine,
+ templateDatabase: TEMPLATE_DATABASE,
+ temporaryDatabase: DATABASE,
+ },
+ input: template,
+ ...currentBindings,
+ predecessorSources: PREDECESSOR_PATHS.map(path => ({
+ path,
+ sha256: sha256(readFileSync(resolve(REPOSITORY_ROOT, path))),
+ })),
+ migrationCount: record.manifest.migrations.length,
+ statementCount: record.manifest.migrations.reduce((sum, migration) => sum + migration.statementCount, 0),
+ stageStatesSha256: sha256(JSON.stringify(canonical(stages))),
+ stages: stages.map(summarizeStage),
+ results: {
+ advisoryContentionObserved: true,
+ atomicCommit: true,
+ atomicCommitOutcome: application.outcome,
+ atomicRollbackExact: true,
+ catalogDriftStatePreserved: true,
+ columnAclDriftRejected,
+ compiledSqlBytes: Buffer.byteLength(plan.sql),
+ compiledSqlSha256: sha256(plan.sql),
+ contendedAdmissionExpiryRejected,
+ contendedAdmissionExpiryStatePreserved: true,
+ membershipOptionsBaseline: { inherit: true, set: true },
+ membershipInheritOptionDriftRejected,
+ membershipSetOptionDriftRejected,
+ securityInvokerBaseline: true,
+ securityInvokerDriftRejected,
+ wrongFinishPreimageRejected: true,
+ wrongFinishPreimageRollbackExact: true,
+ terminalLedgerCount: committed.bundle.ledgerCount,
+ terminalContractReady: committed.state.terminalContractReady,
+ },
+ sourceMutation: false,
+ hostedWrites: false,
+ operatingWrites: false,
+ };
+ } finally {
+ cleanupOwnedFixtures(ownership);
+ }
+ proof.temporaryDatabaseDropped = true;
+ proof.fixtureRolesDropped = true;
+ const currentProofBytes = Buffer.from(`${JSON.stringify(canonical(proof))}\n`);
+ process.stdout.write(`${JSON.stringify({
+ current: {
+ classification: 'current-source-verification',
+ proofSha256: sha256(currentProofBytes),
+ bindings: currentBindings,
+ proof,
+ },
+ retained: retainedProof,
+ })}\n`);
+}
+
+if (process.argv[1] && resolve(process.argv[1]) === fileURLToPath(import.meta.url)) await run();
diff --git a/apps/web/performance/review-media-operating-admission-20261013/SHA256SUMS b/apps/web/performance/review-media-operating-admission-20261013/SHA256SUMS
new file mode 100644
index 0000000000..cce48e79e1
--- /dev/null
+++ b/apps/web/performance/review-media-operating-admission-20261013/SHA256SUMS
@@ -0,0 +1,13 @@
+31e775a669d4a1295fc8b391653f26d0559695937f4cc357818ef53faa7c8e67 apps/web/performance/review-media-operating-admission-20261013/SHA256SUMS-0833ed81-historical
+5a41205af7546221083956ddac62488f51254b253de1d738f39ab714d4835026 apps/web/performance/review-media-operating-admission-20261013/SHA256SUMS-4ba9c6d2-historical
+0d7c993375a3015143cc03b84fd7b369e68713cd49926e87205c023a773def76 apps/web/performance/review-media-operating-admission-20261013/SHA256SUMS-4ba9c6d2-historical.sha256
+ad9406090dea1f2c1f8a146f5e241aae2651449f0c58af50201116e39ccfa2b6 apps/web/performance/review-media-operating-admission-20261013/controller-reverification-v2.json
+d29f5fa8ddc27573b3ea2d2a144c9d9a1c24a6c5ac4d7aeef30e3bda514fc575 apps/web/performance/review-media-operating-admission-20261013/controller-reverification.json
+66634350db141610eca229f7fa4a45ca2b9733541a25c9bcbdff0a492d746225 apps/web/performance/review-media-operating-admission-20261013/current-evidence.json
+af2b42d1ef05343b00bf58c699aff20bb6d80194de40b7f128d3fcf823f5f9a3 apps/web/performance/review-media-operating-admission-20261013/review-fix-parity-20261014-v1.log
+af2b42d1ef05343b00bf58c699aff20bb6d80194de40b7f128d3fcf823f5f9a3 apps/web/performance/review-media-operating-admission-20261013/review-fix-parity-20261014-v2.log
+6ccafd88858b8613ab199e22f14eac97cdadb2f9a69e73d2edd6afb1fd1fefd3 apps/web/performance/review-media-operating-admission-20261013/review-fix-units-20261014-v1.log
+ad4d439f9609db73d5879e655380d640b3b52274dbfe80654f6d715bd7a68ab7 apps/web/performance/review-media-operating-admission-20261013/verification.json
+b30f841c858edfc4865dd704ae7f5463d5570117bc3543fd43a63208b2b34ea9 apps/web/scripts/review-media-operating-admission.mjs
+dfccda6dc30761a7ce0be2c3c15344cf66d9fbca9781907c4d8ac48a96024d9b apps/web/tests-unit/review-media-operating-admission.test.ts
+36242344c78d292a5818ab5b28bec5ce9af9895b9e84cc900cc17a4c56f813b1 docs/operations/review-media-operating-admission-20261013.md
diff --git a/apps/web/performance/review-media-operating-admission-20261013/SHA256SUMS-0833ed81-historical b/apps/web/performance/review-media-operating-admission-20261013/SHA256SUMS-0833ed81-historical
new file mode 100644
index 0000000000..3e7285e293
--- /dev/null
+++ b/apps/web/performance/review-media-operating-admission-20261013/SHA256SUMS-0833ed81-historical
@@ -0,0 +1,5 @@
+d29f5fa8ddc27573b3ea2d2a144c9d9a1c24a6c5ac4d7aeef30e3bda514fc575 apps/web/performance/review-media-operating-admission-20261013/controller-reverification.json
+ad4d439f9609db73d5879e655380d640b3b52274dbfe80654f6d715bd7a68ab7 apps/web/performance/review-media-operating-admission-20261013/verification.json
+4ad55ed526b7af8c0ee1282057dbfa3706249deea04472a2749909af45492064 apps/web/scripts/review-media-operating-admission.mjs
+26d7fe68effda2813b3f9384dd2d7e4f9e8998447cd988b89820d93be7f6bdc7 apps/web/tests-unit/review-media-operating-admission.test.ts
+b4fbd520413f59e82743c545f521df5a8adde04970f610d4d06b432955f07518 docs/operations/review-media-operating-admission-20261013.md
diff --git a/apps/web/performance/review-media-operating-admission-20261013/SHA256SUMS-4ba9c6d2-historical b/apps/web/performance/review-media-operating-admission-20261013/SHA256SUMS-4ba9c6d2-historical
new file mode 100644
index 0000000000..9fdb091e51
--- /dev/null
+++ b/apps/web/performance/review-media-operating-admission-20261013/SHA256SUMS-4ba9c6d2-historical
@@ -0,0 +1,6 @@
+31e775a669d4a1295fc8b391653f26d0559695937f4cc357818ef53faa7c8e67 apps/web/performance/review-media-operating-admission-20261013/SHA256SUMS-0833ed81-historical
+d29f5fa8ddc27573b3ea2d2a144c9d9a1c24a6c5ac4d7aeef30e3bda514fc575 apps/web/performance/review-media-operating-admission-20261013/controller-reverification.json
+ad4d439f9609db73d5879e655380d640b3b52274dbfe80654f6d715bd7a68ab7 apps/web/performance/review-media-operating-admission-20261013/verification.json
+586847d5030cedc6575328cbe78b5eb9f1d9ff47d91e8d8f1aef40076b6d2277 apps/web/scripts/review-media-operating-admission.mjs
+184368546c3668da99449f3d7ec0f08b95e1c06f44ca85100a6d2dfa533bc84a apps/web/tests-unit/review-media-operating-admission.test.ts
+b4fbd520413f59e82743c545f521df5a8adde04970f610d4d06b432955f07518 docs/operations/review-media-operating-admission-20261013.md
diff --git a/apps/web/performance/review-media-operating-admission-20261013/SHA256SUMS-4ba9c6d2-historical.sha256 b/apps/web/performance/review-media-operating-admission-20261013/SHA256SUMS-4ba9c6d2-historical.sha256
new file mode 100644
index 0000000000..c7d4a5cec2
--- /dev/null
+++ b/apps/web/performance/review-media-operating-admission-20261013/SHA256SUMS-4ba9c6d2-historical.sha256
@@ -0,0 +1 @@
+5a41205af7546221083956ddac62488f51254b253de1d738f39ab714d4835026
diff --git a/apps/web/performance/review-media-operating-admission-20261013/SHA256SUMS.sha256 b/apps/web/performance/review-media-operating-admission-20261013/SHA256SUMS.sha256
new file mode 100644
index 0000000000..3cb06f2b4c
--- /dev/null
+++ b/apps/web/performance/review-media-operating-admission-20261013/SHA256SUMS.sha256
@@ -0,0 +1 @@
+f7345de87e22378811fb0bfef0f57461702a4178ec92cd13e82812dcb921138e
diff --git a/apps/web/performance/review-media-operating-admission-20261013/controller-reverification-v2.json b/apps/web/performance/review-media-operating-admission-20261013/controller-reverification-v2.json
new file mode 100644
index 0000000000..ae1b1028f6
--- /dev/null
+++ b/apps/web/performance/review-media-operating-admission-20261013/controller-reverification-v2.json
@@ -0,0 +1,53 @@
+{
+ "schemaVersion": 1,
+ "kind": "local-unit-reverification",
+ "recordedAtUTC": "2026-10-13T20:18:10.822481+00:00",
+ "sourceBaseRevision": "4ba9c6d2f54b6e0c8458b2518977665f606eb4ff",
+ "checks": {
+ "passed": 98,
+ "failed": 0,
+ "assertions": 808,
+ "command": "bun test tests-unit/review-media-operating-admission.test.ts tests-unit/review-media-sql-successor.test.ts tests-unit/review-media-sql-successor-harness.test.ts tests-unit/supabase-migration-bundle.test.ts",
+ "cwd": "apps/web",
+ "log": "apps/web/performance/review-media-operating-admission-20261013/review-fix-units-20261014-v1.log",
+ "logSha256": "6ccafd88858b8613ab199e22f14eac97cdadb2f9a69e73d2edd6afb1fd1fefd3"
+ },
+ "sources": [
+ {
+ "path": "apps/web/scripts/review-media-operating-admission.mjs",
+ "sha256": "b30f841c858edfc4865dd704ae7f5463d5570117bc3543fd43a63208b2b34ea9"
+ },
+ {
+ "path": "apps/web/tests-unit/review-media-operating-admission.test.ts",
+ "sha256": "dfccda6dc30761a7ce0be2c3c15344cf66d9fbca9781907c4d8ac48a96024d9b"
+ },
+ {
+ "path": "apps/web/scripts/review-media-sql-successor.mjs",
+ "sha256": "4f1b2798e0cfc13db4f11cd6eb4a8653320aa8516eccd71d410fcff6d06a7f24"
+ },
+ {
+ "path": ".github/review-media-sql-successor.v1.json",
+ "sha256": "295b2354c562ed87102c5e256497a2705f90941587fefa7e9ad8a4ba8e40787f"
+ }
+ ],
+ "supplementalHarnessVerification": {
+ "passed": 20,
+ "failed": 0,
+ "assertions": 98,
+ "log": "apps/web/performance/review-db-successor-20261012/review-fix-harness-units-20261014-v2.log",
+ "logSha256": "7815d90d73d6b3508a355aee06743c22ff802e78ffca46172cda18ca83ed51b4",
+ "sources": [
+ {
+ "path": "apps/web/performance/review-db-successor-20261012/verify-review-media-sql-successor.mjs",
+ "sha256": "7e750e9d0126f6f10afe9056d747a2ea854d462161ea4e8f0168e2751d554c0a"
+ },
+ {
+ "path": "apps/web/tests-unit/review-media-sql-successor-harness.test.ts",
+ "sha256": "99c6bc0221188f93317640d4d65affdfeb91fea233bb0f0cc0f409407e419d84"
+ }
+ ]
+ },
+ "reason": "Bound stored rehearsal admission, finite preflight and transport timeouts; verify detached checksum pin. The 98-test run covers the four primary source hashes. The later 20-test run replaces its 18 harness tests after the contention fixture change; these runs are not 118 independent tests.",
+ "operatingEvidence": false,
+ "performanceEvidence": false
+}
diff --git a/apps/web/performance/review-media-operating-admission-20261013/controller-reverification.json b/apps/web/performance/review-media-operating-admission-20261013/controller-reverification.json
new file mode 100644
index 0000000000..7a8d215929
--- /dev/null
+++ b/apps/web/performance/review-media-operating-admission-20261013/controller-reverification.json
@@ -0,0 +1,32 @@
+{
+ "schemaVersion": 1,
+ "kind": "local-unit-reverification",
+ "checks": {
+ "passed": 10,
+ "failed": 0,
+ "assertions": 59,
+ "command": "bun test tests-unit/review-media-operating-admission.test.ts",
+ "cwd": "apps/web"
+ },
+ "reason": "Imported controller and manifest changed for terminal boundary fixes.",
+ "sources": [
+ {
+ "path": "apps/web/scripts/review-media-operating-admission.mjs",
+ "sha256": "4ad55ed526b7af8c0ee1282057dbfa3706249deea04472a2749909af45492064"
+ },
+ {
+ "path": "apps/web/tests-unit/review-media-operating-admission.test.ts",
+ "sha256": "26d7fe68effda2813b3f9384dd2d7e4f9e8998447cd988b89820d93be7f6bdc7"
+ },
+ {
+ "path": "apps/web/scripts/review-media-sql-successor.mjs",
+ "sha256": "dcf552697251c2f10d859c7a2f56937a0f6e92ee9467e5b919807e498eff1d49"
+ },
+ {
+ "path": ".github/review-media-sql-successor.v1.json",
+ "sha256": "ae764bb760102a6d7d066be669c85248feed1823eafded4bf8c246a062090a8c"
+ }
+ ],
+ "operatingEvidence": false,
+ "performanceEvidence": false
+}
diff --git a/apps/web/performance/review-media-operating-admission-20261013/current-evidence.json b/apps/web/performance/review-media-operating-admission-20261013/current-evidence.json
new file mode 100644
index 0000000000..f81b23fdd1
--- /dev/null
+++ b/apps/web/performance/review-media-operating-admission-20261013/current-evidence.json
@@ -0,0 +1,12 @@
+{
+ "schemaVersion": 1,
+ "current": "controller-reverification-v2.json",
+ "sha256": "ad9406090dea1f2c1f8a146f5e241aae2651449f0c58af50201116e39ccfa2b6",
+ "historical": [
+ "verification.json",
+ "controller-reverification.json"
+ ],
+ "historicalSourceMatch": false,
+ "operatingEvidence": false,
+ "performanceEvidence": false
+}
diff --git a/apps/web/performance/review-media-operating-admission-20261013/review-fix-parity-20261014-v1.log b/apps/web/performance/review-media-operating-admission-20261013/review-fix-parity-20261014-v1.log
new file mode 100644
index 0000000000..1bfe59481e
--- /dev/null
+++ b/apps/web/performance/review-media-operating-admission-20261013/review-fix-parity-20261014-v1.log
@@ -0,0 +1,5 @@
+
+> web@1.2.4 typecheck:parity
+> node scripts/run-typecheck.mjs --compiler parity
+
+{"status":"passed","diagnostics":0,"logicalInputs":3148,"logicalInputNamesSha256":"4f11b9a9b98661e4b0b52aade8d12be721b72ad229a7bb851ed39c0d94520158","nonLibraryContentSha256":"047401fde3b7a83508e1f431b0321cdf14d0a8d722ec5f126d460b8876799ccd","standardLibraryContentSha256":{"native":"d328ef5dcbac7d4c183cd37a26eb383e8e6921ab2147b20e5d73f3c149223a64","compat":"82afdd90154e9b315a83eb66f2d4ee59972c492094bbf66d7f0791f584e41eb0"}}
diff --git a/apps/web/performance/review-media-operating-admission-20261013/review-fix-parity-20261014-v2.log b/apps/web/performance/review-media-operating-admission-20261013/review-fix-parity-20261014-v2.log
new file mode 100644
index 0000000000..1bfe59481e
--- /dev/null
+++ b/apps/web/performance/review-media-operating-admission-20261013/review-fix-parity-20261014-v2.log
@@ -0,0 +1,5 @@
+
+> web@1.2.4 typecheck:parity
+> node scripts/run-typecheck.mjs --compiler parity
+
+{"status":"passed","diagnostics":0,"logicalInputs":3148,"logicalInputNamesSha256":"4f11b9a9b98661e4b0b52aade8d12be721b72ad229a7bb851ed39c0d94520158","nonLibraryContentSha256":"047401fde3b7a83508e1f431b0321cdf14d0a8d722ec5f126d460b8876799ccd","standardLibraryContentSha256":{"native":"d328ef5dcbac7d4c183cd37a26eb383e8e6921ab2147b20e5d73f3c149223a64","compat":"82afdd90154e9b315a83eb66f2d4ee59972c492094bbf66d7f0791f584e41eb0"}}
diff --git a/apps/web/performance/review-media-operating-admission-20261013/review-fix-units-20261014-v1.log b/apps/web/performance/review-media-operating-admission-20261013/review-fix-units-20261014-v1.log
new file mode 100644
index 0000000000..a1ff521480
--- /dev/null
+++ b/apps/web/performance/review-media-operating-admission-20261013/review-fix-units-20261014-v1.log
@@ -0,0 +1,112 @@
+bun test v1.4.0 (34cbb9a40)
+
+tests-unit/review-media-operating-admission.test.ts:
+(pass) CLI contract accepts only the explicit rehearsal or apply producer modes [0.27ms]
+(pass) rehearsal producer preserves proven receipts and creates a controller-valid private admission [10.67ms]
+(pass) unknown, missing, stale, and mismatched evidence is rejected before custody creation [4.01ms]
+(pass) rehearsal producer never overwrites an existing custody directory [6.03ms]
+(pass) rehearsal producer fails closed on parent, file, and custody fsync failures [5.23ms]
+(pass) rehearsal producer rejects symlink and oversized inputs before custody creation [3.71ms]
+(pass) apply producer binds the verified rehearsal artifacts without extending their time window [55.83ms]
+(pass) apply producer rejects a relabeled or tampered rehearsal before writing an admission [16.40ms]
+(pass) apply producer rejects a fully rehashed receipt and journal relabeled to another rehearsal attempt [8.22ms]
+(pass) apply producer treats a dangling reserved journal symlink as an existing output [8.53ms]
+(pass) apply producer reads rehearsal journals through the no-follow bounded reader [22.06ms]
+
+tests-unit/supabase-migration-bundle.test.ts:
+(pass) actual four source files compile in exact identity/hash/vector order with one output SELECT [158.11ms]
+(pass) order, source identity, source hash, vector and suffix drift reject before an executor exists [665.11ms]
+(pass) five-stage compilation is explicit while the historical four-stage API stays exact [141.48ms]
+(pass) fresh prior N and prefix/suffix roots are mandatory and partial target ledger is rejected by the first transaction guard [239.72ms]
+(pass) reconciliation classifies only exact prior/final objects; all intermediate stages conflict [130.57ms]
+(pass) ambiguous transport performs one read-only reconciliation without claiming or resending a commit [131.87ms]
+(pass) server terminal guard rollback cannot become success after a matching external commit [126.23ms]
+(pass) exit-zero malformed or mismatched readback is confirmed once by exact final state [125.48ms]
+(pass) not-applied preserves failure while partial and unknown are fixed fail-closed outcomes [140.83ms]
+(pass) transport and malformed-output diagnostics never escape through errors or causes [133.84ms]
+(pass) successful output must be exactly one JSON object matching the final bundle readback [137.83ms]
+
+tests-unit/review-media-sql-successor-harness.test.ts:
+(pass) successor acknowledgement loss reconciles a committed terminal state without SQL replay [0.23ms]
+(pass) successor failure at the baseline is classified without diagnostic SQL replay [0.20ms]
+(pass) retained proof is checksum-verified and distinguished from the current harness source [1.04ms]
+(pass) retained proof with bytes not authorized by SHA256SUMS is rejected [10.52ms]
+(pass) jointly altered retained proof and checksum map are rejected by the independent digest [1.59ms]
+(pass) owned database is cleaned after predecessor SQL fails [1.18ms]
+(pass) owned database is cleaned after fixture SQL fails [0.08ms]
+(pass) pre-existing database is never acquired or dropped [0.07ms]
+(pass) database CREATE acknowledgement loss is reconciled without replay and cleaned by exact ownership [0.28ms]
+(pass) cleanup re-reads and removes the exact owned database after the first CREATE readback fails [0.23ms]
+(pass) cleanup refuses a database whose exact owner metadata does not match [0.18ms]
+(pass) fixture role creation acknowledgement loss is reconciled and exact owned roles are cleaned [0.86ms]
+(pass) cleanup re-reads exact owned roles when the first post-create readback fails [0.34ms]
+(pass) fixture role creation failure without owned readback never authorizes cleanup [0.17ms]
+(pass) pre-existing fixture role collision is preserved and never acquired for cleanup [0.05ms]
+(pass) uncertain create collision with another owner is not guessed or removed [0.13ms]
+(pass) cleanup refuses roles whose ownership marker changed after creation [0.12ms]
+(pass) cleanup refuses to remove owned roles after an out-of-scope membership is added [0.07ms]
+
+tests-unit/review-media-sql-successor.test.ts:
+(pass) manifest binds the exact eleven sources and remains held without fresh admission [275.17ms]
+(pass) rehearsal CLI requires one admission, journal and receipt path [0.16ms]
+(pass) compiled plan has one advisory lock, one outer transaction and eleven ledger writes [1502.58ms]
+(pass) apply accepts rehearsal evidence completed after thirty seconds within its original lease [11.98ms]
+(pass) apply rejects rehearsal evidence outside the original fifteen-minute lease [10.41ms]
+(pass) actual psql runner with a spy transport preserves the post-lock expiry code [0.63ms]
+(pass) rollback-only rehearsal plan owns one transaction and captures twelve safe stages [10.28ms]
+(pass) rehearsal admission is distinct and rejects stale source or any wrong writer fence [39.32ms]
+(pass) rehearsal rolls back, separately confirms restoration and emits an apply-bound receipt [21.46ms]
+(pass) lost rehearsal response performs restoration readback once and never resends [13.33ms]
+(pass) post-lock rehearsal expiry keeps its exact bounded code after restoration readback [21.21ms]
+(pass) incomplete rehearsal output cannot create an apply admission receipt [14.22ms]
+(pass) verification line write-success then fsync-failure never publishes the canonical receipt [23.71ms]
+(pass) pending receipt full-write then fsync-failure records failure without canonical publication [24.98ms]
+(pass) pending receipt storage delay cannot verify after the admission expires [15.38ms]
+(pass) expiry terminal audit failure preserves the bounded rehearsal outcome [21.93ms]
+(pass) verification journal storage delay cannot publish after the admission expires [16.64ms]
+(pass) canonical publication storage delay cannot record an expired passed result [23.34ms]
+(pass) publication journal completion cannot return passed after the admission expires [22.64ms]
+(pass) prepublication interruption leaves verified journal and pending receipt apply-invalid [21.36ms]
+(pass) failed canonical publication is audited as failure, never as passed [15.72ms]
+(pass) pre-existing exact canonical bytes do not make an unperformed link durable [24.08ms]
+(pass) unconfirmed directory durability cannot be reconciled from visible canonical bytes [36.73ms]
+(pass) directory fsync failure plus failure-audit loss leaves visible receipt apply-invalid [19.83ms]
+(pass) publication audit fsync failure requires proven durability before reconciliation [17.45ms]
+(pass) publication audit close failure requires proven durability before reconciliation [14.76ms]
+(pass) publication audit failure before any bytes leaves durable receipt apply-invalid [31.55ms]
+(pass) atomic publication acknowledgement loss reconciles exact receipt without retry [18.86ms]
+(pass) apply rejects orphan and semantically tampered rehearsal journals even when rehashed [36.19ms]
+(pass) apply pins the stored admission against a valid fully rehashed rehearsal from another attempt [27.42ms]
+(pass) apply rejects a rehashed rehearsal with mismatched attempt binding [21.98ms]
+(pass) apply rejects a rehashed rehearsal with mismatched rollback binding [35.08ms]
+(pass) apply rejects a rehashed rehearsal with mismatched writerFence binding [12.37ms]
+(pass) apply requires the pinned private canonical rehearsal admission before any external read [85.43ms]
+(pass) admission rejects wrong preimage and every wrong writer fence [50.10ms]
+(pass) success path performs preflight, rechecks rollback and commits all eleven once [2270.70ms]
+(pass) bounded preflight clientStall with 600000ms left cannot journal or write [2157.13ms]
+(pass) bounded preflight statementTimeout with 600000ms left cannot journal or write [2206.43ms]
+(pass) bounded preflight lockTimeout with 600000ms left cannot journal or write [2367.47ms]
+(pass) bounded preflight expiry with 600000ms left cannot journal or write [2590.32ms]
+(pass) bounded preflight clientStall with 5000ms left cannot journal or write [2403.03ms]
+(pass) bounded preflight statementTimeout with 5000ms left cannot journal or write [2490.51ms]
+(pass) bounded preflight lockTimeout with 5000ms left cannot journal or write [2588.69ms]
+(pass) bounded preflight expiry with 5000ms left cannot journal or write [2337.19ms]
+(pass) psql runner keeps a finite mutation/reconciliation cap and rejects unbounded overrides [0.27ms]
+(pass) committed terminal audit fsync failure preserves the committed outcome [2451.85ms]
+(pass) committed terminal audit close failure preserves the committed outcome [2423.49ms]
+(pass) committed terminal audit readback requires both durability and exact bytes [2375.40ms]
+(pass) concurrent rollback deployment change rejects before journal or mutation [2325.17ms]
+(pass) prior readback mismatch fails before journal and mutating transport [2315.87ms]
+(pass) lost apply acknowledgement reconciles once and never resends eleven mutations [2978.11ms]
+(pass) apply fsync failure at custody/start boundary 1 prevents database mutation [2518.75ms]
+(pass) apply fsync failure at custody/start boundary 2 prevents database mutation [2217.47ms]
+(pass) apply fsync failure at custody/start boundary 3 prevents database mutation [2145.64ms]
+(pass) apply fsync failure at custody/start boundary 4 prevents database mutation [2141.04ms]
+(pass) apply fsync failure at custody/start boundary 5 prevents database mutation [2253.09ms]
+(pass) apply fsync failure at custody/start boundary 6 prevents database mutation [2234.35ms]
+(pass) apply preserves expiry and audit failure as separate bounded outcomes [2260.59ms]
+
+ 98 pass
+ 0 fail
+ 808 expect() calls
+Ran 98 tests across 4 files. [57.48s]
diff --git a/apps/web/performance/review-media-operating-admission-20261013/verification.json b/apps/web/performance/review-media-operating-admission-20261013/verification.json
new file mode 100644
index 0000000000..55a3bf9d13
--- /dev/null
+++ b/apps/web/performance/review-media-operating-admission-20261013/verification.json
@@ -0,0 +1,57 @@
+{
+ "schemaVersion": 1,
+ "kind": "local-unit-verification",
+ "recordedAtUTC": "2026-10-13T05:42:51.792097+00:00",
+ "sources": [
+ {
+ "path": "apps/web/scripts/review-media-operating-admission.mjs",
+ "sha256": "4ad55ed526b7af8c0ee1282057dbfa3706249deea04472a2749909af45492064",
+ "bytes": 28152
+ },
+ {
+ "path": "apps/web/tests-unit/review-media-operating-admission.test.ts",
+ "sha256": "26d7fe68effda2813b3f9384dd2d7e4f9e8998447cd988b89820d93be7f6bdc7",
+ "bytes": 20142
+ }
+ ],
+ "environment": {
+ "platform": "macOS",
+ "node": "24.21.0",
+ "bun": "1.4.0"
+ },
+ "checks": [
+ {
+ "command": "bun test tests-unit/review-media-operating-admission.test.ts",
+ "cwd": "apps/web",
+ "passed": 10,
+ "failed": 0,
+ "assertions": 59
+ },
+ {
+ "command": "node --check apps/web/scripts/review-media-operating-admission.mjs",
+ "exitCode": 0
+ },
+ {
+ "command": "node node_modules/eslint/bin/eslint.js scripts/review-media-operating-admission.mjs tests-unit/review-media-operating-admission.test.ts",
+ "cwd": "apps/web",
+ "exitCode": 0
+ }
+ ],
+ "scope": [
+ "CLI input validation",
+ "private exclusive output",
+ "receipt schema/hash binding",
+ "unchanged admission window",
+ "fsync failure",
+ "symlink and oversize input rejection"
+ ],
+ "limitations": [
+ "Synthetic receipts test format and internal consistency only.",
+ "The producer does not authenticate observation origin.",
+ "No hosted mutation, trusted live capture, or performance improvement is established."
+ ],
+ "confidenceInterval": {
+ "applicable": false,
+ "reason": "Deterministic branch checks, not a sampled performance or reliability estimate."
+ }
+}
diff --git a/apps/web/scripts/review-media-operating-admission.mjs b/apps/web/scripts/review-media-operating-admission.mjs
new file mode 100644
index 0000000000..40da2080ce
--- /dev/null
+++ b/apps/web/scripts/review-media-operating-admission.mjs
@@ -0,0 +1,718 @@
+#!/usr/bin/env node
+/** Pure, local producer for private review-media successor admissions. */
+import { createHash } from 'node:crypto';
+import {
+ closeSync,
+ constants,
+ fstatSync,
+ fsyncSync,
+ lstatSync,
+ mkdirSync,
+ openSync,
+ readSync,
+ realpathSync,
+ writeSync,
+} from 'node:fs';
+import { dirname, isAbsolute, join, relative, resolve, sep } from 'node:path';
+import { fileURLToPath } from 'node:url';
+
+import {
+ REPOSITORY_ROOT,
+ loadSuccessorManifest,
+ validateSuccessorAdmission,
+ validateSuccessorRehearsalAdmission,
+} from './review-media-sql-successor.mjs';
+import {
+ TZUDONG_GITHUB_REPOSITORY,
+ TZUDONG_PRODUCTION_ALIASES,
+ TZUDONG_VERCEL_PROJECT_ID,
+ TZUDONG_VERCEL_TEAM_ID,
+ normalizeVercelRollbackExpected,
+} from './vercel-rollback-readback.mjs';
+import { elevenMigrationBundleSuffixRoot } from './supabase-migration-bundle.mjs';
+
+const SCRIPT_PATH = fileURLToPath(import.meta.url);
+const MAX_WINDOW_MS = 15 * 60 * 1000;
+const CLOCK_SKEW_MS = 30_000;
+const MAX_PRIVATE_FILE_BYTES = 1024 * 1024;
+const HEX_SHA256 = /^[0-9a-f]{64}$/;
+const REVISION = /^[0-9a-f]{40}$/;
+const UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/;
+
+const FILES = Object.freeze({
+ applyAdmission: 'apply-admission.json',
+ applyJournal: 'apply-journal.jsonl',
+ checkout: 'checkout-readback.json',
+ protectedMain: 'protected-main-receipt.json',
+ rehearsalAdmission: 'rehearsal-admission.json',
+ rehearsalJournal: 'rehearsal-journal.jsonl',
+ rehearsalReceipt: 'rehearsal-receipt.json',
+ rollback: 'rollback-readback-receipt.json',
+ source: 'source-receipt.json',
+});
+
+const PUBLIC_CODES = new Set([
+ 'SUCCESSOR_OPERATING_ADMISSION_ARGUMENT_INVALID',
+ 'SUCCESSOR_OPERATING_ADMISSION_CUSTODY_INVALID',
+ 'SUCCESSOR_OPERATING_ADMISSION_EVIDENCE_INVALID',
+ 'SUCCESSOR_OPERATING_ADMISSION_INPUT_INVALID',
+ 'SUCCESSOR_OPERATING_ADMISSION_OUTPUT_EXISTS',
+ 'SUCCESSOR_OPERATING_ADMISSION_WRITE_FAILED',
+]);
+
+const operationError = code => {
+ const error = new Error(code);
+ error.code = code;
+ return error;
+};
+const fail = code => { throw operationError(code); };
+const sha256 = value => createHash('sha256').update(value).digest('hex');
+
+function safeCode(error) {
+ try { return typeof error?.code === 'string' ? error.code : ''; } catch { return ''; }
+}
+
+function canonical(value) {
+ if (Array.isArray(value)) return value.map(canonical);
+ if (value && typeof value === 'object') {
+ return Object.fromEntries(Object.keys(value).sort().map(key => [key, canonical(value[key])]));
+ }
+ return value;
+}
+
+const canonicalBytes = value => Buffer.from(`${JSON.stringify(canonical(value))}\n`);
+const equal = (left, right) => JSON.stringify(canonical(left)) === JSON.stringify(canonical(right));
+
+function exactKeys(value, keys, code = 'SUCCESSOR_OPERATING_ADMISSION_EVIDENCE_INVALID') {
+ if (!value || typeof value !== 'object' || Array.isArray(value)) fail(code);
+ const actual = Object.keys(value).sort();
+ const expected = [...keys].sort();
+ if (actual.length !== expected.length || actual.some((key, index) => key !== expected[index])) fail(code);
+}
+
+function assertHex(value) {
+ if (typeof value !== 'string' || !HEX_SHA256.test(value)) {
+ fail('SUCCESSOR_OPERATING_ADMISSION_EVIDENCE_INVALID');
+ }
+}
+
+function assertUuid(value) {
+ if (typeof value !== 'string' || !UUID.test(value)) {
+ fail('SUCCESSOR_OPERATING_ADMISSION_EVIDENCE_INVALID');
+ }
+}
+
+function pathEntryExists(path) {
+ try {
+ lstatSync(path);
+ return true;
+ } catch (error) {
+ if (safeCode(error) === 'ENOENT') return false;
+ fail('SUCCESSOR_OPERATING_ADMISSION_CUSTODY_INVALID');
+ }
+}
+
+function assertOutsideRepository(path, code, { mustExist = true } = {}) {
+ if (typeof path !== 'string' || !isAbsolute(path)) fail(code);
+ const absolute = resolve(path);
+ let comparison;
+ try {
+ comparison = mustExist ? realpathSync(absolute) : realpathSync(dirname(absolute));
+ } catch {
+ fail(code);
+ }
+ const repository = realpathSync(REPOSITORY_ROOT);
+ const rel = relative(repository, comparison);
+ if (!rel || (rel !== '..' && !rel.startsWith(`..${sep}`))) fail(code);
+ return absolute;
+}
+
+function assertPrivateDirectory(path, code) {
+ let status;
+ try { status = lstatSync(path); } catch { fail(code); }
+ if (!status.isDirectory()
+ || status.isSymbolicLink()
+ || (process.platform !== 'win32' && (status.mode & 0o077) !== 0)) fail(code);
+ return realpathSync(path);
+}
+
+function readBoundedDescriptor(descriptor, status, code) {
+ if (!Number.isSafeInteger(status.size)
+ || status.size <= 0
+ || status.size > MAX_PRIVATE_FILE_BYTES) fail(code);
+ const buffer = Buffer.allocUnsafe(status.size + 1);
+ let offset = 0;
+ while (offset < buffer.length) {
+ const count = readSync(descriptor, buffer, offset, buffer.length - offset, null);
+ if (!Number.isSafeInteger(count) || count < 0) fail(code);
+ if (count === 0) break;
+ offset += count;
+ }
+ const finalStatus = fstatSync(descriptor);
+ if (offset !== status.size || finalStatus.size !== status.size) fail(code);
+ return buffer.subarray(0, offset);
+}
+
+function readPrivateCanonicalJson(path, code = 'SUCCESSOR_OPERATING_ADMISSION_INPUT_INVALID') {
+ const absolute = assertOutsideRepository(path, code);
+ const parent = assertPrivateDirectory(dirname(absolute), code);
+ let descriptor;
+ try {
+ descriptor = openSync(absolute, constants.O_RDONLY | constants.O_NOFOLLOW);
+ const status = fstatSync(descriptor);
+ const named = lstatSync(absolute);
+ if (!status.isFile()
+ || named.isSymbolicLink()
+ || status.dev !== named.dev
+ || status.ino !== named.ino
+ || dirname(realpathSync(absolute)) !== parent
+ || (process.platform !== 'win32' && (status.mode & 0o077) !== 0)) fail(code);
+ const bytes = readBoundedDescriptor(descriptor, status, code);
+ let value;
+ try { value = JSON.parse(bytes.toString('utf8')); } catch { fail(code); }
+ if (!value || typeof value !== 'object' || Array.isArray(value)
+ || !bytes.equals(canonicalBytes(value))) fail(code);
+ return Object.freeze({ absolute, bytes, value });
+ } catch (error) {
+ if (safeCode(error) === code) throw error;
+ fail(code);
+ } finally {
+ if (descriptor !== undefined) {
+ try { closeSync(descriptor); } catch { /* bounded failure above */ }
+ }
+ }
+}
+
+function writeAll(descriptor, bytes) {
+ let offset = 0;
+ while (offset < bytes.length) {
+ const written = writeSync(descriptor, bytes, offset, bytes.length - offset);
+ if (!Number.isSafeInteger(written) || written <= 0) {
+ fail('SUCCESSOR_OPERATING_ADMISSION_WRITE_FAILED');
+ }
+ offset += written;
+ }
+}
+
+function writeExclusiveCanonical(path, value, fsyncImpl = fsyncSync) {
+ const bytes = canonicalBytes(value);
+ if (bytes.length === 0 || bytes.length > MAX_PRIVATE_FILE_BYTES) {
+ fail('SUCCESSOR_OPERATING_ADMISSION_WRITE_FAILED');
+ }
+ let descriptor;
+ try {
+ descriptor = openSync(path, 'wx', 0o600);
+ writeAll(descriptor, bytes);
+ fsyncImpl(descriptor);
+ return bytes;
+ } catch (error) {
+ const errorCode = safeCode(error);
+ if (errorCode === 'EEXIST') fail('SUCCESSOR_OPERATING_ADMISSION_OUTPUT_EXISTS');
+ if (errorCode.startsWith('SUCCESSOR_OPERATING_ADMISSION_')) throw error;
+ fail('SUCCESSOR_OPERATING_ADMISSION_WRITE_FAILED');
+ } finally {
+ if (descriptor !== undefined) {
+ try { closeSync(descriptor); } catch { /* file fsync already established the bounded outcome */ }
+ }
+ }
+}
+
+function syncDirectory(path, fsyncImpl = fsyncSync) {
+ let descriptor;
+ try {
+ descriptor = openSync(path, 'r');
+ fsyncImpl(descriptor);
+ } catch {
+ fail('SUCCESSOR_OPERATING_ADMISSION_WRITE_FAILED');
+ } finally {
+ if (descriptor !== undefined) {
+ try { closeSync(descriptor); } catch { /* bounded outcome above */ }
+ }
+ }
+}
+
+function assertWindow(createdAtValue, expiresAtValue, now) {
+ const createdAt = Date.parse(createdAtValue);
+ const expiresAt = Date.parse(expiresAtValue);
+ const nowMs = now instanceof Date ? now.getTime() : NaN;
+ if (!Number.isFinite(createdAt)
+ || !Number.isFinite(expiresAt)
+ || !Number.isFinite(nowMs)
+ || expiresAt <= createdAt
+ || expiresAt - createdAt > MAX_WINDOW_MS
+ || createdAt > nowMs + CLOCK_SKEW_MS
+ || nowMs >= expiresAt) fail('SUCCESSOR_OPERATING_ADMISSION_EVIDENCE_INVALID');
+ return Object.freeze({ createdAt, expiresAt, nowMs });
+}
+
+function assertObservedWindow(document, expiresAtValue, window) {
+ const observedAt = Date.parse(document.observedAt);
+ if (document.expiresAt !== expiresAtValue
+ || !Number.isFinite(observedAt)
+ || observedAt < window.createdAt
+ || observedAt >= window.expiresAt
+ || observedAt > window.nowMs + CLOCK_SKEW_MS) {
+ fail('SUCCESSOR_OPERATING_ADMISSION_EVIDENCE_INVALID');
+ }
+}
+
+function assertCommonReceipt(receipt, input, manifestRecord, window) {
+ const { manifest, manifestSha256 } = manifestRecord;
+ if (receipt.schemaVersion !== 1
+ || receipt.id !== manifest.id
+ || receipt.projectRef !== manifest.projectRef
+ || receipt.purpose !== manifest.purpose
+ || receipt.manifestSha256 !== manifestSha256
+ || receipt.sourceRevision !== input.sourceRevision) {
+ fail('SUCCESSOR_OPERATING_ADMISSION_EVIDENCE_INVALID');
+ }
+ assertObservedWindow(receipt, input.expiresAt, window);
+}
+
+function validateRehearsalInput(input, manifestRecord, now) {
+ exactKeys(input, [
+ 'attemptId', 'checkoutReadback', 'createdAt', 'expiresAt', 'kind', 'manifestSha256',
+ 'protectedMainReceipt', 'rollbackReadbackReceipt', 'schemaVersion', 'sourceReceipt',
+ 'sourceRevision', 'sourceRoot', 'writerFenceReceipts',
+ ], 'SUCCESSOR_OPERATING_ADMISSION_INPUT_INVALID');
+ const { manifest, manifestSha256 } = manifestRecord;
+ if (input.schemaVersion !== 1
+ || input.kind !== 'review-media-successor-rehearsal-admission-input'
+ || input.manifestSha256 !== manifestSha256
+ || input.sourceRoot !== manifest.sourceRoot
+ || !REVISION.test(input.sourceRevision ?? '')) {
+ fail('SUCCESSOR_OPERATING_ADMISSION_EVIDENCE_INVALID');
+ }
+ assertUuid(input.attemptId);
+ const window = assertWindow(input.createdAt, input.expiresAt, now);
+
+ exactKeys(input.checkoutReadback, [
+ 'clean', 'detached', 'expiresAt', 'kind', 'observedAt', 'revision', 'schemaVersion',
+ ]);
+ if (input.checkoutReadback.schemaVersion !== 1
+ || input.checkoutReadback.kind !== 'successor-checkout-readback'
+ || input.checkoutReadback.revision !== input.sourceRevision
+ || input.checkoutReadback.clean !== true
+ || input.checkoutReadback.detached !== true) {
+ fail('SUCCESSOR_OPERATING_ADMISSION_EVIDENCE_INVALID');
+ }
+ assertObservedWindow(input.checkoutReadback, input.expiresAt, window);
+
+ exactKeys(input.sourceReceipt, [
+ 'expiresAt', 'id', 'kind', 'manifestSha256', 'observedAt', 'projectRef', 'purpose',
+ 'schemaVersion', 'sourceRevision', 'sourceRoot',
+ ]);
+ assertCommonReceipt(input.sourceReceipt, input, manifestRecord, window);
+ if (input.sourceReceipt.kind !== 'successor-source-receipt'
+ || input.sourceReceipt.sourceRoot !== manifest.sourceRoot) {
+ fail('SUCCESSOR_OPERATING_ADMISSION_EVIDENCE_INVALID');
+ }
+
+ exactKeys(input.protectedMainReceipt, [
+ 'expiresAt', 'id', 'kind', 'manifestSha256', 'observedAt', 'projectRef', 'purpose',
+ 'ref', 'remote', 'repositoryUrl', 'revision', 'schemaVersion', 'sourceRevision',
+ ]);
+ assertCommonReceipt(input.protectedMainReceipt, input, manifestRecord, window);
+ if (input.protectedMainReceipt.kind !== 'protected-main-readback-receipt'
+ || input.protectedMainReceipt.remote !== manifest.protectedSource.remote
+ || input.protectedMainReceipt.ref !== manifest.protectedSource.ref
+ || input.protectedMainReceipt.repositoryUrl !== manifest.protectedSource.repositoryUrl
+ || input.protectedMainReceipt.revision !== input.sourceRevision) {
+ fail('SUCCESSOR_OPERATING_ADMISSION_EVIDENCE_INVALID');
+ }
+
+ const rollback = input.rollbackReadbackReceipt;
+ exactKeys(rollback, [
+ 'deploymentId', 'deploymentSha', 'deploymentUrl', 'expiresAt', 'gitRef', 'id', 'kind',
+ 'manifestSha256', 'observedAt', 'productionAliases', 'projectId', 'projectRef',
+ 'purpose', 'readyState', 'repository', 'schemaVersion', 'sourceRevision', 'state',
+ 'target', 'teamId',
+ ]);
+ assertCommonReceipt(rollback, input, manifestRecord, window);
+ let normalizedRollback;
+ try {
+ normalizedRollback = normalizeVercelRollbackExpected({
+ deploymentId: rollback.deploymentId,
+ deploymentUrl: rollback.deploymentUrl,
+ gitRef: rollback.gitRef,
+ gitSha: rollback.deploymentSha,
+ });
+ } catch {
+ fail('SUCCESSOR_OPERATING_ADMISSION_EVIDENCE_INVALID');
+ }
+ if (rollback.kind !== 'rollback-readback-receipt'
+ || normalizedRollback.deploymentUrl !== rollback.deploymentUrl
+ || rollback.projectId !== TZUDONG_VERCEL_PROJECT_ID
+ || rollback.teamId !== TZUDONG_VERCEL_TEAM_ID
+ || rollback.repository !== TZUDONG_GITHUB_REPOSITORY
+ || !equal(rollback.productionAliases, TZUDONG_PRODUCTION_ALIASES)
+ || rollback.readyState !== 'READY'
+ || rollback.target !== 'production'
+ || rollback.state !== 'ready') {
+ fail('SUCCESSOR_OPERATING_ADMISSION_EVIDENCE_INVALID');
+ }
+
+ const fenceKeys = Object.keys(manifest.requiredWriterFences).sort();
+ exactKeys(input.writerFenceReceipts, fenceKeys);
+ for (const key of fenceKeys) {
+ const receipt = input.writerFenceReceipts[key];
+ exactKeys(receipt, [
+ 'expiresAt', 'fence', 'id', 'kind', 'manifestSha256', 'observedAt', 'projectRef',
+ 'purpose', 'schemaVersion', 'sourceRevision', 'state',
+ ]);
+ assertCommonReceipt(receipt, input, manifestRecord, window);
+ if (receipt.kind !== 'writer-fence-readback-receipt'
+ || receipt.fence !== key
+ || receipt.state !== manifest.requiredWriterFences[key]) {
+ fail('SUCCESSOR_OPERATING_ADMISSION_EVIDENCE_INVALID');
+ }
+ }
+ return window;
+}
+
+function createCustodyDirectory(path, fsyncImpl) {
+ const absolute = assertOutsideRepository(path, 'SUCCESSOR_OPERATING_ADMISSION_CUSTODY_INVALID', {
+ mustExist: false,
+ });
+ assertPrivateDirectory(dirname(absolute), 'SUCCESSOR_OPERATING_ADMISSION_CUSTODY_INVALID');
+ if (pathEntryExists(absolute)) fail('SUCCESSOR_OPERATING_ADMISSION_OUTPUT_EXISTS');
+ try { mkdirSync(absolute, { mode: 0o700 }); } catch (error) {
+ if (safeCode(error) === 'EEXIST') fail('SUCCESSOR_OPERATING_ADMISSION_OUTPUT_EXISTS');
+ fail('SUCCESSOR_OPERATING_ADMISSION_WRITE_FAILED');
+ }
+ syncDirectory(dirname(absolute), fsyncImpl);
+ return assertPrivateDirectory(absolute, 'SUCCESSOR_OPERATING_ADMISSION_CUSTODY_INVALID');
+}
+
+function existingCustodyDirectory(path) {
+ const absolute = assertOutsideRepository(path, 'SUCCESSOR_OPERATING_ADMISSION_CUSTODY_INVALID');
+ return assertPrivateDirectory(absolute, 'SUCCESSOR_OPERATING_ADMISSION_CUSTODY_INVALID');
+}
+
+export function prepareReviewMediaRehearsalAdmission({
+ custodyDirectory,
+ fsyncImpl = fsyncSync,
+ inputPath,
+ manifestRecord = loadSuccessorManifest(),
+ now = new Date(),
+} = {}) {
+ const inputRecord = readPrivateCanonicalJson(inputPath);
+ const input = inputRecord.value;
+ validateRehearsalInput(input, manifestRecord, now);
+
+ const custody = createCustodyDirectory(custodyDirectory, fsyncImpl);
+ const sourceBytes = canonicalBytes(input.sourceReceipt);
+ const protectedMainBytes = canonicalBytes(input.protectedMainReceipt);
+ const rollbackBytes = canonicalBytes(input.rollbackReadbackReceipt);
+ const writerFences = {};
+ const receiptDocuments = [
+ [FILES.checkout, input.checkoutReadback],
+ [FILES.source, input.sourceReceipt],
+ [FILES.protectedMain, input.protectedMainReceipt],
+ [FILES.rollback, input.rollbackReadbackReceipt],
+ ];
+ for (const key of Object.keys(manifestRecord.manifest.requiredWriterFences).sort()) {
+ const receipt = input.writerFenceReceipts[key];
+ const bytes = canonicalBytes(receipt);
+ writerFences[key] = Object.freeze({
+ evidenceSha256: sha256(bytes),
+ state: receipt.state,
+ });
+ receiptDocuments.push([`writer-fence-${key}.json`, receipt]);
+ }
+
+ const journalPath = join(custody, FILES.rehearsalJournal);
+ const receiptPath = join(custody, FILES.rehearsalReceipt);
+ const admissionPath = join(custody, FILES.rehearsalAdmission);
+ const rollback = input.rollbackReadbackReceipt;
+ const admission = {
+ attemptId: input.attemptId,
+ createdAt: input.createdAt,
+ expiresAt: input.expiresAt,
+ id: manifestRecord.manifest.id,
+ journalPathSha256: sha256(Buffer.from(journalPath)),
+ kind: 'successor-rehearsal-admission',
+ manifestSha256: manifestRecord.manifestSha256,
+ projectRef: manifestRecord.manifest.projectRef,
+ protectedMainReceiptSha256: sha256(protectedMainBytes),
+ purpose: manifestRecord.manifest.purpose,
+ receiptPathSha256: sha256(Buffer.from(receiptPath)),
+ rollback: {
+ deploymentId: rollback.deploymentId,
+ deploymentSha: rollback.deploymentSha,
+ deploymentUrl: rollback.deploymentUrl,
+ gitRef: rollback.gitRef,
+ productionAliases: rollback.productionAliases,
+ projectId: rollback.projectId,
+ readbackSha256: sha256(rollbackBytes),
+ readyState: rollback.readyState,
+ repository: rollback.repository,
+ state: rollback.state,
+ target: rollback.target,
+ teamId: rollback.teamId,
+ },
+ schemaVersion: 1,
+ sourceReceiptSha256: sha256(sourceBytes),
+ sourceRevision: input.sourceRevision,
+ sourceRoot: input.sourceRoot,
+ writerFences,
+ };
+
+ for (const [name, document] of receiptDocuments) {
+ writeExclusiveCanonical(join(custody, name), document, fsyncImpl);
+ }
+ const admissionBytes = writeExclusiveCanonical(admissionPath, admission, fsyncImpl);
+ syncDirectory(custody, fsyncImpl);
+ validateSuccessorRehearsalAdmission(admission, manifestRecord, {
+ now,
+ receiptDirectory: custody,
+ });
+ if (pathEntryExists(journalPath) || pathEntryExists(receiptPath)) {
+ fail('SUCCESSOR_OPERATING_ADMISSION_OUTPUT_EXISTS');
+ }
+ return Object.freeze({
+ admissionPath,
+ admissionSha256: sha256(admissionBytes),
+ custodyDirectory: custody,
+ expiresAt: admission.expiresAt,
+ journalPath,
+ mode: 'rehearsal',
+ receiptPath,
+ status: 'prepared',
+ });
+}
+
+function readCanonicalCustodyFile(custody, name) {
+ return readPrivateCanonicalJson(join(custody, name), 'SUCCESSOR_OPERATING_ADMISSION_EVIDENCE_INVALID');
+}
+
+function readCanonicalCustodyJournal(custody, name) {
+ const path = assertOutsideRepository(
+ join(custody, name),
+ 'SUCCESSOR_OPERATING_ADMISSION_EVIDENCE_INVALID',
+ );
+ let descriptor;
+ try {
+ descriptor = openSync(path, constants.O_RDONLY | constants.O_NOFOLLOW);
+ const status = fstatSync(descriptor);
+ const named = lstatSync(path);
+ if (!status.isFile()
+ || named.isSymbolicLink()
+ || status.dev !== named.dev
+ || status.ino !== named.ino
+ || dirname(realpathSync(path)) !== custody
+ || (process.platform !== 'win32' && (status.mode & 0o077) !== 0)) {
+ fail('SUCCESSOR_OPERATING_ADMISSION_EVIDENCE_INVALID');
+ }
+ const bytes = readBoundedDescriptor(
+ descriptor,
+ status,
+ 'SUCCESSOR_OPERATING_ADMISSION_EVIDENCE_INVALID',
+ );
+ const text = bytes.toString('utf8');
+ const lines = text.endsWith('\n') ? text.slice(0, -1).split('\n') : [];
+ if (lines.length !== 3 || lines.some(line => !line)) {
+ fail('SUCCESSOR_OPERATING_ADMISSION_EVIDENCE_INVALID');
+ }
+ const entries = lines.map(line => {
+ let entry;
+ try { entry = JSON.parse(line); } catch { fail('SUCCESSOR_OPERATING_ADMISSION_EVIDENCE_INVALID'); }
+ if (!entry || typeof entry !== 'object' || Array.isArray(entry)
+ || `${JSON.stringify(canonical(entry))}\n` !== `${line}\n`) {
+ fail('SUCCESSOR_OPERATING_ADMISSION_EVIDENCE_INVALID');
+ }
+ return entry;
+ });
+ return Object.freeze({ bytes, entries, path });
+ } catch (error) {
+ if (safeCode(error) === 'SUCCESSOR_OPERATING_ADMISSION_EVIDENCE_INVALID') throw error;
+ fail('SUCCESSOR_OPERATING_ADMISSION_EVIDENCE_INVALID');
+ } finally {
+ if (descriptor !== undefined) {
+ try { closeSync(descriptor); } catch { /* bounded failure above */ }
+ }
+ }
+}
+
+function validateApplyInput(input, rehearsal, manifestRecord, now) {
+ exactKeys(input, [
+ 'attemptId', 'expiresAt', 'kind', 'manifestSha256', 'observedAt',
+ 'rehearsalAdmissionSha256', 'rehearsalJournalSha256', 'rehearsalReceiptSha256',
+ 'schemaVersion', 'sourceRevision',
+ ], 'SUCCESSOR_OPERATING_ADMISSION_INPUT_INVALID');
+ if (input.schemaVersion !== 1
+ || input.kind !== 'review-media-successor-apply-admission-input'
+ || input.manifestSha256 !== manifestRecord.manifestSha256
+ || input.sourceRevision !== rehearsal.sourceRevision
+ || input.expiresAt !== rehearsal.expiresAt) {
+ fail('SUCCESSOR_OPERATING_ADMISSION_EVIDENCE_INVALID');
+ }
+ assertUuid(input.attemptId);
+ assertHex(input.rehearsalAdmissionSha256);
+ assertHex(input.rehearsalJournalSha256);
+ assertHex(input.rehearsalReceiptSha256);
+ const window = assertWindow(rehearsal.createdAt, rehearsal.expiresAt, now);
+ const observedAt = Date.parse(input.observedAt);
+ if (!Number.isFinite(observedAt)
+ || observedAt < window.createdAt
+ || observedAt >= window.expiresAt
+ || observedAt > window.nowMs + CLOCK_SKEW_MS) {
+ fail('SUCCESSOR_OPERATING_ADMISSION_EVIDENCE_INVALID');
+ }
+ return Object.freeze({ ...window, observedAt });
+}
+
+export function prepareReviewMediaApplyAdmission({
+ custodyDirectory,
+ fsyncImpl = fsyncSync,
+ inputPath,
+ manifestRecord = loadSuccessorManifest(),
+ now = new Date(),
+} = {}) {
+ const inputRecord = readPrivateCanonicalJson(inputPath);
+ const custody = existingCustodyDirectory(custodyDirectory);
+ const rehearsalAdmissionRecord = readCanonicalCustodyFile(custody, FILES.rehearsalAdmission);
+ const rehearsal = rehearsalAdmissionRecord.value;
+ const window = validateApplyInput(inputRecord.value, rehearsal, manifestRecord, now);
+ if (sha256(rehearsalAdmissionRecord.bytes) !== inputRecord.value.rehearsalAdmissionSha256) {
+ fail('SUCCESSOR_OPERATING_ADMISSION_EVIDENCE_INVALID');
+ }
+ validateSuccessorRehearsalAdmission(rehearsal, manifestRecord, {
+ now,
+ receiptDirectory: custody,
+ });
+
+ const rehearsalReceiptRecord = readCanonicalCustodyFile(custody, FILES.rehearsalReceipt);
+ const rehearsalJournalRecord = readCanonicalCustodyJournal(custody, FILES.rehearsalJournal);
+ const rehearsalJournalBytes = rehearsalJournalRecord.bytes;
+ if (sha256(rehearsalReceiptRecord.bytes) !== inputRecord.value.rehearsalReceiptSha256
+ || sha256(rehearsalJournalBytes) !== inputRecord.value.rehearsalJournalSha256) {
+ fail('SUCCESSOR_OPERATING_ADMISSION_EVIDENCE_INVALID');
+ }
+ const receipt = rehearsalReceiptRecord.value;
+ if (receipt.admissionSha256 !== inputRecord.value.rehearsalAdmissionSha256
+ || receipt.expiresAt !== rehearsal.expiresAt) {
+ fail('SUCCESSOR_OPERATING_ADMISSION_EVIDENCE_INVALID');
+ }
+ const stageStatesSha256 = sha256(JSON.stringify(canonical(receipt.stageStates)));
+ const priorStateSha256 = sha256(JSON.stringify(canonical(receipt.stageStates?.[0])));
+ const receiptBindings = {
+ admissionSha256: HEX_SHA256.test(receipt.admissionSha256 ?? ''),
+ attemptId: receipt.attemptId === rehearsal.attemptId,
+ common: receipt.schemaVersion === 1
+ && receipt.id === manifestRecord.manifest.id
+ && receipt.projectRef === manifestRecord.manifest.projectRef
+ && receipt.purpose === manifestRecord.manifest.purpose
+ && receipt.manifestSha256 === manifestRecord.manifestSha256
+ && receipt.sourceRevision === rehearsal.sourceRevision,
+ kind: receipt.kind === 'successor-rehearsal-receipt',
+ journalPathSha256: receipt.journalPathSha256 === sha256(Buffer.from(rehearsalJournalRecord.path)),
+ passed: receipt.passed === true,
+ preflightStateSha256: receipt.preflightStateSha256 === priorStateSha256,
+ restoredStateSha256: receipt.restoredStateSha256 === priorStateSha256,
+ serverMajor: Number.isSafeInteger(receipt.serverVersionNum)
+ && Math.floor(receipt.serverVersionNum / 10_000) === manifestRecord.manifest.serverMajor,
+ sourceRoot: receipt.sourceRoot === manifestRecord.manifest.sourceRoot,
+ stageStatesSha256: receipt.stageStatesSha256 === stageStatesSha256,
+ suffixRoot: receipt.suffixRoot === elevenMigrationBundleSuffixRoot(manifestRecord.manifest.migrations),
+ };
+ if (Object.values(receiptBindings).some(value => !value)) {
+ fail('SUCCESSOR_OPERATING_ADMISSION_EVIDENCE_INVALID');
+ }
+ const publication = rehearsalJournalRecord.entries[2];
+ const publishedAt = Date.parse(publication.publishedAt);
+ if (!Number.isFinite(publishedAt) || window.observedAt < publishedAt) {
+ fail('SUCCESSOR_OPERATING_ADMISSION_EVIDENCE_INVALID');
+ }
+
+ const applyAdmissionPath = join(custody, FILES.applyAdmission);
+ const applyJournalPath = join(custody, FILES.applyJournal);
+ if (pathEntryExists(applyAdmissionPath) || pathEntryExists(applyJournalPath)) {
+ fail('SUCCESSOR_OPERATING_ADMISSION_OUTPUT_EXISTS');
+ }
+ const admission = {
+ attemptId: inputRecord.value.attemptId,
+ createdAt: rehearsal.createdAt,
+ expiresAt: rehearsal.expiresAt,
+ id: rehearsal.id,
+ journalPathSha256: sha256(Buffer.from(applyJournalPath)),
+ manifestSha256: rehearsal.manifestSha256,
+ projectRef: rehearsal.projectRef,
+ protectedMainReceiptSha256: rehearsal.protectedMainReceiptSha256,
+ purpose: rehearsal.purpose,
+ rehearsalAdmissionSha256: sha256(rehearsalAdmissionRecord.bytes),
+ rehearsalJournalSha256: inputRecord.value.rehearsalJournalSha256,
+ rehearsalReceiptSha256: inputRecord.value.rehearsalReceiptSha256,
+ rollback: structuredClone(rehearsal.rollback),
+ schemaVersion: 1,
+ sourceReceiptSha256: rehearsal.sourceReceiptSha256,
+ sourceRevision: rehearsal.sourceRevision,
+ sourceRoot: rehearsal.sourceRoot,
+ stageStates: receipt.stageStates,
+ writerFences: structuredClone(rehearsal.writerFences),
+ };
+ validateSuccessorAdmission(admission, manifestRecord, { now, receiptDirectory: custody });
+ const admissionBytes = writeExclusiveCanonical(applyAdmissionPath, admission, fsyncImpl);
+ syncDirectory(custody, fsyncImpl);
+ if (pathEntryExists(applyJournalPath)) fail('SUCCESSOR_OPERATING_ADMISSION_OUTPUT_EXISTS');
+ return Object.freeze({
+ admissionPath: applyAdmissionPath,
+ admissionSha256: sha256(admissionBytes),
+ custodyDirectory: custody,
+ expiresAt: admission.expiresAt,
+ journalPath: applyJournalPath,
+ mode: 'apply',
+ status: 'prepared',
+ });
+}
+
+export function parseOperatingAdmissionArguments(argv) {
+ const result = {};
+ for (let index = 0; index < argv.length; index += 2) {
+ const option = argv[index];
+ const value = argv[index + 1];
+ if (!['--mode', '--input', '--custody-dir'].includes(option)
+ || !value
+ || value.startsWith('--')
+ || option.slice(2) in result) {
+ fail('SUCCESSOR_OPERATING_ADMISSION_ARGUMENT_INVALID');
+ }
+ result[option.slice(2)] = value;
+ }
+ if (argv.length !== 6
+ || !['rehearsal', 'apply'].includes(result.mode)
+ || !result.input
+ || !result['custody-dir']) {
+ fail('SUCCESSOR_OPERATING_ADMISSION_ARGUMENT_INVALID');
+ }
+ return Object.freeze({
+ custodyDirectory: result['custody-dir'],
+ inputPath: result.input,
+ mode: result.mode,
+ });
+}
+
+export function operatingAdmissionFailureResult(error) {
+ const observedCode = safeCode(error);
+ const code = PUBLIC_CODES.has(observedCode)
+ ? observedCode
+ : 'SUCCESSOR_OPERATING_ADMISSION_INPUT_INVALID';
+ return Object.freeze({ code, status: 'failed' });
+}
+
+export function main(argv = process.argv.slice(2), dependencies = {}) {
+ const args = parseOperatingAdmissionArguments(argv);
+ const producer = args.mode === 'rehearsal'
+ ? prepareReviewMediaRehearsalAdmission
+ : prepareReviewMediaApplyAdmission;
+ return producer({ ...dependencies, ...args });
+}
+
+if (process.argv[1] && resolve(process.argv[1]) === SCRIPT_PATH) {
+ try {
+ console.log(JSON.stringify(main()));
+ } catch (error) {
+ console.error(JSON.stringify(operatingAdmissionFailureResult(error)));
+ process.exitCode = 1;
+ }
+}
diff --git a/apps/web/scripts/review-media-sql-rehearsal.mjs b/apps/web/scripts/review-media-sql-rehearsal.mjs
new file mode 100644
index 0000000000..69f2113830
--- /dev/null
+++ b/apps/web/scripts/review-media-sql-rehearsal.mjs
@@ -0,0 +1,54 @@
+#!/usr/bin/env node
+/** Rollback-only operating rehearsal for the eleven-stage review-media successor. */
+import { resolve } from 'node:path';
+import { fileURLToPath } from 'node:url';
+
+import {
+ successorFailureResult,
+ runReviewMediaSuccessorRehearsal,
+} from './review-media-sql-successor.mjs';
+
+const SCRIPT_PATH = fileURLToPath(import.meta.url);
+
+function fail(code) {
+ const error = new Error(code);
+ error.code = code;
+ throw error;
+}
+
+export function parseRehearsalArguments(argv) {
+ const result = {};
+ for (let index = 0; index < argv.length; index += 2) {
+ const option = argv[index];
+ const value = argv[index + 1];
+ if (!['--admission', '--journal', '--receipt'].includes(option)
+ || !value
+ || value.startsWith('--')
+ || option.slice(2) in result) {
+ fail('SUCCESSOR_REHEARSAL_ARGUMENT_INVALID');
+ }
+ result[option.slice(2)] = value;
+ }
+ if (argv.length !== 6 || !result.admission || !result.journal || !result.receipt) {
+ fail('SUCCESSOR_REHEARSAL_ARGUMENT_INVALID');
+ }
+ return result;
+}
+
+export function main(argv = process.argv.slice(2), dependencies) {
+ const args = parseRehearsalArguments(argv);
+ return runReviewMediaSuccessorRehearsal({
+ admissionPath: args.admission,
+ journalPath: args.journal,
+ receiptPath: args.receipt,
+ }, dependencies);
+}
+
+if (process.argv[1] && resolve(process.argv[1]) === SCRIPT_PATH) {
+ try {
+ console.log(JSON.stringify(main()));
+ } catch (error) {
+ console.error(JSON.stringify(successorFailureResult(error)));
+ process.exitCode = 1;
+ }
+}
diff --git a/apps/web/scripts/review-media-sql-successor.mjs b/apps/web/scripts/review-media-sql-successor.mjs
new file mode 100644
index 0000000000..1d3d78f632
--- /dev/null
+++ b/apps/web/scripts/review-media-sql-successor.mjs
@@ -0,0 +1,2181 @@
+/** Local-only controller for the atomic eleven-stage review-media successor. */
+import { spawnSync } from 'node:child_process';
+import { createHash } from 'node:crypto';
+import {
+ closeSync,
+ constants,
+ existsSync,
+ fstatSync,
+ fsyncSync,
+ linkSync,
+ lstatSync,
+ openSync,
+ readFileSync,
+ realpathSync,
+ writeSync,
+} from 'node:fs';
+import { dirname, isAbsolute, join, relative, resolve, sep } from 'node:path';
+import { fileURLToPath } from 'node:url';
+
+import {
+ boundedMigrationError,
+ psqlConnectionEnvironment,
+ readOriginalStatementVector,
+ selectDirectDatabaseTransport,
+} from './apply-supabase-migration.mjs';
+import {
+ compileElevenMigrationRehearsalSources,
+ compileElevenMigrationBundle,
+ executeMigrationBundle,
+ elevenMigrationBundleSuffixRoot,
+} from './supabase-migration-bundle.mjs';
+import {
+ atomicMigrationSql,
+ statementSpans,
+} from './supabase-migration-transaction.mjs';
+import {
+ TZUDONG_GITHUB_REPOSITORY,
+ TZUDONG_PRODUCTION_ALIASES,
+ TZUDONG_VERCEL_PROJECT_ID,
+ TZUDONG_VERCEL_TEAM_ID,
+ captureVercelRollbackReadbackSync,
+ normalizeVercelRollbackExpected,
+} from './vercel-rollback-readback.mjs';
+
+const SCRIPT_PATH = fileURLToPath(import.meta.url);
+export const REPOSITORY_ROOT = resolve(dirname(SCRIPT_PATH), '../../..');
+export const SUCCESSOR_MANIFEST_PATH = resolve(REPOSITORY_ROOT, '.github/review-media-sql-successor.v1.json');
+export const SUCCESSOR_PRE_COMMIT_EXPIRY_MARKER = '-- successor-pre-commit-expiry-guard';
+
+const HEX_SHA256 = /^[0-9a-f]{64}$/;
+const REVISION = /^[0-9a-f]{40}$/;
+const UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/;
+const PROJECT_REF = 'aqlcofblfxdrjhhdmarw';
+const PURPOSE = 'review-media-current-state-eleven-stage';
+const ADMISSION_MAX_AGE_MS = 15 * 60 * 1000;
+const PREFLIGHT_TIMEOUT_MS = 30_000;
+const PSQL_MAX_TIMEOUT_MS = ADMISSION_MAX_AGE_MS + 30_000;
+const GIT_REMOTE_READ_TIMEOUT_MS = 15_000;
+const PRIVATE_RECEIPT_FILES = Object.freeze({
+ protectedMain: 'protected-main-receipt.json',
+ rehearsal: 'rehearsal-receipt.json',
+ rehearsalAdmission: 'rehearsal-admission.json',
+ rehearsalJournal: 'rehearsal-journal.jsonl',
+ rollback: 'rollback-readback-receipt.json',
+ source: 'source-receipt.json',
+});
+const FIXED_CONTROLLER_CODES = new Set([
+ 'MIGRATION_BUNDLE_OUTCOME_UNCONFIRMED',
+ 'MIGRATION_BUNDLE_READBACK_INVALID',
+ 'MIGRATION_BUNDLE_RECONCILIATION_CONFLICT',
+ 'MIGRATION_BUNDLE_RECONCILIATION_UNKNOWN',
+ 'MIGRATION_BUNDLE_TERMINAL_READBACK_FAILED',
+ 'SUCCESSOR_ADMISSION_EXPIRED',
+ 'SUCCESSOR_ADMISSION_INVALID',
+ 'SUCCESSOR_ARGUMENT_INVALID',
+ 'SUCCESSOR_CHECKOUT_INVALID',
+ 'SUCCESSOR_DATABASE_TARGET_INVALID',
+ 'SUCCESSOR_JOURNAL_APPEND_FAILED',
+ 'SUCCESSOR_JOURNAL_BINDING_MISMATCH',
+ 'SUCCESSOR_JOURNAL_EXISTS',
+ 'SUCCESSOR_JOURNAL_INVALID',
+ 'SUCCESSOR_LAUNCH_HELD',
+ 'SUCCESSOR_MANIFEST_INVALID',
+ 'SUCCESSOR_PREFLIGHT_MISMATCH',
+ 'SUCCESSOR_READBACK_INVALID',
+ 'SUCCESSOR_REHEARSAL_ARGUMENT_INVALID',
+ 'SUCCESSOR_REHEARSAL_BASELINE_INVALID',
+ 'SUCCESSOR_REHEARSAL_OUTCOME_UNCONFIRMED',
+ 'SUCCESSOR_REHEARSAL_OUTPUT_INVALID',
+ 'SUCCESSOR_REHEARSAL_RECEIPT_PUBLISH_FAILED',
+ 'SUCCESSOR_REHEARSAL_RECEIPT_WRITE_FAILED',
+ 'SUCCESSOR_REHEARSAL_RESTORE_CONFLICT',
+ 'SUCCESSOR_REHEARSAL_RESTORE_UNKNOWN',
+ 'SUCCESSOR_ROLLBACK_READBACK_INVALID',
+ 'SUCCESSOR_SOURCE_DRIFT',
+]);
+const FIXED_JOURNAL_OPERATION_CODES = new Set([
+ ...FIXED_CONTROLLER_CODES,
+ 'SUCCESSOR_COMMITTED',
+]);
+
+const operationError = code => {
+ const error = new Error(code);
+ error.code = code;
+ return error;
+};
+const fail = code => { throw operationError(code); };
+const sha256 = value => createHash('sha256').update(value).digest('hex');
+const quote = value => `'${String(value).replaceAll("'", "''")}'`;
+
+function canonical(value) {
+ if (Array.isArray(value)) return value.map(canonical);
+ if (value && typeof value === 'object') {
+ return Object.fromEntries(Object.keys(value).sort().map(key => [key, canonical(value[key])]));
+ }
+ return value;
+}
+
+function equal(left, right) {
+ return JSON.stringify(canonical(left)) === JSON.stringify(canonical(right));
+}
+
+function exactKeys(value, keys, code) {
+ if (!value || typeof value !== 'object' || Array.isArray(value)) fail(code);
+ const actual = Object.keys(value).sort();
+ const expected = [...keys].sort();
+ if (actual.length !== expected.length || actual.some((key, index) => key !== expected[index])) fail(code);
+}
+
+function safeCode(error, key = 'code') {
+ try {
+ const value = error?.[key];
+ return typeof value === 'string' ? value : '';
+ } catch { return ''; }
+}
+
+function safeTrue(error, key) {
+ try { return error?.[key] === true; } catch { return false; }
+}
+
+export function boundedSuccessorError(error) {
+ const code = safeCode(error);
+ if (FIXED_CONTROLLER_CODES.has(code)) {
+ const bounded = operationError(code);
+ if (code === 'SUCCESSOR_JOURNAL_APPEND_FAILED') {
+ const operationCode = safeCode(error, 'operationCode');
+ if (FIXED_JOURNAL_OPERATION_CODES.has(operationCode)) bounded.operationCode = operationCode;
+ }
+ return bounded;
+ }
+ return boundedMigrationError(error);
+}
+
+export function successorFailureResult(error) {
+ const bounded = boundedSuccessorError(error);
+ return {
+ code: bounded.code,
+ ...(bounded.operationCode ? { operationCode: bounded.operationCode } : {}),
+ status: 'failed',
+ };
+}
+
+function journalFailure(operationCode) {
+ const error = operationError('SUCCESSOR_JOURNAL_APPEND_FAILED');
+ if (FIXED_JOURNAL_OPERATION_CODES.has(operationCode)) error.operationCode = operationCode;
+ return error;
+}
+
+function parseJsonObject(bytes, code) {
+ let value;
+ try { value = JSON.parse(Buffer.isBuffer(bytes) ? bytes.toString('utf8') : bytes); } catch { fail(code); }
+ if (!value || typeof value !== 'object' || Array.isArray(value)) fail(code);
+ return value;
+}
+
+function assertHex(value, code) {
+ if (typeof value !== 'string' || !HEX_SHA256.test(value)) fail(code);
+}
+
+function sourceProjection(migration) {
+ return {
+ id: migration.id,
+ version: migration.version,
+ name: migration.name,
+ path: migration.path,
+ bytes: migration.bytes,
+ sha256: migration.sha256,
+ statementCount: migration.statementCount,
+ statementVectorSha256: migration.statementVectorSha256,
+ originalStatementVector: migration.originalStatementVector,
+ };
+}
+
+function assertToolchain(manifest) {
+ const expected = [
+ 'apps/web/scripts/review-media-sql-successor.mjs',
+ 'apps/web/scripts/review-media-sql-rehearsal.mjs',
+ 'apps/web/scripts/supabase-migration-bundle.mjs',
+ 'apps/web/scripts/supabase-migration-transaction.mjs',
+ 'apps/web/scripts/apply-supabase-migration.mjs',
+ 'apps/web/scripts/vercel-rollback-readback.mjs',
+ 'backend/supabase/scripts/g037_supabase_statement_vector.mjs',
+ ];
+ if (!Array.isArray(manifest.toolchain) || manifest.toolchain.length !== expected.length) fail('SUCCESSOR_MANIFEST_INVALID');
+ manifest.toolchain.forEach((entry, index) => {
+ exactKeys(entry, ['path', 'sha256'], 'SUCCESSOR_MANIFEST_INVALID');
+ if (entry.path !== expected[index]) fail('SUCCESSOR_MANIFEST_INVALID');
+ assertHex(entry.sha256, 'SUCCESSOR_MANIFEST_INVALID');
+ const bytes = readFileSync(resolve(REPOSITORY_ROOT, entry.path));
+ if (sha256(bytes) !== entry.sha256) fail('SUCCESSOR_SOURCE_DRIFT');
+ });
+ exactKeys(manifest.legacyReleaseManifest, ['entries', 'path', 'sha256'], 'SUCCESSOR_MANIFEST_INVALID');
+ if (manifest.legacyReleaseManifest.path !== '.github/supabase-migration-release-manifest.v1.json'
+ || manifest.legacyReleaseManifest.entries !== 3) fail('SUCCESSOR_MANIFEST_INVALID');
+ const legacyBytes = readFileSync(resolve(REPOSITORY_ROOT, manifest.legacyReleaseManifest.path));
+ if (sha256(legacyBytes) !== manifest.legacyReleaseManifest.sha256) fail('SUCCESSOR_SOURCE_DRIFT');
+ const legacy = parseJsonObject(legacyBytes, 'SUCCESSOR_SOURCE_DRIFT');
+ if (!Array.isArray(legacy.migrations) || legacy.migrations.length !== 3) fail('SUCCESSOR_SOURCE_DRIFT');
+}
+
+export function loadSuccessorManifest({
+ manifestPath = SUCCESSOR_MANIFEST_PATH,
+ readVectorImpl = readOriginalStatementVector,
+} = {}) {
+ if (resolve(manifestPath) !== SUCCESSOR_MANIFEST_PATH) fail('SUCCESSOR_MANIFEST_INVALID');
+ const bytes = readFileSync(manifestPath);
+ const manifest = parseJsonObject(bytes, 'SUCCESSOR_MANIFEST_INVALID');
+ exactKeys(manifest, [
+ 'advisoryLockKey',
+ 'databaseName',
+ 'executorRole',
+ 'id',
+ 'launchPolicy',
+ 'legacyReleaseManifest',
+ 'migrations',
+ 'operatingTransition',
+ 'projectRef',
+ 'protectedSource',
+ 'purpose',
+ 'requiredWriterFences',
+ 'schemaVersion',
+ 'serverMajor',
+ 'sourceRoot',
+ 'stateRootAlgorithm',
+ 'terminalContract',
+ 'toolchain',
+ ], 'SUCCESSOR_MANIFEST_INVALID');
+ if (manifest.schemaVersion !== 1
+ || manifest.id !== 'review_media_sql_successor_v1'
+ || manifest.purpose !== PURPOSE
+ || manifest.projectRef !== PROJECT_REF
+ || manifest.databaseName !== 'postgres'
+ || manifest.executorRole !== 'postgres'
+ || manifest.serverMajor !== 17
+ || manifest.advisoryLockKey !== '7311754282260211'
+ || manifest.stateRootAlgorithm !== 'review-media-successor-state/v1'
+ || !Array.isArray(manifest.migrations)
+ || manifest.migrations.length !== 11) fail('SUCCESSOR_MANIFEST_INVALID');
+ exactKeys(manifest.operatingTransition, ['afterEleven', 'beforeEleven'], 'SUCCESSOR_MANIFEST_INVALID');
+ if (!equal(manifest.operatingTransition, { beforeEleven: 86, afterEleven: 97 })) {
+ fail('SUCCESSOR_MANIFEST_INVALID');
+ }
+ exactKeys(manifest.protectedSource, ['ref', 'remote', 'repositoryUrl'], 'SUCCESSOR_MANIFEST_INVALID');
+ if (manifest.protectedSource.remote !== 'origin'
+ || manifest.protectedSource.ref !== 'refs/heads/main'
+ || manifest.protectedSource.repositoryUrl !== 'https://github.com/twoimo/tzudong.git') {
+ fail('SUCCESSOR_MANIFEST_INVALID');
+ }
+ assertHex(manifest.sourceRoot, 'SUCCESSOR_MANIFEST_INVALID');
+ exactKeys(manifest.launchPolicy, ['code', 'reason', 'state'], 'SUCCESSOR_MANIFEST_INVALID');
+ if (!['held', 'ready'].includes(manifest.launchPolicy.state)
+ || manifest.launchPolicy.code !== 'SUCCESSOR_LAUNCH_HELD'
+ || manifest.launchPolicy.reason !== 'protected-release-and-fresh-admission-required') {
+ fail('SUCCESSOR_MANIFEST_INVALID');
+ }
+ exactKeys(manifest.requiredWriterFences, [
+ 'adminRecordMutationsHold',
+ 'automaticReviewer',
+ 'browserRpc',
+ 'dashboard',
+ 'directSql',
+ 'g037WriteFreeze',
+ 'providerIngress',
+ ], 'SUCCESSOR_MANIFEST_INVALID');
+ if (!equal(manifest.requiredWriterFences, {
+ adminRecordMutationsHold: 'active',
+ automaticReviewer: 'stopped',
+ browserRpc: 'stopped',
+ dashboard: 'stopped',
+ directSql: 'stopped',
+ g037WriteFreeze: 'active',
+ providerIngress: 'stopped',
+ })) fail('SUCCESSOR_MANIFEST_INVALID');
+ exactKeys(manifest.terminalContract, [
+ 'finishPostimageSha256',
+ 'finishPreimageSha256',
+ 'requiredFunctionBodySha256',
+ 'requiredFunctions',
+ 'requiredPolicies',
+ ], 'SUCCESSOR_MANIFEST_INVALID');
+ const terminalContract = {
+ finishPreimageSha256: 'f6c4e71988ed2b789b887484af5fdf2e482bde98f1d1c5b5d4e7ad124c314bd1',
+ finishPostimageSha256: 'a6b4211b2d8486d482f0758ce249eec157249e1a5176be286b76af7e7a775ee9',
+ requiredFunctionBodySha256: {
+ 'public.admin_review_verification_cleanup(uuid,text,text[])': '468c12ee6c3463876cbccb394fcba8f870459a1f6cf3d945d97307f8551f094b',
+ 'public.finish_review_media_cleanup()': 'a6b4211b2d8486d482f0758ce249eec157249e1a5176be286b76af7e7a775ee9',
+ 'public.mutate_review_with_media(uuid,uuid,text,timestamp with time zone,text,text[],text[])': 'd5d6646a4294c0e7bd0cd75ea47a996c9c57bac6c32a39ac53fbb7a8b1e7e323',
+ 'review_media_private.enqueue_removed()': 'cbf6a87e453e8c2e14d67acd73a92e399fbba4b4f24ac27966806e13ef1fd905',
+ 'review_media_private.guard_references()': '94113c8ae1231e8eae5a698f35a1370a38367e42c79cc87ac62db76c32e735b1',
+ },
+ requiredFunctions: [
+ 'public.mutate_review_with_media(uuid,uuid,text,timestamp with time zone,text,text[],text[])',
+ 'public.queue_review_upload_cleanup(uuid,text[])',
+ 'public.read_review_media_commit(uuid,uuid,text)',
+ 'public.pending_review_media_cleanup()',
+ 'public.finish_review_media_cleanup()',
+ 'public.review_media_delete_allowed(text)',
+ 'public.review_media_upload_allowed(text,text)',
+ 'public.admin_review_verification_cleanup(uuid,text,text[])',
+ ],
+ requiredPolicies: [
+ 'review_media_safe_delete',
+ 'review_media_safe_insert',
+ 'review_media_safe_update',
+ 'review_verifications_owner_read',
+ 'review_verifications_owner_insert',
+ 'review_verifications_owner_delete',
+ 'review_verifications_safe_delete',
+ 'review_photos_food_insert',
+ 'review_photos_food_update',
+ ],
+ };
+ if (!equal(manifest.terminalContract, terminalContract)) fail('SUCCESSOR_MANIFEST_INVALID');
+ assertToolchain(manifest);
+
+ const expectedIds = [
+ 'review_media_commit_cleanup',
+ 'review_verification_private',
+ 'review_media_catalog_integration',
+ 'admin_review_media_completion',
+ 'review_legacy_shared_cleanup',
+ 'admin_evaluation_revision_safeupdate',
+ 'admin_record_expired_preview_readback',
+ 'admin_verification_durable_staging',
+ 'review_media_storage_retirement_fence',
+ 'review_storage_caller_bound_admin_policy',
+ 'review_owner_cleanup_completion',
+ ];
+ const materials = manifest.migrations.map((migration, index) => {
+ exactKeys(migration, [
+ 'bytes',
+ 'id',
+ 'name',
+ 'originalStatementVector',
+ 'path',
+ 'sha256',
+ 'statementCount',
+ 'statementVectorSha256',
+ 'version',
+ ], 'SUCCESSOR_MANIFEST_INVALID');
+ const pathMatch = /^backend\/supabase\/migrations\/(\d{14})_([a-z0-9_]+)\.sql$/.exec(migration.path);
+ if (migration.id !== expectedIds[index]
+ || !pathMatch
+ || migration.version !== pathMatch[1]
+ || migration.name !== pathMatch[2]
+ || migration.id !== migration.name
+ || !Number.isSafeInteger(migration.bytes)
+ || migration.bytes <= 0
+ || !Number.isSafeInteger(migration.statementCount)
+ || migration.statementCount <= 0
+ || !Array.isArray(migration.originalStatementVector)
+ || migration.originalStatementVector.length !== migration.statementCount) fail('SUCCESSOR_MANIFEST_INVALID');
+ assertHex(migration.sha256, 'SUCCESSOR_MANIFEST_INVALID');
+ assertHex(migration.statementVectorSha256, 'SUCCESSOR_MANIFEST_INVALID');
+ if (sha256(JSON.stringify(migration.originalStatementVector)) !== migration.statementVectorSha256) {
+ fail('SUCCESSOR_MANIFEST_INVALID');
+ }
+ const sourceBytes = readFileSync(resolve(REPOSITORY_ROOT, migration.path));
+ if (sourceBytes.length !== migration.bytes || sha256(sourceBytes) !== migration.sha256) fail('SUCCESSOR_SOURCE_DRIFT');
+ const observedVector = readVectorImpl(migration, sourceBytes);
+ if (!equal(observedVector, migration.originalStatementVector)) fail('SUCCESSOR_SOURCE_DRIFT');
+ return { path: migration.path, bytes: sourceBytes, originalVector: observedVector };
+ });
+ if (sha256(JSON.stringify(manifest.migrations.map(sourceProjection))) !== manifest.sourceRoot) {
+ fail('SUCCESSOR_MANIFEST_INVALID');
+ }
+ return Object.freeze({
+ bytes,
+ manifest,
+ manifestSha256: sha256(bytes),
+ materials: Object.freeze(materials),
+ });
+}
+
+function sortedAclSql(expression) {
+ return `ARRAY(SELECT value::text FROM unnest(coalesce(${expression},'{}')) value ORDER BY value::text)`;
+}
+
+export function buildSuccessorStateQuery(manifest, suffixRoot = elevenMigrationBundleSuffixRoot(manifest.migrations)) {
+ const versions = manifest.migrations.map(migration => migration.version);
+ const targetCount = manifest.migrations.length;
+ const targetValues = versions.map(version => `(${quote(version)})`).join(',');
+ const targets = versions.map(version => quote(version)).join(',');
+ const exactRows = manifest.migrations.map(migration => `EXISTS(SELECT 1 FROM supabase_migrations.schema_migrations WHERE version=${quote(migration.version)} AND name=${quote(migration.name)} AND statements=ARRAY(SELECT jsonb_array_elements_text(${quote(JSON.stringify(migration.originalStatementVector))}::jsonb)))`).join(' AND ');
+ const schemas = "'public','pipeline_control','privacy_retention','review_media_private','storage'";
+ const acl = expression => sortedAclSql(expression);
+ return `SELECT * FROM (WITH
+ target_versions(version) AS (VALUES ${targetValues}),
+ ledger_prefix AS (
+ SELECT count(*)::int AS row_count,coalesce(jsonb_agg(jsonb_build_object('version',version,'name',name,'statements',statements) ORDER BY version),'[]'::jsonb)::text AS material
+ FROM supabase_migrations.schema_migrations WHERE version<>ALL(ARRAY[${targets}])
+ ),
+ ledger_all AS (
+ SELECT count(*)::int AS row_count,coalesce(jsonb_agg(jsonb_build_object('version',version,'name',name,'statements',statements) ORDER BY version),'[]'::jsonb)::text AS material
+ FROM supabase_migrations.schema_migrations
+ ),
+ target_state AS (
+ SELECT count(*)::int AS target_count FROM supabase_migrations.schema_migrations WHERE version=ANY(ARRAY[${targets}])
+ ),
+ catalog_material AS (
+ SELECT jsonb_build_object(
+ 'schemas',coalesce((SELECT jsonb_agg(to_jsonb(x) ORDER BY x.schema_name) FROM (SELECT n.nspname AS schema_name,pg_get_userbyid(n.nspowner) AS owner,${acl('n.nspacl')} AS acl FROM pg_namespace n WHERE n.nspname IN(${schemas})) x),'[]'::jsonb),
+ 'relations',coalesce((SELECT jsonb_agg(to_jsonb(x) ORDER BY x.schema_name,x.relation_name) FROM (SELECT n.nspname AS schema_name,c.relname AS relation_name,c.relkind,c.relpersistence,pg_get_userbyid(c.relowner) AS owner,c.relrowsecurity,c.relforcerowsecurity,c.relreplident,${acl('c.reloptions')} AS reloptions,CASE WHEN c.relkind IN('v','m') THEN pg_get_viewdef(c.oid,true) ELSE '' END AS view_definition,${acl('c.relacl')} AS acl FROM pg_class c JOIN pg_namespace n ON n.oid=c.relnamespace WHERE n.nspname IN(${schemas})) x),'[]'::jsonb),
+ 'columns',coalesce((SELECT jsonb_agg(to_jsonb(x) ORDER BY x.schema_name,x.relation_name,x.attnum) FROM (SELECT n.nspname AS schema_name,c.relname AS relation_name,a.attnum,a.attname,format_type(a.atttypid,a.atttypmod) AS data_type,a.attnotnull,a.attidentity,a.attgenerated,coalesce(pg_get_expr(d.adbin,d.adrelid),'') AS default_expression,${acl('a.attacl')} AS acl FROM pg_attribute a JOIN pg_class c ON c.oid=a.attrelid JOIN pg_namespace n ON n.oid=c.relnamespace LEFT JOIN pg_attrdef d ON d.adrelid=a.attrelid AND d.adnum=a.attnum WHERE n.nspname IN(${schemas}) AND a.attnum>0 AND NOT a.attisdropped) x),'[]'::jsonb),
+ 'constraints',coalesce((SELECT jsonb_agg(to_jsonb(x) ORDER BY x.schema_name,x.relation_name,x.constraint_name) FROM (SELECT n.nspname AS schema_name,c.relname AS relation_name,k.conname AS constraint_name,k.contype,k.condeferrable,k.condeferred,k.convalidated,pg_get_constraintdef(k.oid,true) AS definition FROM pg_constraint k JOIN pg_class c ON c.oid=k.conrelid JOIN pg_namespace n ON n.oid=c.relnamespace WHERE n.nspname IN(${schemas})) x),'[]'::jsonb),
+ 'indexes',coalesce((SELECT jsonb_agg(to_jsonb(x) ORDER BY x.schema_name,x.index_name) FROM (SELECT n.nspname AS schema_name,c.relname AS index_name,pg_get_indexdef(i.indexrelid) AS definition FROM pg_index i JOIN pg_class c ON c.oid=i.indexrelid JOIN pg_namespace n ON n.oid=c.relnamespace WHERE n.nspname IN(${schemas})) x),'[]'::jsonb),
+ 'triggers',coalesce((SELECT jsonb_agg(to_jsonb(x) ORDER BY x.schema_name,x.relation_name,x.trigger_name) FROM (SELECT n.nspname AS schema_name,c.relname AS relation_name,t.tgname AS trigger_name,t.tgenabled,pg_get_triggerdef(t.oid,true) AS definition FROM pg_trigger t JOIN pg_class c ON c.oid=t.tgrelid JOIN pg_namespace n ON n.oid=c.relnamespace WHERE n.nspname IN(${schemas}) AND NOT t.tgisinternal) x),'[]'::jsonb),
+ 'policies',coalesce((SELECT jsonb_agg(to_jsonb(x) ORDER BY x.schema_name,x.relation_name,x.policy_name) FROM (SELECT n.nspname AS schema_name,c.relname AS relation_name,p.polname AS policy_name,p.polcmd,p.polpermissive,ARRAY(SELECT coalesce(r.rolname,'PUBLIC') FROM unnest(p.polroles) role_oid LEFT JOIN pg_roles r ON r.oid=role_oid ORDER BY coalesce(r.rolname,'PUBLIC')) AS roles,coalesce(pg_get_expr(p.polqual,p.polrelid),'') AS using_expression,coalesce(pg_get_expr(p.polwithcheck,p.polrelid),'') AS check_expression FROM pg_policy p JOIN pg_class c ON c.oid=p.polrelid JOIN pg_namespace n ON n.oid=c.relnamespace WHERE n.nspname IN(${schemas})) x),'[]'::jsonb),
+ 'functions',coalesce((SELECT jsonb_agg(to_jsonb(x) ORDER BY x.signature) FROM (SELECT p.oid::regprocedure::text AS signature,pg_get_userbyid(p.proowner) AS owner,l.lanname AS language,format_type(p.prorettype,NULL) AS result_type,p.prokind,p.prosecdef,p.proleakproof,p.proisstrict,p.proretset,p.provolatile,p.proparallel,coalesce(p.proconfig,'{}'::text[]) AS config,${acl('p.proacl')} AS acl,encode(sha256(convert_to(p.prosrc,'UTF8')),'hex') AS body_sha256 FROM pg_proc p JOIN pg_namespace n ON n.oid=p.pronamespace JOIN pg_language l ON l.oid=p.prolang WHERE n.nspname IN(${schemas})) x),'[]'::jsonb),
+ 'roles',coalesce((SELECT jsonb_agg(to_jsonb(x) ORDER BY x.role_name) FROM (SELECT r.rolname AS role_name,r.rolsuper,r.rolinherit,r.rolcreaterole,r.rolcreatedb,r.rolcanlogin,r.rolreplication,r.rolbypassrls FROM pg_roles r) x),'[]'::jsonb),
+ 'memberships',coalesce((SELECT jsonb_agg(to_jsonb(x) ORDER BY x.role_name,x.member_name,x.grantor_name) FROM (SELECT role.rolname AS role_name,member.rolname AS member_name,grantor.rolname AS grantor_name,m.admin_option,m.inherit_option,m.set_option FROM pg_auth_members m JOIN pg_roles role ON role.oid=m.roleid JOIN pg_roles member ON member.oid=m.member JOIN pg_roles grantor ON grantor.oid=m.grantor) x),'[]'::jsonb),
+ 'defaultAcls',coalesce((SELECT jsonb_agg(to_jsonb(x) ORDER BY x.owner,x.schema_name,x.object_type) FROM (SELECT pg_get_userbyid(d.defaclrole) AS owner,coalesce(n.nspname,'') AS schema_name,d.defaclobjtype AS object_type,${acl('d.defaclacl')} AS acl FROM pg_default_acl d LEFT JOIN pg_namespace n ON n.oid=d.defaclnamespace) x),'[]'::jsonb)
+ )::text AS material
+ ),
+ g014_material AS (
+ SELECT jsonb_build_object(
+ 'manifest',coalesce((SELECT jsonb_agg(to_jsonb(m) ORDER BY manifest_kind,manifest_key) FROM privacy_retention.g014_catalog_contract_manifest m),'[]'::jsonb),
+ 'allowlist',coalesce((SELECT jsonb_agg(to_jsonb(a) ORDER BY function_schema,function_name,identity_arguments,grantee) FROM privacy_retention.g014_public_rpc_allowlist a),'[]'::jsonb)
+ )::text AS material
+ ),
+ storage_material AS (
+ SELECT jsonb_build_object(
+ 'buckets',coalesce((SELECT jsonb_agg(to_jsonb(b) ORDER BY id) FROM (
+ SELECT id,name,owner,public,avif_autodetection,file_size_limit,allowed_mime_types,owner_id,type,
+ versioning_status,lifecycle_configuration,lifecycle_configuration_generation
+ FROM storage.buckets WHERE id IN('review-photos','review-verifications')) b),'[]'::jsonb),
+ 'policies',coalesce((SELECT jsonb_agg(to_jsonb(x) ORDER BY x.policy_name) FROM (SELECT p.polname AS policy_name,p.polcmd,p.polpermissive,ARRAY(SELECT coalesce(r.rolname,'PUBLIC') FROM unnest(p.polroles) role_oid LEFT JOIN pg_roles r ON r.oid=role_oid ORDER BY coalesce(r.rolname,'PUBLIC')) AS roles,coalesce(pg_get_expr(p.polqual,p.polrelid),'') AS using_expression,coalesce(pg_get_expr(p.polwithcheck,p.polrelid),'') AS check_expression FROM pg_policy p WHERE p.polrelid='storage.objects'::regclass) x),'[]'::jsonb)
+ )::text AS material
+ ),
+ contract AS (
+ SELECT
+ (SELECT encode(sha256(convert_to(p.prosrc,'UTF8')),'hex') FROM pg_proc p WHERE p.oid=to_regprocedure('public.finish_review_media_cleanup()')) AS finish_hash,
+ EXISTS(SELECT 1 FROM pg_roles WHERE rolname='privacy_workflow_owner' AND NOT rolsuper AND NOT rolinherit AND NOT rolcreaterole AND NOT rolcreatedb AND NOT rolcanlogin AND NOT rolreplication AND NOT rolbypassrls)
+ AND EXISTS(SELECT 1 FROM pg_auth_members m WHERE m.roleid='privacy_workflow_owner'::regrole AND m.member='postgres'::regrole AND m.admin_option AND m.grantor<>'postgres'::regrole)
+ AND NOT EXISTS(SELECT 1 FROM pg_auth_members m WHERE m.roleid='privacy_workflow_owner'::regrole AND m.member='postgres'::regrole AND m.admin_option AND m.grantor='postgres'::regrole) AS owner_authority_ready,
+ to_regclass('review_media_private.commits') IS NOT NULL
+ AND to_regclass('review_media_private.cleanup') IS NOT NULL
+ AND NOT EXISTS(SELECT 1 FROM unnest(ARRAY[
+ 'public.mutate_review_with_media(uuid,uuid,text,timestamp with time zone,text,text[],text[])',
+ 'public.queue_review_upload_cleanup(uuid,text[])','public.read_review_media_commit(uuid,uuid,text)',
+ 'public.pending_review_media_cleanup()','public.finish_review_media_cleanup()',
+ 'public.review_media_delete_allowed(text)','public.review_media_upload_allowed(text,text)',
+ 'public.admin_review_verification_cleanup(uuid,text,text[])']) signature WHERE to_regprocedure(signature) IS NULL)
+ AND (SELECT encode(sha256(convert_to(prosrc,'UTF8')),'hex') FROM pg_proc WHERE oid=to_regprocedure('public.mutate_review_with_media(uuid,uuid,text,timestamp with time zone,text,text[],text[])'))='d5d6646a4294c0e7bd0cd75ea47a996c9c57bac6c32a39ac53fbb7a8b1e7e323'
+ AND (SELECT encode(sha256(convert_to(prosrc,'UTF8')),'hex') FROM pg_proc WHERE oid=to_regprocedure('review_media_private.enqueue_removed()'))='cbf6a87e453e8c2e14d67acd73a92e399fbba4b4f24ac27966806e13ef1fd905'
+ AND (SELECT encode(sha256(convert_to(prosrc,'UTF8')),'hex') FROM pg_proc WHERE oid=to_regprocedure('review_media_private.guard_references()'))='94113c8ae1231e8eae5a698f35a1370a38367e42c79cc87ac62db76c32e735b1'
+ AND (SELECT encode(sha256(convert_to(prosrc,'UTF8')),'hex') FROM pg_proc WHERE oid=to_regprocedure('public.admin_review_verification_cleanup(uuid,text,text[])'))='468c12ee6c3463876cbccb394fcba8f870459a1f6cf3d945d97307f8551f094b'
+ AND (SELECT encode(sha256(convert_to(prosrc,'UTF8')),'hex') FROM pg_proc WHERE oid=to_regprocedure('public.finish_review_media_cleanup()'))='a6b4211b2d8486d482f0758ce249eec157249e1a5176be286b76af7e7a775ee9'
+ AND NOT EXISTS(SELECT 1 FROM unnest(ARRAY[
+ 'public.mutate_review_with_media(uuid,uuid,text,timestamp with time zone,text,text[],text[])',
+ 'public.queue_review_upload_cleanup(uuid,text[])','public.read_review_media_commit(uuid,uuid,text)',
+ 'public.pending_review_media_cleanup()','public.finish_review_media_cleanup()',
+ 'public.review_media_delete_allowed(text)','public.review_media_upload_allowed(text,text)']) signature
+ WHERE NOT coalesce(has_function_privilege('authenticated',to_regprocedure(signature),'EXECUTE'),false)
+ OR coalesce(has_function_privilege('anon',to_regprocedure(signature),'EXECUTE'),false)
+ OR coalesce(has_function_privilege('service_role',to_regprocedure(signature),'EXECUTE'),false))
+ AND coalesce(has_function_privilege('service_role',to_regprocedure('public.admin_review_verification_cleanup(uuid,text,text[])'),'EXECUTE'),false)
+ AND NOT coalesce(has_function_privilege('anon',to_regprocedure('public.admin_review_verification_cleanup(uuid,text,text[])'),'EXECUTE'),false)
+ AND NOT coalesce(has_function_privilege('authenticated',to_regprocedure('public.admin_review_verification_cleanup(uuid,text,text[])'),'EXECUTE'),false)
+ AND NOT coalesce(has_function_privilege('anon',to_regprocedure('public.is_user_admin(uuid)'),'EXECUTE'),false)
+ AND NOT coalesce(has_function_privilege('authenticated',to_regprocedure('public.is_user_admin(uuid)'),'EXECUTE'),false)
+ AND NOT coalesce(has_function_privilege('service_role',to_regprocedure('public.is_user_admin(uuid)'),'EXECUTE'),false)
+ AND (SELECT count(*)=9 FROM pg_policy p WHERE p.polrelid='storage.objects'::regclass AND p.polname=ANY(ARRAY[
+ 'review_media_safe_delete','review_media_safe_insert','review_media_safe_update',
+ 'review_verifications_owner_read','review_verifications_owner_insert','review_verifications_owner_delete',
+ 'review_verifications_safe_delete','review_photos_food_insert','review_photos_food_update']))
+ AND EXISTS(SELECT 1 FROM storage.buckets WHERE id='review-verifications' AND NOT public) AS terminal_ready
+ )
+ SELECT json_build_object(
+ 'bundle',json_build_object(
+ 'ledgerCount',ledger_all.row_count,
+ 'priorCount',ledger_prefix.row_count,
+ 'prefixRoot',encode(sha256(convert_to(ledger_prefix.material,'UTF8')),'hex'),
+ 'targetCount',target_state.target_count,
+ 'suffixRoot',CASE WHEN target_state.target_count=${targetCount} AND ${exactRows} THEN ${quote(suffixRoot)} ELSE NULL END
+ ),
+ 'state',json_build_object(
+ 'activeReviewItems',(SELECT count(*)::int FROM pipeline_control.restaurant_review_items WHERE state IN('queued','running')),
+ 'currentUser',current_user,
+ 'databaseName',current_database(),
+ 'finishCleanupBodySha256',contract.finish_hash,
+ 'g014Root',encode(sha256(convert_to(g014_material.material,'UTF8')),'hex'),
+ 'ledgerRoot',encode(sha256(convert_to(ledger_all.material,'UTF8')),'hex'),
+ 'ownerAuthorityReady',contract.owner_authority_ready,
+ 'reviewItems',(SELECT count(*)::int FROM pipeline_control.restaurant_review_items),
+ 'reviewPolicyEnabled',coalesce((SELECT enabled FROM pipeline_control.restaurant_review_policy WHERE singleton),true),
+ 'reviewRuns',(SELECT count(*)::int FROM pipeline_control.restaurant_review_runs),
+ 'schemaRoot',encode(sha256(convert_to(catalog_material.material,'UTF8')),'hex'),
+ 'serverMajor',(current_setting('server_version_num')::int/10000),
+ 'sessionUser',session_user,
+ 'storageRoot',encode(sha256(convert_to(storage_material.material,'UTF8')),'hex'),
+ 'terminalContractReady',contract.terminal_ready
+ )
+ )::text AS state
+ FROM ledger_prefix,ledger_all,target_state,catalog_material,g014_material,storage_material,contract) AS successor_state`;
+}
+
+function validateStageState(stage, index, manifest, suffixRoot, priorCount, prefixRoot) {
+ exactKeys(stage, ['bundle', 'state'], 'SUCCESSOR_ADMISSION_INVALID');
+ exactKeys(stage.bundle, ['ledgerCount', 'prefixRoot', 'priorCount', 'suffixRoot', 'targetCount'], 'SUCCESSOR_ADMISSION_INVALID');
+ exactKeys(stage.state, [
+ 'activeReviewItems',
+ 'currentUser',
+ 'databaseName',
+ 'finishCleanupBodySha256',
+ 'g014Root',
+ 'ledgerRoot',
+ 'ownerAuthorityReady',
+ 'reviewItems',
+ 'reviewPolicyEnabled',
+ 'reviewRuns',
+ 'schemaRoot',
+ 'serverMajor',
+ 'sessionUser',
+ 'storageRoot',
+ 'terminalContractReady',
+ ], 'SUCCESSOR_ADMISSION_INVALID');
+ if (stage.bundle.ledgerCount !== priorCount + index
+ || stage.bundle.priorCount !== priorCount
+ || stage.bundle.prefixRoot !== prefixRoot
+ || stage.bundle.targetCount !== index
+ || stage.bundle.suffixRoot !== (index === manifest.migrations.length ? suffixRoot : null)
+ || stage.state.activeReviewItems !== 0
+ || stage.state.currentUser !== manifest.executorRole
+ || stage.state.databaseName !== manifest.databaseName
+ || stage.state.ownerAuthorityReady !== true
+ || stage.state.reviewItems !== 0
+ || stage.state.reviewPolicyEnabled !== false
+ || stage.state.reviewRuns !== 0
+ || stage.state.serverMajor !== manifest.serverMajor
+ || stage.state.sessionUser !== manifest.executorRole
+ || stage.state.terminalContractReady !== (index === manifest.migrations.length)) {
+ fail('SUCCESSOR_ADMISSION_INVALID');
+ }
+ assertHex(stage.state.ledgerRoot, 'SUCCESSOR_ADMISSION_INVALID');
+ assertHex(stage.state.schemaRoot, 'SUCCESSOR_ADMISSION_INVALID');
+ assertHex(stage.state.g014Root, 'SUCCESSOR_ADMISSION_INVALID');
+ assertHex(stage.state.storageRoot, 'SUCCESSOR_ADMISSION_INVALID');
+ if (stage.state.finishCleanupBodySha256 !== null) {
+ assertHex(stage.state.finishCleanupBodySha256, 'SUCCESSOR_ADMISSION_INVALID');
+ }
+ if (index === manifest.migrations.length - 1
+ && stage.state.finishCleanupBodySha256 !== manifest.terminalContract.finishPreimageSha256) {
+ fail('SUCCESSOR_ADMISSION_INVALID');
+ }
+ if (index === manifest.migrations.length
+ && stage.state.finishCleanupBodySha256 !== manifest.terminalContract.finishPostimageSha256) {
+ fail('SUCCESSOR_ADMISSION_INVALID');
+ }
+ if (index === 0 && stage.state.ledgerRoot !== prefixRoot) fail('SUCCESSOR_ADMISSION_INVALID');
+}
+
+function canonicalReceiptBytes(value) {
+ return Buffer.from(`${JSON.stringify(canonical(value))}\n`);
+}
+
+function readPrivateCanonicalReceipt(directory, fileName, expectedSha256) {
+ if (typeof directory !== 'string' || !isAbsolute(directory) || typeof fileName !== 'string') {
+ fail('SUCCESSOR_ADMISSION_INVALID');
+ }
+ let bytes;
+ let receiptPath;
+ let receiptDirectory;
+ try {
+ const directoryStatus = lstatSync(directory);
+ if (!directoryStatus.isDirectory()
+ || directoryStatus.isSymbolicLink()
+ || (process.platform !== 'win32' && (directoryStatus.mode & 0o077) !== 0)) {
+ fail('SUCCESSOR_ADMISSION_INVALID');
+ }
+ receiptDirectory = realpathSync(directory);
+ receiptPath = assertPrivateAdmissionPath(join(directory, fileName));
+ bytes = readFileSync(receiptPath);
+ } catch {
+ fail('SUCCESSOR_ADMISSION_INVALID');
+ }
+ if (dirname(realpathSync(receiptPath)) !== receiptDirectory) fail('SUCCESSOR_ADMISSION_INVALID');
+ if (sha256(bytes) !== expectedSha256) fail('SUCCESSOR_ADMISSION_INVALID');
+ const receipt = parseJsonObject(bytes, 'SUCCESSOR_ADMISSION_INVALID');
+ if (!bytes.equals(canonicalReceiptBytes(receipt))) fail('SUCCESSOR_ADMISSION_INVALID');
+ return receipt;
+}
+
+function readPrivateCanonicalJournal(directory, fileName, expectedSha256) {
+ if (typeof directory !== 'string' || !isAbsolute(directory) || typeof fileName !== 'string') {
+ fail('SUCCESSOR_ADMISSION_INVALID');
+ }
+ let bytes;
+ let journalPath;
+ let receiptDirectory;
+ try {
+ const directoryStatus = lstatSync(directory);
+ if (!directoryStatus.isDirectory()
+ || directoryStatus.isSymbolicLink()
+ || (process.platform !== 'win32' && (directoryStatus.mode & 0o077) !== 0)) {
+ fail('SUCCESSOR_ADMISSION_INVALID');
+ }
+ receiptDirectory = realpathSync(directory);
+ journalPath = assertPrivateAdmissionPath(join(directory, fileName));
+ bytes = readFileSync(journalPath);
+ } catch {
+ fail('SUCCESSOR_ADMISSION_INVALID');
+ }
+ if (dirname(realpathSync(journalPath)) !== receiptDirectory
+ || sha256(bytes) !== expectedSha256) fail('SUCCESSOR_ADMISSION_INVALID');
+ const text = bytes.toString('utf8');
+ if (!text.endsWith('\n')) fail('SUCCESSOR_ADMISSION_INVALID');
+ const lines = text.slice(0, -1).split('\n');
+ if (lines.length !== 3 || lines.some(line => !line)) fail('SUCCESSOR_ADMISSION_INVALID');
+ return Object.freeze(lines.map(line => {
+ const entry = parseJsonObject(line, 'SUCCESSOR_ADMISSION_INVALID');
+ if (`${JSON.stringify(canonical(entry))}\n` !== `${line}\n`) fail('SUCCESSOR_ADMISSION_INVALID');
+ return entry;
+ }));
+}
+
+function assertReceiptWindow(receipt, admission, createdAt, expiresAt, nowMs) {
+ if (receipt.expiresAt !== admission.expiresAt) fail('SUCCESSOR_ADMISSION_INVALID');
+ const observedAt = Date.parse(receipt.observedAt);
+ if (!Number.isFinite(observedAt)
+ || observedAt < createdAt
+ || observedAt >= expiresAt
+ || observedAt > nowMs + 30_000
+ || Date.parse(receipt.expiresAt) !== expiresAt) fail('SUCCESSOR_ADMISSION_INVALID');
+}
+
+function assertPriorReceiptWindow(receipt, expiresAt, nowMs) {
+ const observedAt = Date.parse(receipt.observedAt);
+ const receiptExpiresAt = Date.parse(receipt.expiresAt);
+ const receiptLeaseStartedAt = receiptExpiresAt - ADMISSION_MAX_AGE_MS;
+ if (!Number.isFinite(observedAt)
+ || !Number.isFinite(receiptExpiresAt)
+ || receiptExpiresAt <= observedAt
+ || receiptExpiresAt - observedAt > ADMISSION_MAX_AGE_MS
+ || observedAt < receiptLeaseStartedAt
+ || observedAt > nowMs + 30_000
+ || receiptExpiresAt < expiresAt
+ || nowMs >= receiptExpiresAt) fail('SUCCESSOR_ADMISSION_INVALID');
+}
+
+function validatePrivateReceipts(admission, manifestRecord, {
+ createdAt,
+ expiresAt,
+ includeRehearsal = true,
+ nowMs,
+ receiptDirectory,
+}) {
+ const { manifest, manifestSha256 } = manifestRecord;
+ const common = receipt => receipt.schemaVersion === 1
+ && receipt.id === manifest.id
+ && receipt.projectRef === manifest.projectRef
+ && receipt.purpose === manifest.purpose
+ && receipt.manifestSha256 === manifestSha256
+ && receipt.sourceRevision === admission.sourceRevision;
+
+ const source = readPrivateCanonicalReceipt(
+ receiptDirectory,
+ PRIVATE_RECEIPT_FILES.source,
+ admission.sourceReceiptSha256,
+ );
+ exactKeys(source, [
+ 'expiresAt', 'id', 'kind', 'manifestSha256', 'observedAt', 'projectRef', 'purpose',
+ 'schemaVersion', 'sourceRevision', 'sourceRoot',
+ ], 'SUCCESSOR_ADMISSION_INVALID');
+ if (!common(source)
+ || source.kind !== 'successor-source-receipt'
+ || source.sourceRoot !== manifest.sourceRoot) fail('SUCCESSOR_ADMISSION_INVALID');
+ assertReceiptWindow(source, admission, createdAt, expiresAt, nowMs);
+
+ const protectedMain = readPrivateCanonicalReceipt(
+ receiptDirectory,
+ PRIVATE_RECEIPT_FILES.protectedMain,
+ admission.protectedMainReceiptSha256,
+ );
+ exactKeys(protectedMain, [
+ 'expiresAt', 'id', 'kind', 'manifestSha256', 'observedAt', 'projectRef', 'purpose',
+ 'ref', 'remote', 'repositoryUrl', 'revision', 'schemaVersion', 'sourceRevision',
+ ], 'SUCCESSOR_ADMISSION_INVALID');
+ if (!common(protectedMain)
+ || protectedMain.kind !== 'protected-main-readback-receipt'
+ || protectedMain.remote !== manifest.protectedSource.remote
+ || protectedMain.ref !== manifest.protectedSource.ref
+ || protectedMain.repositoryUrl !== manifest.protectedSource.repositoryUrl
+ || protectedMain.revision !== admission.sourceRevision) fail('SUCCESSOR_ADMISSION_INVALID');
+ assertReceiptWindow(protectedMain, admission, createdAt, expiresAt, nowMs);
+
+ if (includeRehearsal) {
+ const rehearsalAdmission = readPrivateCanonicalReceipt(
+ receiptDirectory,
+ PRIVATE_RECEIPT_FILES.rehearsalAdmission,
+ admission.rehearsalAdmissionSha256,
+ );
+ const rehearsalWindow = validateSuccessorRehearsalAdmission(rehearsalAdmission, manifestRecord, {
+ now: new Date(nowMs),
+ receiptDirectory,
+ });
+ if (rehearsalWindow.createdAt > createdAt
+ || rehearsalWindow.expiresAt < expiresAt
+ || rehearsalAdmission.sourceReceiptSha256 !== admission.sourceReceiptSha256
+ || rehearsalAdmission.protectedMainReceiptSha256 !== admission.protectedMainReceiptSha256
+ || !equal(rehearsalAdmission.rollback, admission.rollback)
+ || !equal(rehearsalAdmission.writerFences, admission.writerFences)
+ || rehearsalAdmission.journalPathSha256 !== sha256(Buffer.from(resolve(
+ receiptDirectory, PRIVATE_RECEIPT_FILES.rehearsalJournal,
+ )))
+ || rehearsalAdmission.receiptPathSha256 !== sha256(Buffer.from(resolve(
+ receiptDirectory, PRIVATE_RECEIPT_FILES.rehearsal,
+ )))) fail('SUCCESSOR_ADMISSION_INVALID');
+ const rehearsal = readPrivateCanonicalReceipt(
+ receiptDirectory,
+ PRIVATE_RECEIPT_FILES.rehearsal,
+ admission.rehearsalReceiptSha256,
+ );
+ exactKeys(rehearsal, [
+ 'admissionSha256', 'attemptId', 'expiresAt', 'id', 'journalPathSha256', 'kind',
+ 'manifestSha256', 'observedAt', 'passed',
+ 'preflightStateSha256', 'projectRef', 'purpose', 'restoredStateSha256',
+ 'schemaVersion', 'serverVersionNum', 'sourceRevision', 'sourceRoot', 'stageStates',
+ 'stageStatesSha256', 'suffixRoot',
+ ], 'SUCCESSOR_ADMISSION_INVALID');
+ const stageStatesSha256 = sha256(JSON.stringify(canonical(admission.stageStates)));
+ const priorStateSha256 = sha256(JSON.stringify(canonical(admission.stageStates[0])));
+ if (!common(rehearsal)
+ || rehearsal.kind !== 'successor-rehearsal-receipt'
+ || rehearsal.passed !== true
+ || rehearsal.attemptId !== rehearsalAdmission.attemptId
+ || rehearsal.admissionSha256 !== admission.rehearsalAdmissionSha256
+ || rehearsal.expiresAt !== rehearsalAdmission.expiresAt
+ || rehearsal.journalPathSha256 !== sha256(Buffer.from(resolve(
+ receiptDirectory,
+ PRIVATE_RECEIPT_FILES.rehearsalJournal,
+ )))
+ || !Number.isSafeInteger(rehearsal.serverVersionNum)
+ || Math.floor(rehearsal.serverVersionNum / 10_000) !== manifest.serverMajor
+ || rehearsal.sourceRoot !== manifest.sourceRoot
+ || rehearsal.suffixRoot !== elevenMigrationBundleSuffixRoot(manifest.migrations)
+ || rehearsal.stageStatesSha256 !== stageStatesSha256
+ || rehearsal.preflightStateSha256 !== priorStateSha256
+ || rehearsal.restoredStateSha256 !== priorStateSha256
+ || !equal(rehearsal.stageStates, admission.stageStates)) {
+ fail('SUCCESSOR_ADMISSION_INVALID');
+ }
+ assertPriorReceiptWindow(rehearsal, expiresAt, nowMs);
+
+ const journal = readPrivateCanonicalJournal(
+ receiptDirectory,
+ PRIVATE_RECEIPT_FILES.rehearsalJournal,
+ admission.rehearsalJournalSha256,
+ );
+ const [started, verification, publication] = journal;
+ exactKeys(started, [
+ 'admissionSha256', 'attemptId', 'event', 'manifestSha256', 'sourceRevision',
+ 'sourceRoot', 'startedAt',
+ ], 'SUCCESSOR_ADMISSION_INVALID');
+ exactKeys(verification, [
+ 'admissionSha256', 'attemptId', 'code', 'event', 'manifestSha256',
+ 'receiptSha256', 'sourceRevision', 'sourceRoot', 'verifiedAt',
+ ], 'SUCCESSOR_ADMISSION_INVALID');
+ exactKeys(publication, [
+ 'admissionSha256', 'attemptId', 'code', 'event', 'manifestSha256',
+ 'publishedAt', 'receiptSha256', 'sourceRevision', 'sourceRoot',
+ ], 'SUCCESSOR_ADMISSION_INVALID');
+ const startedAt = Date.parse(started.startedAt);
+ const verifiedAt = Date.parse(verification.verifiedAt);
+ const publishedAt = Date.parse(publication.publishedAt);
+ const observedAt = Date.parse(rehearsal.observedAt);
+ const rehearsalLeaseStartedAt = rehearsalWindow.createdAt;
+ if (started.event !== 'started'
+ || verification.event !== 'verification'
+ || verification.code !== 'SUCCESSOR_REHEARSAL_VERIFIED'
+ || publication.event !== 'publication'
+ || publication.code !== 'SUCCESSOR_REHEARSAL_PUBLICATION_CONFIRMED'
+ || started.attemptId !== rehearsal.attemptId
+ || verification.attemptId !== rehearsal.attemptId
+ || publication.attemptId !== rehearsal.attemptId
+ || started.admissionSha256 !== rehearsal.admissionSha256
+ || verification.admissionSha256 !== rehearsal.admissionSha256
+ || publication.admissionSha256 !== rehearsal.admissionSha256
+ || started.manifestSha256 !== manifestSha256
+ || verification.manifestSha256 !== manifestSha256
+ || publication.manifestSha256 !== manifestSha256
+ || started.sourceRevision !== admission.sourceRevision
+ || verification.sourceRevision !== admission.sourceRevision
+ || publication.sourceRevision !== admission.sourceRevision
+ || started.sourceRoot !== manifest.sourceRoot
+ || verification.sourceRoot !== manifest.sourceRoot
+ || publication.sourceRoot !== manifest.sourceRoot
+ || verification.receiptSha256 !== admission.rehearsalReceiptSha256
+ || publication.receiptSha256 !== admission.rehearsalReceiptSha256
+ || !Number.isFinite(startedAt)
+ || !Number.isFinite(verifiedAt)
+ || !Number.isFinite(publishedAt)
+ || startedAt < rehearsalLeaseStartedAt
+ || observedAt < rehearsalLeaseStartedAt
+ || verifiedAt < rehearsalLeaseStartedAt
+ || publishedAt < rehearsalLeaseStartedAt
+ || startedAt > observedAt
+ || verifiedAt < observedAt
+ || publishedAt < verifiedAt
+ || verifiedAt >= Date.parse(rehearsal.expiresAt)
+ || publishedAt >= Date.parse(rehearsal.expiresAt)
+ || verifiedAt > nowMs + 30_000
+ || publishedAt > nowMs + 30_000) fail('SUCCESSOR_ADMISSION_INVALID');
+ }
+
+ const rollback = readPrivateCanonicalReceipt(
+ receiptDirectory,
+ PRIVATE_RECEIPT_FILES.rollback,
+ admission.rollback.readbackSha256,
+ );
+ exactKeys(rollback, [
+ 'deploymentId', 'deploymentSha', 'deploymentUrl', 'expiresAt', 'gitRef', 'id', 'kind',
+ 'manifestSha256', 'observedAt', 'productionAliases', 'projectId', 'projectRef',
+ 'purpose', 'readyState', 'repository', 'schemaVersion', 'sourceRevision', 'state',
+ 'target', 'teamId',
+ ], 'SUCCESSOR_ADMISSION_INVALID');
+ if (!common(rollback)
+ || rollback.kind !== 'rollback-readback-receipt'
+ || rollback.deploymentId !== admission.rollback.deploymentId
+ || rollback.deploymentSha !== admission.rollback.deploymentSha
+ || rollback.deploymentUrl !== admission.rollback.deploymentUrl
+ || rollback.gitRef !== admission.rollback.gitRef
+ || !equal(rollback.productionAliases, admission.rollback.productionAliases)
+ || rollback.projectId !== admission.rollback.projectId
+ || rollback.readyState !== admission.rollback.readyState
+ || rollback.repository !== admission.rollback.repository
+ || rollback.state !== admission.rollback.state
+ || rollback.target !== admission.rollback.target
+ || rollback.teamId !== admission.rollback.teamId) fail('SUCCESSOR_ADMISSION_INVALID');
+ assertReceiptWindow(rollback, admission, createdAt, expiresAt, nowMs);
+
+ for (const key of Object.keys(manifest.requiredWriterFences).sort()) {
+ const fence = readPrivateCanonicalReceipt(
+ receiptDirectory,
+ `writer-fence-${key}.json`,
+ admission.writerFences[key].evidenceSha256,
+ );
+ exactKeys(fence, [
+ 'expiresAt', 'fence', 'id', 'kind', 'manifestSha256', 'observedAt', 'projectRef',
+ 'purpose', 'schemaVersion', 'sourceRevision', 'state',
+ ], 'SUCCESSOR_ADMISSION_INVALID');
+ if (!common(fence)
+ || fence.kind !== 'writer-fence-readback-receipt'
+ || fence.fence !== key
+ || fence.state !== manifest.requiredWriterFences[key]) fail('SUCCESSOR_ADMISSION_INVALID');
+ assertReceiptWindow(fence, admission, createdAt, expiresAt, nowMs);
+ }
+}
+
+function validateAdmissionEnvelope(admission, manifestRecord, {
+ expectedKind,
+ includeRehearsal,
+ now,
+ receiptDirectory,
+ requiredReceiptHashes,
+}) {
+ const { manifest, manifestSha256 } = manifestRecord;
+ if (admission.schemaVersion !== 1
+ || admission.id !== manifest.id
+ || admission.purpose !== manifest.purpose
+ || admission.projectRef !== manifest.projectRef
+ || admission.manifestSha256 !== manifestSha256
+ || admission.sourceRoot !== manifest.sourceRoot
+ || (expectedKind !== undefined && admission.kind !== expectedKind)
+ || !UUID.test(admission.attemptId)
+ || !REVISION.test(admission.sourceRevision)) fail('SUCCESSOR_ADMISSION_INVALID');
+ for (const field of requiredReceiptHashes) {
+ assertHex(admission[field], 'SUCCESSOR_ADMISSION_INVALID');
+ }
+ assertHex(admission.journalPathSha256, 'SUCCESSOR_ADMISSION_INVALID');
+ exactKeys(admission.rollback, [
+ 'deploymentId', 'deploymentSha', 'deploymentUrl', 'gitRef', 'productionAliases',
+ 'projectId', 'readbackSha256', 'readyState', 'repository', 'state', 'target', 'teamId',
+ ], 'SUCCESSOR_ADMISSION_INVALID');
+ if (!REVISION.test(admission.rollback.deploymentSha)) fail('SUCCESSOR_ADMISSION_INVALID');
+ assertHex(admission.rollback.readbackSha256, 'SUCCESSOR_ADMISSION_INVALID');
+ let normalizedRollback;
+ try {
+ normalizedRollback = normalizeVercelRollbackExpected({
+ deploymentId: admission.rollback.deploymentId,
+ deploymentUrl: admission.rollback.deploymentUrl,
+ gitRef: admission.rollback.gitRef,
+ gitSha: admission.rollback.deploymentSha,
+ });
+ } catch { fail('SUCCESSOR_ADMISSION_INVALID'); }
+ if (normalizedRollback.deploymentUrl !== admission.rollback.deploymentUrl
+ || admission.rollback.projectId !== TZUDONG_VERCEL_PROJECT_ID
+ || admission.rollback.teamId !== TZUDONG_VERCEL_TEAM_ID
+ || admission.rollback.repository !== TZUDONG_GITHUB_REPOSITORY
+ || !equal(admission.rollback.productionAliases, TZUDONG_PRODUCTION_ALIASES)
+ || admission.rollback.readyState !== 'READY'
+ || admission.rollback.target !== 'production'
+ || admission.rollback.state !== 'ready') fail('SUCCESSOR_ADMISSION_INVALID');
+
+ const createdAt = Date.parse(admission.createdAt);
+ const expiresAt = Date.parse(admission.expiresAt);
+ const nowMs = now.getTime();
+ if (!Number.isFinite(createdAt) || !Number.isFinite(expiresAt)
+ || expiresAt <= createdAt
+ || expiresAt - createdAt > ADMISSION_MAX_AGE_MS
+ || createdAt > nowMs + 30_000
+ || nowMs >= expiresAt) fail('SUCCESSOR_ADMISSION_EXPIRED');
+
+ const fenceKeys = Object.keys(manifest.requiredWriterFences).sort();
+ exactKeys(admission.writerFences, fenceKeys, 'SUCCESSOR_ADMISSION_INVALID');
+ for (const key of fenceKeys) {
+ const fence = admission.writerFences[key];
+ exactKeys(fence, ['evidenceSha256', 'state'], 'SUCCESSOR_ADMISSION_INVALID');
+ if (fence.state !== manifest.requiredWriterFences[key]) fail('SUCCESSOR_ADMISSION_INVALID');
+ assertHex(fence.evidenceSha256, 'SUCCESSOR_ADMISSION_INVALID');
+ }
+ validatePrivateReceipts(admission, manifestRecord, {
+ createdAt,
+ expiresAt,
+ includeRehearsal,
+ nowMs,
+ receiptDirectory,
+ });
+ return Object.freeze({ createdAt, expiresAt });
+}
+
+export function validateSuccessorAdmission(admission, manifestRecord, {
+ now = new Date(),
+ receiptDirectory,
+} = {}) {
+ const { manifest } = manifestRecord;
+ exactKeys(admission, [
+ 'attemptId',
+ 'createdAt',
+ 'expiresAt',
+ 'id',
+ 'journalPathSha256',
+ 'manifestSha256',
+ 'projectRef',
+ 'protectedMainReceiptSha256',
+ 'purpose',
+ 'rehearsalAdmissionSha256',
+ 'rehearsalJournalSha256',
+ 'rehearsalReceiptSha256',
+ 'rollback',
+ 'schemaVersion',
+ 'sourceReceiptSha256',
+ 'sourceRevision',
+ 'sourceRoot',
+ 'stageStates',
+ 'writerFences',
+ ], 'SUCCESSOR_ADMISSION_INVALID');
+ const envelope = validateAdmissionEnvelope(admission, manifestRecord, {
+ includeRehearsal: true,
+ now,
+ receiptDirectory,
+ requiredReceiptHashes: [
+ 'protectedMainReceiptSha256',
+ 'rehearsalAdmissionSha256',
+ 'rehearsalJournalSha256',
+ 'rehearsalReceiptSha256',
+ 'sourceReceiptSha256',
+ ],
+ });
+
+ const stageBinding = validateSuccessorStageStates(admission.stageStates, manifest);
+ return Object.freeze({ ...envelope, ...stageBinding });
+}
+
+export function validateSuccessorStageStates(stageStates, manifest) {
+ if (!Array.isArray(stageStates)
+ || stageStates.length !== manifest.migrations.length + 1) fail('SUCCESSOR_ADMISSION_INVALID');
+ const suffixRoot = elevenMigrationBundleSuffixRoot(manifest.migrations);
+ const priorCount = stageStates[0]?.bundle?.priorCount;
+ const prefixRoot = stageStates[0]?.bundle?.prefixRoot;
+ if (priorCount !== manifest.operatingTransition.beforeEleven
+ || priorCount + manifest.migrations.length !== manifest.operatingTransition.afterEleven) {
+ fail('SUCCESSOR_ADMISSION_INVALID');
+ }
+ assertHex(prefixRoot, 'SUCCESSOR_ADMISSION_INVALID');
+ stageStates.forEach((stage, index) => validateStageState(
+ stage,
+ index,
+ manifest,
+ suffixRoot,
+ priorCount,
+ prefixRoot,
+ ));
+ const g014Roots = stageStates.map(stage => stage.state.g014Root);
+ if (g014Roots[1] !== g014Roots[0]
+ || g014Roots[2] !== g014Roots[0]
+ || new Set(g014Roots.slice(4)).size !== 1) {
+ fail('SUCCESSOR_ADMISSION_INVALID');
+ }
+ const ledgerRoots = stageStates.map(stage => stage.state.ledgerRoot);
+ if (new Set(ledgerRoots).size !== ledgerRoots.length) fail('SUCCESSOR_ADMISSION_INVALID');
+ return Object.freeze({ prefixRoot, priorCount, suffixRoot });
+}
+
+export function validateSuccessorRehearsalAdmission(admission, manifestRecord, {
+ now = new Date(),
+ receiptDirectory,
+} = {}) {
+ exactKeys(admission, [
+ 'attemptId',
+ 'createdAt',
+ 'expiresAt',
+ 'id',
+ 'journalPathSha256',
+ 'kind',
+ 'manifestSha256',
+ 'projectRef',
+ 'protectedMainReceiptSha256',
+ 'purpose',
+ 'receiptPathSha256',
+ 'rollback',
+ 'schemaVersion',
+ 'sourceReceiptSha256',
+ 'sourceRevision',
+ 'sourceRoot',
+ 'writerFences',
+ ], 'SUCCESSOR_ADMISSION_INVALID');
+ assertHex(admission.receiptPathSha256, 'SUCCESSOR_ADMISSION_INVALID');
+ return validateAdmissionEnvelope(admission, manifestRecord, {
+ expectedKind: 'successor-rehearsal-admission',
+ includeRehearsal: false,
+ now,
+ receiptDirectory,
+ requiredReceiptHashes: ['protectedMainReceiptSha256', 'sourceReceiptSha256'],
+ });
+}
+
+function assertAdmissionFresh(expiresAt, value) {
+ const nowMs = value instanceof Date ? value.getTime() : NaN;
+ if (!Number.isFinite(nowMs) || nowMs >= expiresAt) fail('SUCCESSOR_ADMISSION_EXPIRED');
+}
+
+function assertLiveRollbackReadback(admission, observed, { checkedAt, createdAt, expiresAt }) {
+ exactKeys(observed, [
+ 'deploymentId', 'deploymentUrl', 'gitRef', 'gitSha', 'kind', 'observedAt',
+ 'productionAliases', 'projectId', 'readyState', 'repository', 'schemaVersion',
+ 'target', 'teamId',
+ ], 'SUCCESSOR_ROLLBACK_READBACK_INVALID');
+ const liveObservedAt = Date.parse(observed.observedAt);
+ if (observed.schemaVersion !== 1
+ || observed.kind !== 'vercel-rollback-readback'
+ || observed.deploymentId !== admission.rollback.deploymentId
+ || observed.deploymentUrl !== admission.rollback.deploymentUrl
+ || observed.gitRef !== admission.rollback.gitRef
+ || observed.gitSha !== admission.rollback.deploymentSha
+ || !equal(observed.productionAliases, admission.rollback.productionAliases)
+ || observed.projectId !== admission.rollback.projectId
+ || observed.readyState !== admission.rollback.readyState
+ || observed.repository !== admission.rollback.repository
+ || observed.target !== admission.rollback.target
+ || observed.teamId !== admission.rollback.teamId
+ || !Number.isFinite(liveObservedAt)
+ || liveObservedAt < createdAt
+ || liveObservedAt >= expiresAt
+ || liveObservedAt > checkedAt.getTime()) fail('SUCCESSOR_ROLLBACK_READBACK_INVALID');
+}
+
+export function assertLaunchReady(manifest) {
+ if (manifest.launchPolicy.state !== 'ready') fail('SUCCESSOR_LAUNCH_HELD');
+}
+
+function successorExpirySql(admission, suffix, {
+ armTransactionTimeout = false,
+ maxTransactionTimeoutMs = ADMISSION_MAX_AGE_MS,
+} = {}) {
+ const tag = `$successor_expiry_${sha256(`${admission.expiresAt}:${suffix}`).slice(0, 18)}$`;
+ const armTimeout = armTransactionTimeout
+ ? `\n PERFORM set_config('transaction_timeout',least(${maxTransactionTimeoutMs},greatest(1,ceil(extract(epoch FROM (${quote(admission.expiresAt)}::timestamptz-transaction_timestamp()))*1000)::bigint))::text,true);`
+ : '';
+ return `DO ${tag} BEGIN IF clock_timestamp() >= ${quote(admission.expiresAt)}::timestamptz THEN RAISE EXCEPTION 'SUCCESSOR_ADMISSION_EXPIRED'; END IF;${armTimeout} END ${tag};`;
+}
+
+function stripCompiledStageReadback(sql) {
+ const spans = statementSpans(sql);
+ const read = spans.at(-1);
+ if (!read || !/^SELECT\b/i.test(read.token)) fail('SUCCESSOR_SOURCE_DRIFT');
+ return sql.slice(0, read.start).trimEnd();
+}
+
+function guardCompiledSuccessorStages(compiled, migrations, admission) {
+ const stages = compiled.plans.map((plan, index) => stripCompiledStageReadback(
+ atomicMigrationSql(plan, migrations[index]),
+ ));
+ const compiledPrefix = stages.join('\n');
+ if (!compiled.sql.startsWith(`${compiledPrefix}\n`)) fail('SUCCESSOR_SOURCE_DRIFT');
+ const tail = compiled.sql.slice(compiledPrefix.length + 1);
+ return `${stages.map((stage, index) => `${stage}\n${successorExpirySql(admission, `stage-${index + 1}`)}`).join('\n')}\n${tail}${SUCCESSOR_PRE_COMMIT_EXPIRY_MARKER}\n${successorExpirySql(admission, 'pre-commit')}\n`;
+}
+
+export function compileSuccessorPlan(manifestRecord, admission) {
+ const { manifest, materials } = manifestRecord;
+ const suffixRoot = elevenMigrationBundleSuffixRoot(manifest.migrations);
+ const stateQuery = buildSuccessorStateQuery(manifest, suffixRoot);
+ const migrations = manifest.migrations.map((migration, index) => ({
+ id: migration.id,
+ path: migration.path,
+ sha256: migration.sha256,
+ statementVectorSha256: migration.statementVectorSha256,
+ expectedPriorState: { query: stateQuery, expected: admission.stageStates[index] },
+ terminalReadback: { query: stateQuery, expected: admission.stageStates[index + 1] },
+ }));
+ const prior = admission.stageStates[0];
+ const terminal = admission.stageStates.at(-1);
+ const compiled = compileElevenMigrationBundle({
+ schemaVersion: 1,
+ id: manifest.id,
+ priorCount: prior.bundle.priorCount,
+ prefixRoot: prior.bundle.prefixRoot,
+ suffixRoot,
+ migrations,
+ priorReadback: { query: stateQuery, expected: prior },
+ finalReadback: { query: stateQuery, expected: terminal },
+ }, materials);
+ const lockTag = `$successor_${sha256(manifest.advisoryLockKey).slice(0, 20)}$`;
+ const guardedStages = guardCompiledSuccessorStages(compiled, migrations, admission);
+ const guardedSql = `SET LOCAL lock_timeout='2s';\nSET LOCAL statement_timeout='15s';\nSET LOCAL idle_in_transaction_session_timeout='30s';\nDO ${lockTag} BEGIN PERFORM pg_advisory_xact_lock(${manifest.advisoryLockKey}::bigint); END ${lockTag};\n${successorExpirySql(admission, 'post-lock', { armTransactionTimeout: true })}\n${guardedStages}`;
+ return Object.freeze({
+ ...compiled,
+ sql: guardedSql,
+ stateQuery,
+ });
+}
+
+export function validateSuccessorBaseline(stage, manifest) {
+ const suffixRoot = elevenMigrationBundleSuffixRoot(manifest.migrations);
+ const priorCount = stage?.bundle?.priorCount;
+ const prefixRoot = stage?.bundle?.prefixRoot;
+ if (priorCount !== manifest.operatingTransition.beforeEleven) {
+ fail('SUCCESSOR_REHEARSAL_BASELINE_INVALID');
+ }
+ try {
+ assertHex(prefixRoot, 'SUCCESSOR_ADMISSION_INVALID');
+ validateStageState(stage, 0, manifest, suffixRoot, priorCount, prefixRoot);
+ } catch {
+ fail('SUCCESSOR_REHEARSAL_BASELINE_INVALID');
+ }
+ return Object.freeze({ prefixRoot, priorCount, suffixRoot });
+}
+
+function rehearsalExpirySql(admission, suffix) {
+ const tag = `$successor_rehearsal_expiry_${sha256(`${admission.expiresAt}:${suffix}`).slice(0, 18)}$`;
+ return `DO ${tag} BEGIN IF clock_timestamp() >= ${quote(admission.expiresAt)}::timestamptz THEN RAISE EXCEPTION 'SUCCESSOR_ADMISSION_EXPIRED'; END IF; END ${tag};`;
+}
+
+function rehearsalStageSql(stateQuery, index) {
+ return `SELECT json_build_object('index',${index},'kind','successor-rehearsal-stage','serverVersionNum',current_setting('server_version_num')::int,'state',(${stateQuery})::jsonb)::text;`;
+}
+
+export function compileSuccessorStateReadQuery(manifestRecord) {
+ const stateQuery = buildSuccessorStateQuery(manifestRecord.manifest);
+ return Object.freeze({
+ sql: `SET TRANSACTION ISOLATION LEVEL REPEATABLE READ READ ONLY;\nSET LOCAL statement_timeout='15s';\nSELECT json_build_object('kind','successor-rehearsal-readback','serverVersionNum',current_setting('server_version_num')::int,'state',(${stateQuery})::jsonb)::text;`,
+ stateQuery,
+ });
+}
+
+export function compileSuccessorRehearsalPlan(manifestRecord, admission, expectedBaseline) {
+ const { manifest, materials } = manifestRecord;
+ validateSuccessorBaseline(expectedBaseline, manifest);
+ const sources = compileElevenMigrationRehearsalSources(manifest.migrations, materials);
+ const suffixRoot = elevenMigrationBundleSuffixRoot(manifest.migrations);
+ const stateQuery = buildSuccessorStateQuery(manifest, suffixRoot);
+ const lockTag = `$successor_rehearsal_${sha256(manifest.advisoryLockKey).slice(0, 18)}$`;
+ const baselineTag = `$successor_rehearsal_baseline_${sha256(JSON.stringify(canonical(expectedBaseline))).slice(0, 18)}$`;
+ const parts = [
+ 'BEGIN;',
+ "SET LOCAL lock_timeout='2s';",
+ "SET LOCAL statement_timeout='15s';",
+ "SET LOCAL idle_in_transaction_session_timeout='30s';",
+ `DO ${lockTag} BEGIN PERFORM pg_advisory_xact_lock(${manifest.advisoryLockKey}::bigint); END ${lockTag};`,
+ rehearsalExpirySql(admission, 'after-lock'),
+ 'LOCK TABLE supabase_migrations.schema_migrations IN EXCLUSIVE MODE;',
+ `DO ${baselineTag} BEGIN
+ IF NOT has_schema_privilege(current_user,'supabase_migrations','USAGE')
+ OR NOT has_table_privilege(current_user,'supabase_migrations.schema_migrations','SELECT')
+ OR NOT has_table_privilege(current_user,'supabase_migrations.schema_migrations','INSERT')
+ OR (${stateQuery})::jsonb IS DISTINCT FROM ${quote(JSON.stringify(canonical(expectedBaseline)))}::jsonb
+ THEN RAISE EXCEPTION 'SUCCESSOR_REHEARSAL_BASELINE_INVALID'; END IF;
+END ${baselineTag};`,
+ rehearsalStageSql(stateQuery, 0),
+ ];
+ sources.forEach((source, index) => {
+ const migration = manifest.migrations[index];
+ const ledgerTag = `$successor_rehearsal_ledger_${sha256(migration.id).slice(0, 18)}$`;
+ parts.push(
+ rehearsalExpirySql(admission, `before-${index + 1}`),
+ '\\o /dev/null',
+ source.execution,
+ `INSERT INTO supabase_migrations.schema_migrations(version,name,statements) VALUES(${quote(source.version)},${quote(source.name)},ARRAY(SELECT jsonb_array_elements_text(${quote(JSON.stringify(source.originalVector))}::jsonb)));`,
+ `DO ${ledgerTag} BEGIN
+ IF NOT EXISTS(SELECT 1 FROM supabase_migrations.schema_migrations WHERE version=${quote(source.version)} AND name IS NOT DISTINCT FROM ${quote(source.name)} AND statements IS NOT DISTINCT FROM ARRAY(SELECT jsonb_array_elements_text(${quote(JSON.stringify(source.originalVector))}::jsonb)))
+ THEN RAISE EXCEPTION 'MIGRATION_LEDGER_READBACK_FAILED'; END IF;
+END ${ledgerTag};`,
+ '\\o',
+ rehearsalStageSql(stateQuery, index + 1),
+ );
+ });
+ parts.push(rehearsalExpirySql(admission, 'before-rollback'), 'ROLLBACK;');
+ return Object.freeze({
+ sql: parts.join('\n'),
+ stateQuery,
+ suffixRoot,
+ transactionMode: 'explicit-rollback-only',
+ });
+}
+
+function checkoutPath(path, code, { mustExist = true } = {}) {
+ if (typeof path !== 'string' || !isAbsolute(path)) fail(code);
+ const absolute = resolve(path);
+ const comparison = mustExist ? realpathSync(absolute) : realpathSync(dirname(absolute));
+ const rel = relative(realpathSync(REPOSITORY_ROOT), comparison);
+ if (!rel || (rel !== '..' && !rel.startsWith(`..${sep}`))) fail(code);
+ return absolute;
+}
+
+function assertPrivateAdmissionPath(path) {
+ const absolute = checkoutPath(path, 'SUCCESSOR_ADMISSION_INVALID');
+ const status = lstatSync(absolute);
+ if (!status.isFile() || status.isSymbolicLink()) fail('SUCCESSOR_ADMISSION_INVALID');
+ if (process.platform !== 'win32' && (status.mode & 0o077) !== 0) fail('SUCCESSOR_ADMISSION_INVALID');
+ return absolute;
+}
+
+function assertJournalPath(path) {
+ const absolute = checkoutPath(path, 'SUCCESSOR_JOURNAL_INVALID', { mustExist: false });
+ if (existsSync(absolute)) fail('SUCCESSOR_JOURNAL_EXISTS');
+ const parent = lstatSync(dirname(absolute));
+ if (!parent.isDirectory() || parent.isSymbolicLink()) fail('SUCCESSOR_JOURNAL_INVALID');
+ if (process.platform !== 'win32' && (parent.mode & 0o077) !== 0) fail('SUCCESSOR_JOURNAL_INVALID');
+ return absolute;
+}
+
+function syncDirectory(path, fsyncImpl = fsyncSync) {
+ const descriptor = openSync(path, 'r');
+ try { fsyncImpl(descriptor); } finally { closeSync(descriptor); }
+}
+
+function writeJournalStart(path, entry, { fsyncImpl = fsyncSync } = {}) {
+ let descriptor;
+ try {
+ descriptor = openSync(path, 'wx', 0o600);
+ writeAllSync(descriptor, Buffer.from(`${JSON.stringify(canonical(entry))}\n`));
+ fsyncImpl(descriptor);
+ syncDirectory(dirname(path), fsyncImpl);
+ return descriptor;
+ } catch (error) {
+ if (descriptor !== undefined) {
+ try { closeSync(descriptor); } catch { /* preserve the bounded original failure */ }
+ }
+ if (safeCode(error) === 'EEXIST') fail('SUCCESSOR_JOURNAL_EXISTS');
+ fail('SUCCESSOR_JOURNAL_INVALID');
+ }
+}
+
+function writeAllSync(descriptor, bytes, code = 'SUCCESSOR_JOURNAL_APPEND_FAILED') {
+ let offset = 0;
+ while (offset < bytes.length) {
+ const written = writeSync(descriptor, bytes, offset, bytes.length - offset);
+ if (!Number.isSafeInteger(written) || written <= 0) fail(code);
+ offset += written;
+ }
+}
+
+export function appendJournal(descriptor, entry, {
+ close = true,
+ fsyncImpl = fsyncSync,
+ closeImpl = closeSync,
+} = {}) {
+ let durable = false;
+ try {
+ writeAllSync(descriptor, Buffer.from(`${JSON.stringify(canonical(entry))}\n`));
+ fsyncImpl(descriptor);
+ durable = true;
+ if (close) closeImpl(descriptor);
+ } catch {
+ try { closeSync(descriptor); } catch { /* fixed outcome below */ }
+ const error = operationError('SUCCESSOR_JOURNAL_APPEND_FAILED');
+ error.publicationAuditReadbackEligible = durable;
+ throw error;
+ }
+}
+
+function assertDurableRehearsalArtifacts(directory, admission, fsyncImpl) {
+ // Visibility after a failed publication fsync is not evidence of durability.
+ // Reconfirm the exact admitted files before any new database mutation.
+ try {
+ const directoryStatus = lstatSync(directory);
+ if (!directoryStatus.isDirectory() || directoryStatus.isSymbolicLink()
+ || (process.platform !== 'win32' && (directoryStatus.mode & 0o077) !== 0)) {
+ fail('SUCCESSOR_ADMISSION_INVALID');
+ }
+ for (const [fileName, expected] of [
+ [PRIVATE_RECEIPT_FILES.rehearsalAdmission, admission.rehearsalAdmissionSha256],
+ [PRIVATE_RECEIPT_FILES.rehearsal, admission.rehearsalReceiptSha256],
+ [PRIVATE_RECEIPT_FILES.rehearsalJournal, admission.rehearsalJournalSha256],
+ ]) {
+ const path = join(directory, fileName);
+ const descriptor = openSync(path, constants.O_RDONLY | constants.O_NOFOLLOW);
+ try {
+ const status = fstatSync(descriptor);
+ const named = lstatSync(path);
+ if (!status.isFile() || named.isSymbolicLink()
+ || status.dev !== named.dev || status.ino !== named.ino
+ || (process.platform !== 'win32' && (status.mode & 0o077) !== 0)
+ || sha256(readFileSync(descriptor)) !== expected) fail('SUCCESSOR_ADMISSION_INVALID');
+ fsyncImpl(descriptor);
+ } finally { closeSync(descriptor); }
+ }
+ syncDirectory(directory, fsyncImpl);
+ } catch { fail('SUCCESSOR_ADMISSION_INVALID'); }
+}
+
+function gitCommand(args, repositoryRoot = REPOSITORY_ROOT) {
+ const result = spawnSync('/usr/bin/git', args, { cwd: repositoryRoot, encoding: 'utf8' });
+ if (result.error || result.status !== 0) fail('SUCCESSOR_CHECKOUT_INVALID');
+ return result.stdout.trim();
+}
+
+export function currentSuccessorGitFacts({ repositoryRoot = REPOSITORY_ROOT } = {}) {
+ const revision = gitCommand(['rev-parse', 'HEAD'], repositoryRoot);
+ const dirty = gitCommand(['status', '--porcelain=v1'], repositoryRoot);
+ const symbolic = spawnSync('/usr/bin/git', ['symbolic-ref', '-q', '--short', 'HEAD'], {
+ cwd: repositoryRoot,
+ encoding: 'utf8',
+ });
+ if (symbolic.error || ![0, 1].includes(symbolic.status)) fail('SUCCESSOR_CHECKOUT_INVALID');
+ return { revision, clean: dirty === '', detached: symbolic.status === 1 };
+}
+
+export function protectedSourceReadbackBinding(manifest, revision) {
+ if (!manifest?.protectedSource || !REVISION.test(revision ?? '')) fail('SUCCESSOR_ADMISSION_INVALID');
+ exactKeys(manifest.protectedSource, ['ref', 'remote', 'repositoryUrl'], 'SUCCESSOR_ADMISSION_INVALID');
+ return Object.freeze({
+ projectRef: manifest.projectRef,
+ ref: manifest.protectedSource.ref,
+ remote: manifest.protectedSource.remote,
+ repositoryUrl: manifest.protectedSource.repositoryUrl,
+ revision,
+ schemaVersion: 1,
+ });
+}
+
+export function currentProtectedMainReadback(manifest, {
+ repositoryRoot = REPOSITORY_ROOT,
+ spawnImpl = spawnSync,
+} = {}) {
+ if (!manifest?.protectedSource) fail('SUCCESSOR_CHECKOUT_INVALID');
+ const run = args => {
+ const result = spawnImpl('/usr/bin/git', args, {
+ cwd: repositoryRoot,
+ encoding: 'utf8',
+ killSignal: 'SIGKILL',
+ maxBuffer: 1024 * 1024,
+ timeout: GIT_REMOTE_READ_TIMEOUT_MS,
+ });
+ if (result.error || result.status !== 0) fail('SUCCESSOR_CHECKOUT_INVALID');
+ return result.stdout.trim();
+ };
+ const repositoryUrl = run(['remote', 'get-url', manifest.protectedSource.remote]);
+ if (repositoryUrl !== manifest.protectedSource.repositoryUrl) fail('SUCCESSOR_CHECKOUT_INVALID');
+ const output = run([
+ 'ls-remote',
+ '--exit-code',
+ '--refs',
+ manifest.protectedSource.remote,
+ manifest.protectedSource.ref,
+ ]);
+ const rows = output.split(/\r?\n/).filter(Boolean);
+ const match = rows.length === 1 ? /^([0-9a-f]{40})\t(.+)$/.exec(rows[0]) : null;
+ if (!match || match[2] !== manifest.protectedSource.ref) fail('SUCCESSOR_CHECKOUT_INVALID');
+ return protectedSourceReadbackBinding(manifest, match[1]);
+}
+
+function assertCheckout(admission, facts) {
+ if (!facts || facts.revision !== admission.sourceRevision || facts.clean !== true || facts.detached !== true) {
+ fail('SUCCESSOR_CHECKOUT_INVALID');
+ }
+}
+
+function assertProtectedSourceReadback(admission, manifest, observed) {
+ const expected = protectedSourceReadbackBinding(manifest, admission.sourceRevision);
+ if (!equal(observed, expected)) fail('SUCCESSOR_CHECKOUT_INVALID');
+}
+
+export function assertSuccessorDatabaseTarget(databaseUrl, manifest) {
+ let target;
+ try { target = new URL(databaseUrl); } catch { fail('SUCCESSOR_DATABASE_TARGET_INVALID'); }
+ const malformedPercentEncoding = value => /%(?![0-9a-f]{2})/i.test(value);
+ const queryKeys = [...target.searchParams.keys()];
+ const sslModes = target.searchParams.getAll('sslmode');
+ const safeQuery = queryKeys.length === 0
+ || (queryKeys.length === 1
+ && queryKeys[0] === 'sslmode'
+ && sslModes.length === 1
+ && ['require', 'verify-ca', 'verify-full'].includes(sslModes[0]));
+ const directHost = target.hostname === `db.${manifest.projectRef}.supabase.co`;
+ const sharedPoolerHost = /^aws-[0-9]+-[a-z0-9]+(?:-[a-z0-9]+)*\.pooler\.supabase\.com$/.test(target.hostname);
+ const directTarget = directHost
+ && target.username === 'postgres'
+ && ['5432', '6543'].includes(target.port);
+ const sharedPoolerTarget = sharedPoolerHost
+ && target.username === `postgres.${manifest.projectRef}`
+ && ['5432', '6543'].includes(target.port);
+ if (!['postgres:', 'postgresql:'].includes(target.protocol)
+ || target.hash
+ || malformedPercentEncoding(target.username)
+ || malformedPercentEncoding(target.password)
+ || malformedPercentEncoding(target.pathname)
+ || malformedPercentEncoding(target.search)
+ || target.pathname !== `/${manifest.databaseName}`
+ || !safeQuery
+ || (!directTarget && !sharedPoolerTarget)) fail('SUCCESSOR_DATABASE_TARGET_INVALID');
+}
+
+function transportError(result) {
+ if (result.error) return operationError('MIGRATION_PSQL_EXECUTION_FAILED');
+ const stderr = typeof result.stderr === 'string' ? result.stderr : '';
+ const sqlstate = /ERROR:\s+([0-9A-Z]{5}):/m.exec(stderr)?.[1];
+ if (/ERROR:\s+(?:[0-9A-Z]{5}:\s*)?SUCCESSOR_ADMISSION_EXPIRED\b/m.test(stderr)) {
+ return operationError('SUCCESSOR_ADMISSION_EXPIRED');
+ }
+ const error = operationError(sqlstate ? `MIGRATION_PSQL_FAILED_${sqlstate}` : 'MIGRATION_PSQL_FAILED');
+ error.fixedSqlCode = /ERROR:\s+(?:[0-9A-Z]{5}:\s*)?(MIGRATION_[A-Z0-9_]{1,96})\b/m.exec(stderr)?.[1];
+ return error;
+}
+
+export function createSuccessorPsqlRunner(databaseUrl, {
+ environment = process.env,
+ psql = environment.TZUDONG_SUCCESSOR_PSQL || 'psql',
+ spawnImpl = spawnSync,
+} = {}) {
+ return (_databaseUrl, sql, singleTransaction, { timeoutMs = PSQL_MAX_TIMEOUT_MS } = {}) => {
+ if (!Number.isSafeInteger(timeoutMs) || timeoutMs <= 0 || timeoutMs > PSQL_MAX_TIMEOUT_MS) {
+ fail('SUCCESSOR_ARGUMENT_INVALID');
+ }
+ const args = ['--no-psqlrc', '--set=ON_ERROR_STOP=1', '--quiet', '--tuples-only', '--no-align'];
+ if (singleTransaction) args.splice(1, 0, '--single-transaction');
+ args.push('--file=-');
+ const result = spawnImpl(psql, args, {
+ encoding: 'utf8',
+ env: psqlConnectionEnvironment(databaseUrl, environment),
+ input: `\\set VERBOSITY verbose\n${sql}`,
+ maxBuffer: 16 * 1024 * 1024,
+ timeout: timeoutMs,
+ // A stalled client must not keep spawnSync waiting after SIGTERM.
+ killSignal: 'SIGKILL',
+ });
+ if (result.error || result.status !== 0) throw transportError(result);
+ return result.stdout || '';
+ };
+}
+
+function parseReadback(output) {
+ if (typeof output !== 'string' || !output.trim()) fail('SUCCESSOR_READBACK_INVALID');
+ return parseJsonObject(output.trim(), 'SUCCESSOR_READBACK_INVALID');
+}
+
+function parseRehearsalReadback(output, manifest) {
+ let value;
+ try {
+ value = parseJsonObject(output.trim(), 'SUCCESSOR_REHEARSAL_BASELINE_INVALID');
+ exactKeys(value, ['kind', 'serverVersionNum', 'state'], 'SUCCESSOR_REHEARSAL_BASELINE_INVALID');
+ if (value.kind !== 'successor-rehearsal-readback'
+ || !Number.isSafeInteger(value.serverVersionNum)
+ || Math.floor(value.serverVersionNum / 10_000) !== manifest.serverMajor) {
+ fail('SUCCESSOR_REHEARSAL_BASELINE_INVALID');
+ }
+ validateSuccessorBaseline(value.state, manifest);
+ } catch {
+ fail('SUCCESSOR_REHEARSAL_BASELINE_INVALID');
+ }
+ return value;
+}
+
+function parseRehearsalStages(output, manifest, expectedBaseline, expectedServerVersionNum) {
+ if (typeof output !== 'string') fail('SUCCESSOR_REHEARSAL_OUTPUT_INVALID');
+ const lines = output.split(/\r?\n/).map(line => line.trim()).filter(Boolean);
+ if (lines.length !== manifest.migrations.length + 1) fail('SUCCESSOR_REHEARSAL_OUTPUT_INVALID');
+ let stages;
+ try {
+ stages = lines.map((line, index) => {
+ const value = parseJsonObject(line, 'SUCCESSOR_REHEARSAL_OUTPUT_INVALID');
+ exactKeys(value, ['index', 'kind', 'serverVersionNum', 'state'], 'SUCCESSOR_REHEARSAL_OUTPUT_INVALID');
+ if (value.kind !== 'successor-rehearsal-stage'
+ || value.index !== index
+ || value.serverVersionNum !== expectedServerVersionNum) {
+ fail('SUCCESSOR_REHEARSAL_OUTPUT_INVALID');
+ }
+ return value.state;
+ });
+ validateSuccessorStageStates(stages, manifest);
+ } catch {
+ fail('SUCCESSOR_REHEARSAL_OUTPUT_INVALID');
+ }
+ if (!equal(stages[0], expectedBaseline)) fail('SUCCESSOR_REHEARSAL_OUTPUT_INVALID');
+ return Object.freeze(stages);
+}
+
+function assertRehearsalReceiptPath(path, admissionFile) {
+ let absolute;
+ try {
+ absolute = checkoutPath(path, 'SUCCESSOR_REHEARSAL_RECEIPT_WRITE_FAILED', { mustExist: false });
+ if (absolute !== resolve(dirname(admissionFile), PRIVATE_RECEIPT_FILES.rehearsal)
+ || existsSync(absolute)) fail('SUCCESSOR_REHEARSAL_RECEIPT_WRITE_FAILED');
+ const parent = lstatSync(dirname(absolute));
+ if (!parent.isDirectory()
+ || parent.isSymbolicLink()
+ || (process.platform !== 'win32' && (parent.mode & 0o077) !== 0)) {
+ fail('SUCCESSOR_REHEARSAL_RECEIPT_WRITE_FAILED');
+ }
+ } catch {
+ fail('SUCCESSOR_REHEARSAL_RECEIPT_WRITE_FAILED');
+ }
+ return absolute;
+}
+
+function assertRehearsalJournalPath(path, admissionFile) {
+ const absolute = assertJournalPath(path);
+ if (absolute !== resolve(dirname(admissionFile), PRIVATE_RECEIPT_FILES.rehearsalJournal)) {
+ fail('SUCCESSOR_JOURNAL_BINDING_MISMATCH');
+ }
+ return absolute;
+}
+
+function assertPendingRehearsalReceiptPath(receiptFile, attemptId) {
+ let pendingFile;
+ try {
+ pendingFile = checkoutPath(
+ `${receiptFile}.${attemptId}.pending`,
+ 'SUCCESSOR_REHEARSAL_RECEIPT_WRITE_FAILED',
+ { mustExist: false },
+ );
+ if (dirname(pendingFile) !== dirname(receiptFile) || existsSync(pendingFile)) {
+ fail('SUCCESSOR_REHEARSAL_RECEIPT_WRITE_FAILED');
+ }
+ } catch {
+ fail('SUCCESSOR_REHEARSAL_RECEIPT_WRITE_FAILED');
+ }
+ return pendingFile;
+}
+
+function writePendingReceipt(path, receipt) {
+ let descriptor;
+ try {
+ descriptor = openSync(path, 'wx', 0o600);
+ writeAllSync(
+ descriptor,
+ canonicalReceiptBytes(receipt),
+ 'SUCCESSOR_REHEARSAL_RECEIPT_WRITE_FAILED',
+ );
+ fsyncSync(descriptor);
+ closeSync(descriptor);
+ } catch {
+ if (descriptor !== undefined) {
+ try { closeSync(descriptor); } catch { /* fixed outcome below */ }
+ }
+ fail('SUCCESSOR_REHEARSAL_RECEIPT_WRITE_FAILED');
+ }
+}
+
+function publishPendingReceipt(pendingPath, receiptPath) {
+ let directoryDescriptor;
+ try {
+ linkSync(pendingPath, receiptPath);
+ } catch {
+ const error = operationError('SUCCESSOR_REHEARSAL_RECEIPT_PUBLISH_FAILED');
+ error.publicationReadbackEligible = false;
+ throw error;
+ }
+ try {
+ directoryDescriptor = openSync(dirname(receiptPath), 'r');
+ fsyncSync(directoryDescriptor);
+ } catch {
+ if (directoryDescriptor !== undefined) {
+ try { closeSync(directoryDescriptor); } catch { /* fixed outcome below */ }
+ }
+ const error = operationError('SUCCESSOR_REHEARSAL_RECEIPT_PUBLISH_FAILED');
+ error.publicationReadbackEligible = false;
+ throw error;
+ }
+ try {
+ closeSync(directoryDescriptor);
+ } catch {
+ const error = operationError('SUCCESSOR_REHEARSAL_RECEIPT_PUBLISH_FAILED');
+ error.publicationReadbackEligible = true;
+ throw error;
+ }
+}
+
+function publishedReceiptMatches(path, expectedBytes) {
+ try {
+ const status = lstatSync(path);
+ if (!status.isFile()
+ || status.isSymbolicLink()
+ || (process.platform !== 'win32' && (status.mode & 0o077) !== 0)) return false;
+ return readFileSync(path).equals(expectedBytes);
+ } catch {
+ return false;
+ }
+}
+
+function canonicalJournalBytes(entries) {
+ return Buffer.from(entries
+ .map(entry => JSON.stringify(canonical(entry)))
+ .join('\n') + '\n');
+}
+
+function publishedJournalMatches(path, entries) {
+ try {
+ const status = lstatSync(path);
+ if (!status.isFile()
+ || status.isSymbolicLink()
+ || (process.platform !== 'win32' && (status.mode & 0o077) !== 0)
+ || dirname(realpathSync(path)) !== realpathSync(dirname(path))) return false;
+ return readFileSync(path).equals(canonicalJournalBytes(entries));
+ } catch {
+ return false;
+ }
+}
+
+function appendRehearsalTerminal(descriptor, admission, code, now, {
+ binding,
+ appendJournalImpl = appendJournal,
+} = {}) {
+ const entry = {
+ attemptId: admission.attemptId,
+ code,
+ completedAt: now().toISOString(),
+ event: 'terminal',
+ ...(binding ?? {}),
+ };
+ try {
+ appendJournalImpl(descriptor, entry);
+ } catch {
+ try { closeSync(descriptor); } catch { /* append implementation may already have closed it */ }
+ throw journalFailure(code);
+ }
+}
+
+function appendRehearsalVerification(descriptor, entry, {
+ appendJournalImpl = appendJournal,
+} = {}) {
+ try {
+ appendJournalImpl(descriptor, entry, { close: false });
+ } catch {
+ try { closeSync(descriptor); } catch { /* append implementation may already have closed it */ }
+ fail('SUCCESSOR_JOURNAL_APPEND_FAILED');
+ }
+}
+
+function assertFreshRehearsalBoundary(descriptor, admission, expiresAt, now, {
+ binding,
+ appendJournalImpl = appendJournal,
+} = {}) {
+ const checkedAt = now();
+ try {
+ assertAdmissionFresh(expiresAt, checkedAt);
+ } catch {
+ appendRehearsalTerminal(
+ descriptor,
+ admission,
+ 'SUCCESSOR_ADMISSION_EXPIRED',
+ () => checkedAt,
+ { binding, appendJournalImpl },
+ );
+ fail('SUCCESSOR_ADMISSION_EXPIRED');
+ }
+ return checkedAt;
+}
+
+export function runReviewMediaSuccessorRehearsal({
+ admissionPath,
+ journalPath,
+ receiptPath,
+ environment = process.env,
+}, {
+ now = () => new Date(),
+ gitFactsImpl = currentSuccessorGitFacts,
+ loadManifestImpl = loadSuccessorManifest,
+ protectedMainReadbackImpl = currentProtectedMainReadback,
+ rollbackReadbackImpl = captureVercelRollbackReadbackSync,
+ runPsqlImpl,
+ writePendingReceiptImpl = writePendingReceipt,
+ publishReceiptImpl = publishPendingReceipt,
+ appendJournalImpl = appendJournal,
+ fsyncImpl = fsyncSync,
+} = {}) {
+ const manifestRecord = loadManifestImpl();
+ const admissionFile = assertPrivateAdmissionPath(admissionPath);
+ const admissionBytes = readFileSync(admissionFile);
+ const admission = parseJsonObject(admissionBytes, 'SUCCESSOR_ADMISSION_INVALID');
+ const admissionWindow = validateSuccessorRehearsalAdmission(admission, manifestRecord, {
+ now: now(),
+ receiptDirectory: dirname(admissionFile),
+ });
+ assertCheckout(admission, gitFactsImpl());
+ assertProtectedSourceReadback(
+ admission,
+ manifestRecord.manifest,
+ protectedMainReadbackImpl(manifestRecord.manifest),
+ );
+ let rollbackReadback;
+ try {
+ rollbackReadback = rollbackReadbackImpl({
+ expected: {
+ deploymentId: admission.rollback.deploymentId,
+ deploymentUrl: admission.rollback.deploymentUrl,
+ gitRef: admission.rollback.gitRef,
+ gitSha: admission.rollback.deploymentSha,
+ },
+ now,
+ });
+ } catch {
+ fail('SUCCESSOR_ROLLBACK_READBACK_INVALID');
+ }
+ const rollbackCheckedAt = now();
+ assertAdmissionFresh(admissionWindow.expiresAt, rollbackCheckedAt);
+ assertLiveRollbackReadback(admission, rollbackReadback, {
+ ...admissionWindow,
+ checkedAt: rollbackCheckedAt,
+ });
+
+ const journalFile = assertRehearsalJournalPath(journalPath, admissionFile);
+ if (sha256(Buffer.from(journalFile)) !== admission.journalPathSha256) {
+ fail('SUCCESSOR_JOURNAL_BINDING_MISMATCH');
+ }
+ const receiptFile = assertRehearsalReceiptPath(receiptPath, admissionFile);
+ if (sha256(Buffer.from(receiptFile)) !== admission.receiptPathSha256) {
+ fail('SUCCESSOR_REHEARSAL_RECEIPT_WRITE_FAILED');
+ }
+ const pendingReceiptFile = assertPendingRehearsalReceiptPath(receiptFile, admission.attemptId);
+ const { databaseUrl } = selectDirectDatabaseTransport(environment);
+ assertSuccessorDatabaseTarget(databaseUrl, manifestRecord.manifest);
+ const transport = runPsqlImpl ?? createSuccessorPsqlRunner(databaseUrl, { environment });
+ const readPlan = compileSuccessorStateReadQuery(manifestRecord);
+
+ let preflight;
+ try {
+ preflight = parseRehearsalReadback(
+ transport(databaseUrl, readPlan.sql, true),
+ manifestRecord.manifest,
+ );
+ } catch (error) {
+ const code = safeCode(error) === 'SUCCESSOR_REHEARSAL_BASELINE_INVALID'
+ ? 'SUCCESSOR_REHEARSAL_BASELINE_INVALID'
+ : boundedSuccessorError(error).code;
+ fail(code);
+ }
+
+ let finalRollbackReadback;
+ try {
+ finalRollbackReadback = rollbackReadbackImpl({
+ expected: {
+ deploymentId: admission.rollback.deploymentId,
+ deploymentUrl: admission.rollback.deploymentUrl,
+ gitRef: admission.rollback.gitRef,
+ gitSha: admission.rollback.deploymentSha,
+ },
+ now,
+ });
+ } catch {
+ fail('SUCCESSOR_ROLLBACK_READBACK_INVALID');
+ }
+ const finalRollbackCheckedAt = now();
+ assertAdmissionFresh(admissionWindow.expiresAt, finalRollbackCheckedAt);
+ assertLiveRollbackReadback(admission, finalRollbackReadback, {
+ ...admissionWindow,
+ checkedAt: finalRollbackCheckedAt,
+ });
+
+ const plan = compileSuccessorRehearsalPlan(manifestRecord, admission, preflight.state);
+ const admissionSha256 = sha256(admissionBytes);
+ const startedEntry = {
+ admissionSha256,
+ attemptId: admission.attemptId,
+ event: 'started',
+ manifestSha256: manifestRecord.manifestSha256,
+ sourceRevision: admission.sourceRevision,
+ sourceRoot: manifestRecord.manifest.sourceRoot,
+ startedAt: now().toISOString(),
+ };
+ const descriptor = writeJournalStart(journalFile, startedEntry, { fsyncImpl });
+
+ let transactionOutput;
+ let transactionError;
+ try {
+ assertAdmissionFresh(admissionWindow.expiresAt, now());
+ transactionOutput = transport(databaseUrl, plan.sql, false);
+ } catch (error) {
+ transactionError = error;
+ }
+
+ let restored;
+ try {
+ restored = parseRehearsalReadback(
+ transport(databaseUrl, readPlan.sql, true),
+ manifestRecord.manifest,
+ );
+ } catch {
+ appendRehearsalTerminal(descriptor, admission, 'SUCCESSOR_REHEARSAL_RESTORE_UNKNOWN', now, {
+ appendJournalImpl,
+ });
+ fail('SUCCESSOR_REHEARSAL_RESTORE_UNKNOWN');
+ }
+ if (restored.serverVersionNum !== preflight.serverVersionNum
+ || !equal(restored.state, preflight.state)) {
+ appendRehearsalTerminal(descriptor, admission, 'SUCCESSOR_REHEARSAL_RESTORE_CONFLICT', now, {
+ appendJournalImpl,
+ });
+ fail('SUCCESSOR_REHEARSAL_RESTORE_CONFLICT');
+ }
+ if (transactionError) {
+ const bounded = boundedSuccessorError(transactionError);
+ const code = bounded.code === 'SUCCESSOR_ADMISSION_EXPIRED'
+ ? bounded.code
+ : 'SUCCESSOR_REHEARSAL_OUTCOME_UNCONFIRMED';
+ appendRehearsalTerminal(descriptor, admission, code, now, {
+ appendJournalImpl,
+ });
+ fail(code);
+ }
+
+ let stageStates;
+ try {
+ stageStates = parseRehearsalStages(
+ transactionOutput,
+ manifestRecord.manifest,
+ preflight.state,
+ preflight.serverVersionNum,
+ );
+ } catch {
+ appendRehearsalTerminal(descriptor, admission, 'SUCCESSOR_REHEARSAL_OUTPUT_INVALID', now, {
+ appendJournalImpl,
+ });
+ fail('SUCCESSOR_REHEARSAL_OUTPUT_INVALID');
+ }
+ const receiptObservedAt = assertFreshRehearsalBoundary(
+ descriptor,
+ admission,
+ admissionWindow.expiresAt,
+ now,
+ { appendJournalImpl },
+ );
+ const priorStateSha256 = sha256(JSON.stringify(canonical(preflight.state)));
+ const receipt = {
+ admissionSha256,
+ attemptId: admission.attemptId,
+ expiresAt: admission.expiresAt,
+ id: manifestRecord.manifest.id,
+ kind: 'successor-rehearsal-receipt',
+ journalPathSha256: sha256(Buffer.from(journalFile)),
+ manifestSha256: manifestRecord.manifestSha256,
+ observedAt: receiptObservedAt.toISOString(),
+ passed: true,
+ preflightStateSha256: priorStateSha256,
+ projectRef: manifestRecord.manifest.projectRef,
+ purpose: manifestRecord.manifest.purpose,
+ restoredStateSha256: priorStateSha256,
+ schemaVersion: 1,
+ serverVersionNum: preflight.serverVersionNum,
+ sourceRevision: admission.sourceRevision,
+ sourceRoot: manifestRecord.manifest.sourceRoot,
+ stageStates,
+ stageStatesSha256: sha256(JSON.stringify(canonical(stageStates))),
+ suffixRoot: plan.suffixRoot,
+ };
+ const receiptSha256 = sha256(canonicalReceiptBytes(receipt));
+ try {
+ writePendingReceiptImpl(pendingReceiptFile, receipt);
+ } catch {
+ appendRehearsalTerminal(descriptor, admission, 'SUCCESSOR_REHEARSAL_RECEIPT_WRITE_FAILED', now, {
+ appendJournalImpl,
+ });
+ fail('SUCCESSOR_REHEARSAL_RECEIPT_WRITE_FAILED');
+ }
+ const verificationBinding = {
+ admissionSha256,
+ manifestSha256: manifestRecord.manifestSha256,
+ receiptSha256,
+ sourceRevision: admission.sourceRevision,
+ sourceRoot: manifestRecord.manifest.sourceRoot,
+ };
+ const verificationCheckedAt = assertFreshRehearsalBoundary(
+ descriptor,
+ admission,
+ admissionWindow.expiresAt,
+ now,
+ { binding: verificationBinding, appendJournalImpl },
+ );
+ const verificationEntry = {
+ attemptId: admission.attemptId,
+ code: 'SUCCESSOR_REHEARSAL_VERIFIED',
+ event: 'verification',
+ verifiedAt: verificationCheckedAt.toISOString(),
+ ...verificationBinding,
+ };
+ appendRehearsalVerification(descriptor, verificationEntry, {
+ appendJournalImpl,
+ });
+ assertFreshRehearsalBoundary(
+ descriptor,
+ admission,
+ admissionWindow.expiresAt,
+ now,
+ { binding: verificationBinding, appendJournalImpl },
+ );
+ const receiptBytes = canonicalReceiptBytes(receipt);
+ let publishFailed = false;
+ let publicationReadbackEligible = false;
+ try {
+ publishReceiptImpl(pendingReceiptFile, receiptFile);
+ } catch (error) {
+ publishFailed = true;
+ publicationReadbackEligible = safeTrue(error, 'publicationReadbackEligible');
+ }
+ if ((publishFailed && !publicationReadbackEligible)
+ || !publishedReceiptMatches(receiptFile, receiptBytes)) {
+ appendRehearsalTerminal(
+ descriptor,
+ admission,
+ 'SUCCESSOR_REHEARSAL_RECEIPT_PUBLISH_FAILED',
+ now,
+ { appendJournalImpl },
+ );
+ fail('SUCCESSOR_REHEARSAL_RECEIPT_PUBLISH_FAILED');
+ }
+ const publicationCheckedAt = assertFreshRehearsalBoundary(
+ descriptor,
+ admission,
+ admissionWindow.expiresAt,
+ now,
+ { binding: verificationBinding, appendJournalImpl },
+ );
+ const publicationEntry = {
+ attemptId: admission.attemptId,
+ code: 'SUCCESSOR_REHEARSAL_PUBLICATION_CONFIRMED',
+ event: 'publication',
+ publishedAt: publicationCheckedAt.toISOString(),
+ ...verificationBinding,
+ };
+ let publicationAuditReadback = 'confirmed';
+ try {
+ appendJournalImpl(descriptor, publicationEntry);
+ } catch (error) {
+ publicationAuditReadback = safeTrue(error, 'publicationAuditReadbackEligible') && publishedJournalMatches(
+ journalFile,
+ [startedEntry, verificationEntry, publicationEntry],
+ ) ? 'reconciled' : 'unconfirmed';
+ try { closeSync(descriptor); } catch { /* append implementation may already have closed it */ }
+ if (publicationAuditReadback === 'unconfirmed') fail('SUCCESSOR_JOURNAL_APPEND_FAILED');
+ }
+ assertAdmissionFresh(admissionWindow.expiresAt, now());
+ return Object.freeze({
+ attemptId: admission.attemptId,
+ code: 'SUCCESSOR_REHEARSAL_PASSED',
+ publicationAuditReadback,
+ receiptSha256,
+ publicationReadback: publishFailed ? 'reconciled' : 'confirmed',
+ sourceRevision: admission.sourceRevision,
+ status: 'rolled_back',
+ });
+}
+
+export function runReviewMediaSuccessor({
+ admissionPath,
+ journalPath,
+ environment = process.env,
+}, {
+ now = () => new Date(),
+ gitFactsImpl = currentSuccessorGitFacts,
+ loadManifestImpl = loadSuccessorManifest,
+ protectedMainReadbackImpl = currentProtectedMainReadback,
+ rollbackReadbackImpl = captureVercelRollbackReadbackSync,
+ runPsqlImpl,
+ fsyncImpl = fsyncSync,
+ appendJournalImpl = appendJournal,
+} = {}) {
+ const manifestRecord = loadManifestImpl();
+ assertLaunchReady(manifestRecord.manifest);
+ const admissionFile = assertPrivateAdmissionPath(admissionPath);
+ const admissionBytes = readFileSync(admissionFile);
+ const admission = parseJsonObject(admissionBytes, 'SUCCESSOR_ADMISSION_INVALID');
+ const admissionWindow = validateSuccessorAdmission(admission, manifestRecord, {
+ now: now(),
+ receiptDirectory: dirname(admissionFile),
+ });
+ assertCheckout(admission, gitFactsImpl());
+ assertProtectedSourceReadback(
+ admission,
+ manifestRecord.manifest,
+ protectedMainReadbackImpl(manifestRecord.manifest),
+ );
+ let rollbackReadback;
+ try {
+ rollbackReadback = rollbackReadbackImpl({
+ expected: {
+ deploymentId: admission.rollback.deploymentId,
+ deploymentUrl: admission.rollback.deploymentUrl,
+ gitRef: admission.rollback.gitRef,
+ gitSha: admission.rollback.deploymentSha,
+ },
+ now,
+ });
+ } catch {
+ fail('SUCCESSOR_ROLLBACK_READBACK_INVALID');
+ }
+ const rollbackCheckedAt = now();
+ assertAdmissionFresh(admissionWindow.expiresAt, rollbackCheckedAt);
+ try {
+ assertLiveRollbackReadback(admission, rollbackReadback, {
+ ...admissionWindow,
+ checkedAt: rollbackCheckedAt,
+ });
+ } catch {
+ fail('SUCCESSOR_ROLLBACK_READBACK_INVALID');
+ }
+ const journalFile = assertJournalPath(journalPath);
+ if (sha256(Buffer.from(journalFile)) !== admission.journalPathSha256) fail('SUCCESSOR_JOURNAL_BINDING_MISMATCH');
+ const plan = compileSuccessorPlan(manifestRecord, admission);
+ const { databaseUrl } = selectDirectDatabaseTransport(environment);
+ assertSuccessorDatabaseTarget(databaseUrl, manifestRecord.manifest);
+ const transport = runPsqlImpl ?? createSuccessorPsqlRunner(databaseUrl, { environment });
+
+ let observed;
+ try {
+ const preflightAt = now();
+ assertAdmissionFresh(admissionWindow.expiresAt, preflightAt);
+ const timeoutMs = Math.min(PREFLIGHT_TIMEOUT_MS, admissionWindow.expiresAt - preflightAt.getTime());
+ const preflightSql = [
+ 'SET TRANSACTION ISOLATION LEVEL REPEATABLE READ READ ONLY;',
+ 'SET LOCAL standard_conforming_strings=on;',
+ "SET LOCAL lock_timeout='2s';",
+ "SET LOCAL statement_timeout='15s';",
+ "SET LOCAL idle_in_transaction_session_timeout='30s';",
+ successorExpirySql(admission, 'preflight', {
+ armTransactionTimeout: true,
+ maxTransactionTimeoutMs: PREFLIGHT_TIMEOUT_MS,
+ }),
+ `${plan.stateQuery};`,
+ successorExpirySql(admission, 'post-preflight'),
+ ].join('\n');
+ observed = parseReadback(transport(
+ databaseUrl,
+ preflightSql,
+ true,
+ { timeoutMs },
+ ));
+ } catch (error) {
+ throw boundedSuccessorError(error);
+ }
+ if (!equal(observed, plan.priorReadback.expected)) fail('SUCCESSOR_PREFLIGHT_MISMATCH');
+
+ let finalRollbackReadback;
+ try {
+ finalRollbackReadback = rollbackReadbackImpl({
+ expected: {
+ deploymentId: admission.rollback.deploymentId,
+ deploymentUrl: admission.rollback.deploymentUrl,
+ gitRef: admission.rollback.gitRef,
+ gitSha: admission.rollback.deploymentSha,
+ },
+ now,
+ });
+ } catch {
+ fail('SUCCESSOR_ROLLBACK_READBACK_INVALID');
+ }
+ const finalRollbackCheckedAt = now();
+ assertAdmissionFresh(admissionWindow.expiresAt, finalRollbackCheckedAt);
+ assertLiveRollbackReadback(admission, finalRollbackReadback, {
+ ...admissionWindow,
+ checkedAt: finalRollbackCheckedAt,
+ });
+
+ const admissionSha256 = sha256(admissionBytes);
+ assertDurableRehearsalArtifacts(dirname(admissionFile), admission, fsyncImpl);
+ const startedEntry = {
+ admissionSha256,
+ attemptId: admission.attemptId,
+ event: 'started',
+ manifestSha256: manifestRecord.manifestSha256,
+ sourceRevision: admission.sourceRevision,
+ sourceRoot: manifestRecord.manifest.sourceRoot,
+ startedAt: now().toISOString(),
+ };
+ const descriptor = writeJournalStart(journalFile, startedEntry, { fsyncImpl });
+ let result;
+ try {
+ assertAdmissionFresh(admissionWindow.expiresAt, now());
+ result = executeMigrationBundle(databaseUrl, plan, { runPsqlImpl: transport });
+ } catch (error) {
+ const bounded = boundedSuccessorError(error);
+ try {
+ appendJournalImpl(descriptor, {
+ attemptId: admission.attemptId,
+ code: bounded.code,
+ completedAt: now().toISOString(),
+ event: 'terminal',
+ });
+ } catch {
+ try { closeSync(descriptor); } catch { /* append may already have closed it */ }
+ throw journalFailure(bounded.code);
+ }
+ throw bounded;
+ }
+ const terminalEntry = {
+ attemptId: admission.attemptId,
+ code: 'SUCCESSOR_COMMITTED',
+ completedAt: now().toISOString(),
+ event: 'terminal',
+ };
+ let terminalAuditReadback = 'confirmed';
+ try {
+ appendJournalImpl(descriptor, terminalEntry);
+ } catch (error) {
+ terminalAuditReadback = safeTrue(error, 'publicationAuditReadbackEligible') && publishedJournalMatches(
+ journalFile,
+ [startedEntry, terminalEntry],
+ ) ? 'reconciled' : 'unconfirmed';
+ try { closeSync(descriptor); } catch { /* append implementation may already have closed it */ }
+ if (terminalAuditReadback === 'unconfirmed') throw journalFailure('SUCCESSOR_COMMITTED');
+ }
+ return Object.freeze({
+ attemptId: admission.attemptId,
+ code: 'SUCCESSOR_COMMITTED',
+ sourceRevision: admission.sourceRevision,
+ status: 'committed',
+ terminalAuditReadback,
+ terminal: result,
+ });
+}
+
+function parseArguments(argv) {
+ const result = {};
+ for (let index = 0; index < argv.length; index += 2) {
+ const option = argv[index];
+ const value = argv[index + 1];
+ if (!['--admission', '--journal'].includes(option) || !value || value.startsWith('--') || option.slice(2) in result) {
+ fail('SUCCESSOR_ARGUMENT_INVALID');
+ }
+ result[option.slice(2)] = value;
+ }
+ if (argv.length !== 4 || !result.admission || !result.journal) fail('SUCCESSOR_ARGUMENT_INVALID');
+ return result;
+}
+
+export function main(argv = process.argv.slice(2), dependencies) {
+ const args = parseArguments(argv);
+ return runReviewMediaSuccessor({
+ admissionPath: args.admission,
+ journalPath: args.journal,
+ }, dependencies);
+}
+
+if (process.argv[1] && resolve(process.argv[1]) === SCRIPT_PATH) {
+ try {
+ console.log(JSON.stringify(main()));
+ } catch (error) {
+ console.error(JSON.stringify(successorFailureResult(error)));
+ process.exitCode = 1;
+ }
+}
diff --git a/apps/web/scripts/supabase-migration-bundle.mjs b/apps/web/scripts/supabase-migration-bundle.mjs
index b41250d338..83b25d8b89 100644
--- a/apps/web/scripts/supabase-migration-bundle.mjs
+++ b/apps/web/scripts/supabase-migration-bundle.mjs
@@ -10,6 +10,7 @@ import {
const BUNDLE_SIZE = 4;
const SUCCESSOR_BUNDLE_SIZE = 5;
+const REVIEW_MEDIA_SUCCESSOR_BUNDLE_SIZE = 11;
const HEX_SHA256 = /^[0-9a-f]{64}$/;
const MIGRATION_ID = /^[a-z0-9_]{1,80}$/;
@@ -27,6 +28,7 @@ function boundedApplyError(error) {
if (['MIGRATION_BUNDLE_READBACK_INVALID', 'MIGRATION_BUNDLE_TERMINAL_READBACK_FAILED'].includes(code)) {
return operationError(code);
}
+ if (code === 'SUCCESSOR_ADMISSION_EXPIRED') return operationError(code);
return boundedMigrationError(error);
}
@@ -152,6 +154,10 @@ export function fiveMigrationBundleSuffixRoot(migrations) {
return bundleSuffixRoot(migrations, SUCCESSOR_BUNDLE_SIZE);
}
+export function elevenMigrationBundleSuffixRoot(migrations) {
+ return bundleSuffixRoot(migrations, REVIEW_MEDIA_SUCCESSOR_BUNDLE_SIZE);
+}
+
function validateBundle(bundle, materials, expectedSize) {
exactKeys(bundle, [
'finalReadback',
@@ -277,6 +283,57 @@ export function compileFiveMigrationBundle(bundle, materials) {
return compileMigrationSequence(bundle, materials, SUCCESSOR_BUNDLE_SIZE);
}
+export function compileElevenMigrationBundle(bundle, materials) {
+ return compileMigrationSequence(bundle, materials, REVIEW_MEDIA_SUCCESSOR_BUNDLE_SIZE);
+}
+
+/**
+ * Validate and unwrap the exact eleven migration sources for a rollback-only
+ * rehearsal. The returned execution strings preserve every admitted source
+ * statement byte; only an admitted file-level BEGIN/COMMIT envelope is removed
+ * by migrationEnvelope so the caller can own one outer transaction.
+ */
+export function compileElevenMigrationRehearsalSources(migrations, materials) {
+ if (!Array.isArray(migrations)
+ || migrations.length !== REVIEW_MEDIA_SUCCESSOR_BUNDLE_SIZE
+ || !Array.isArray(materials)
+ || materials.length !== REVIEW_MEDIA_SUCCESSOR_BUNDLE_SIZE) {
+ fail('MIGRATION_BUNDLE_SIZE_INVALID');
+ }
+ const ids = new Set();
+ const paths = new Set();
+ let previousVersion = '';
+ const plans = migrations.map((migration, index) => {
+ if (!migration || typeof migration !== 'object' || Array.isArray(migration)) {
+ fail('MIGRATION_BUNDLE_INVALID');
+ }
+ const match = /^backend\/supabase\/migrations\/(\d{14})_[a-z0-9_]+\.sql$/.exec(migration.path ?? '');
+ const material = materials[index];
+ if (!MIGRATION_ID.test(migration.id ?? '')
+ || !match
+ || !HEX_SHA256.test(migration.sha256 ?? '')
+ || !HEX_SHA256.test(migration.statementVectorSha256 ?? '')
+ || ids.has(migration.id)
+ || paths.has(migration.path)
+ || (previousVersion && match[1] <= previousVersion)
+ || !material
+ || !Buffer.isBuffer(material.bytes)
+ || !Array.isArray(material.originalVector)
+ || material.path !== migration.path) {
+ fail('MIGRATION_BUNDLE_ORDER_OR_IDENTITY_INVALID');
+ }
+ const plan = migrationEnvelope(material.bytes, migration, material.originalVector);
+ if (plan.vectorSha256 !== migration.statementVectorSha256) {
+ fail('MIGRATION_BUNDLE_VECTOR_DIGEST_MISMATCH');
+ }
+ ids.add(migration.id);
+ paths.add(migration.path);
+ previousVersion = match[1];
+ return Object.freeze(plan);
+ });
+ return Object.freeze(plans);
+}
+
export function bundleReconciliationSql(compiled) {
const { query } = validatedReadbackParts(compiled.finalReadback);
return `SET TRANSACTION READ ONLY; SET LOCAL standard_conforming_strings=on; SET LOCAL statement_timeout='15s';\n${query};\n`;
diff --git a/apps/web/tests-unit/naver-loader-failure-browser.test.ts b/apps/web/tests-unit/naver-loader-failure-browser.test.ts
new file mode 100644
index 0000000000..5b6c10a021
--- /dev/null
+++ b/apps/web/tests-unit/naver-loader-failure-browser.test.ts
@@ -0,0 +1,215 @@
+import { afterAll, beforeAll, expect, test } from 'bun:test';
+import { existsSync } from 'node:fs';
+import { join } from 'node:path';
+import { execFileSync } from 'node:child_process';
+import { chromium, expect as browserExpect, type Browser, type Page } from '@playwright/test';
+
+const app = join(import.meta.dir, '..');
+const chromeAvailable = [
+ '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome',
+ '/opt/google/chrome/chrome',
+ join(process.env.ProgramFiles ?? process.env.PROGRAMFILES ?? '', 'Google/Chrome/Application/chrome.exe'),
+ join(process.env.LOCALAPPDATA ?? '', 'Google/Chrome/Application/chrome.exe'),
+].some(existsSync);
+const browserTest = chromeAvailable ? test : test.skip;
+const ui = browserExpect.configure({ timeout: 1500 });
+const baselineInput = process.env.NAVER_LOADER_BASELINE ?? '';
+const baseline = ['056a86600f412eb42df4304e3206841679012f3e', '7a9b19b19416ce098f2f1a6fa5d36c8674b409b0'].includes(baselineInput)
+ ? baselineInput : null;
+let browser: Browser;
+const scripts: Record = {};
+const fakeSdk = 'window.naver={maps:{Map:function(){},LatLng:function(){},Point:function(){},Event:{}}};';
+const fixture = `
+import React from 'react';
+import {createRoot} from 'react-dom/client';
+import {useNaverMaps} from './hooks/use-naver-maps';
+import {NaverMapLoadErrorState} from './components/map/map-view-status-panels';
+import {AdminMapLoadErrorState} from './components/admin/admin-map-load-error-state';
+function Consumer({id}) {
+ const strategy=new URLSearchParams(location.search).get('strategy')==='lazyOnload'?'lazyOnload':'afterInteractive';
+ const {isLoaded,isLoading,loadError,load}=useNaverMaps({autoLoad:true,strategy});
+ const admin=new URLSearchParams(location.search).get('ui')==='admin';
+ return
+ {loadError?'error':isLoaded?'ready':isLoading?'loading':'idle'}
+ {loadError&&(admin?:)}
+ ;
+}
+const count=Number(new URLSearchParams(location.search).get('consumers')||1);
+const root=createRoot(document.getElementById('root'));
+window.__unmountNaverFixture=()=>root.unmount();
+root.render(<>{Array.from({length:count},(_,i)=>)}>);
+`;
+
+beforeAll(async () => {
+ if (!chromeAvailable) return;
+ for (const mode of ['live', 'local', 'configured']) {
+ const bundle = await Bun.build({ entrypoints: ['naver-loader-fixture'], target: 'browser', format: 'iife', write: false,
+ define: { 'process.env': JSON.stringify({ NODE_ENV: 'production', NEXT_PUBLIC_NAVER_CLIENT_ID: mode !== 'local' ? 'browser-fixture-client' : '', NEXT_PUBLIC_TZUDONG_LOCAL_RUNTIME: mode === 'local' ? '1' : '0', NEXT_PUBLIC_NAVER_MAPS_SCRIPT_URL: mode === 'configured' ? 'https://sdk.fixture.test/naver.js' : undefined }) },
+ plugins: [{ name: 'naver-loader-fixture', setup(builder) {
+ builder.onResolve({ filter: /^naver-loader-fixture$/ }, () => ({ path: 'fixture.tsx', namespace: 'fixture' }));
+ builder.onLoad({ filter: /.*/, namespace: 'fixture' }, () => ({ contents: fixture, loader: 'tsx', resolveDir: app }));
+ if (baseline) {
+ builder.onResolve({ filter: /hooks\/use-naver-maps$/ }, () => ({ path: 'use-naver-maps.tsx', namespace: 'baseline' }));
+ builder.onLoad({ filter: /.*/, namespace: 'baseline' }, () => ({ contents: execFileSync('git', ['show', baseline + ':apps/web/hooks/use-naver-maps.tsx'], { cwd: app, encoding: 'utf8' }), loader: 'tsx', resolveDir: app }));
+ }
+ builder.onResolve({ filter: /^@\// }, ({ path }) => {
+ const resolved = ['', '.ts', '.tsx'].map(suffix => join(app, path.slice(2)) + suffix).find(existsSync);
+ return resolved ? { path: resolved } : undefined;
+ });
+ } }] });
+ expect(bundle.success).toBe(true);
+ scripts[mode] = await bundle.outputs[0].text();
+ }
+ browser = await chromium.launch({ channel: 'chrome', headless: true });
+});
+afterAll(async () => { await browser?.close(); });
+
+type Outcome = 'network-error' | 'missing-api' | 'success';
+async function run(outcomes: Outcome[], check: (page: Page, counts: { live: number; stub: number }) => Promise, consumers = 1, mode = 'live', uiMode = 'public', strategy = 'afterInteractive') {
+ const context = await browser.newContext();
+ const page = await context.newPage();
+ const counts = { live: 0, stub: 0 };
+ if (strategy === 'lazyOnload') await page.addInitScript(() => {
+ let id = 0;
+ const callbacks = new Map();
+ Object.assign(window, {
+ requestIdleCallback: (callback: IdleRequestCallback) => { callbacks.set(++id, callback); return id; },
+ cancelIdleCallback: (handle: number) => { callbacks.delete(handle); },
+ __idleCallbackCount: () => callbacks.size,
+ __flushIdle: () => {
+ const pending = [...callbacks.values()]; callbacks.clear();
+ pending.forEach(callback => callback({ didTimeout: false, timeRemaining: () => 50 }));
+ },
+ });
+ });
+ await page.addInitScript(() => {
+ const active = new Map>();
+ const add = document.addEventListener.bind(document);
+ const remove = document.removeEventListener.bind(document);
+ document.addEventListener = ((type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions) => {
+ if (type === 'load' || type === 'error') {
+ const listeners = active.get(type) ?? new Set();
+ listeners.add(listener); active.set(type, listeners);
+ }
+ add(type, listener, options);
+ }) as typeof document.addEventListener;
+ document.removeEventListener = ((type: string, listener: EventListenerOrEventListenerObject, options?: boolean | EventListenerOptions) => {
+ active.get(type)?.delete(listener); remove(type, listener, options);
+ }) as typeof document.removeEventListener;
+ Object.assign(window, { __documentScriptListenerCount: () => [...active.values()].reduce((sum, listeners) => sum + listeners.size, 0) });
+ });
+ await page.route('**/*', async route => {
+ const url = new URL(route.request().url());
+ if (route.request().isNavigationRequest()) return route.fulfill({ contentType: 'text/html', body: '
' });
+ if ((url.hostname === 'oapi.map.naver.com' && url.pathname === '/openapi/v3/maps.js')
+ || (url.hostname === 'sdk.fixture.test' && url.pathname === '/naver.js')) {
+ const outcome = outcomes[Math.min(counts.live++, outcomes.length - 1)];
+ if (outcome === 'network-error') return route.abort('failed');
+ return route.fulfill({ contentType: 'application/javascript', body: outcome === 'success' ? fakeSdk : '/* provider loaded without an API */' });
+ }
+ if (url.pathname === '/__local/naver-maps.js') {
+ counts.stub++;
+ return route.fulfill({ contentType: 'application/javascript', body: fakeSdk });
+ }
+ return route.abort();
+ });
+ try {
+ await page.goto(`http://naver-loader.fixture.test/?consumers=${consumers}&ui=${uiMode}&strategy=${strategy}`);
+ await page.addScriptTag({ content: scripts[mode] });
+ await check(page, counts);
+ } catch (error) {
+ console.error(JSON.stringify({ mode, consumers, strategy, uiMode, liveRequests: counts.live, stubRequests: counts.stub }));
+ throw error;
+ } finally { await context.close(); }
+}
+
+browserTest('live SDK success becomes ready without requesting the local stub', async () => run(['success'], async (page, counts) => {
+ await ui(page.getByTestId('state-0')).toHaveText('ready');
+ expect(counts).toEqual({ live: 1, stub: 0 });
+}));
+
+for (const failure of ['network-error', 'missing-api'] as const) {
+ browserTest(`live ${failure} remains a visible error and never becomes a simulated map`, async () => run([failure], async (page, counts) => {
+ await ui(page.getByTestId('state-0')).toHaveText('error');
+ await ui(page.getByRole('heading', { name: '지도를 불러오지 못했어요' })).toBeVisible();
+ await page.waitForTimeout(250); // Observe automatic retries; no product delay.
+ expect(counts).toEqual({ live: 1, stub: 0 });
+ expect(await page.locator('script[data-local-naver-maps="true"]').count()).toBe(0);
+ }));
+
+ browserTest(`explicit retry recovers after ${failure} without a page reload`, async () => run([failure, 'success'], async (page, counts) => {
+ await ui(page.getByTestId('state-0')).toHaveText('error');
+ await page.getByRole('button', { name: '다시 시도', exact: true }).click();
+ await ui(page.getByTestId('state-0')).toHaveText('ready');
+ expect(counts).toEqual({ live: 2, stub: 0 });
+ }));
+}
+
+for (const failure of ['network-error', 'missing-api'] as const) {
+browserTest(`one retry after ${failure} recovers every concurrent consumer`, async () => run([failure, 'success'], async (page, counts) => {
+ await ui(page.getByTestId('state-0')).toHaveText('error');
+ await ui(page.getByTestId('state-1')).toHaveText('error');
+ expect(counts).toEqual({ live: 1, stub: 0 });
+ await page.getByRole('button', { name: '다시 시도', exact: true }).first().click();
+ await ui(page.getByTestId('state-0')).toHaveText('ready');
+ await ui(page.getByTestId('state-1')).toHaveText('ready');
+ expect(counts).toEqual({ live: 2, stub: 0 });
+}, 2));
+
+browserTest(`admin error control recovers after ${failure}`, async () => run([failure, 'success'], async (page, counts) => {
+ await ui(page.getByTestId('state-0')).toHaveText('error');
+ await ui(page.getByRole('alert')).toContainText('네이버 지도를 불러오지 못했습니다');
+ await page.getByRole('button', { name: '다시 시도', exact: true }).click();
+ await ui(page.getByTestId('state-0')).toHaveText('ready');
+ await ui(page.getByRole('alert')).toHaveCount(0);
+ expect(counts).toEqual({ live: 2, stub: 0 });
+}, 1, 'live', 'admin'));
+
+browserTest(`configured SDK URL shares one retry after ${failure}`, async () => run([failure, 'success'], async (page, counts) => {
+ await ui(page.getByTestId('state-0')).toHaveText('error');
+ await ui(page.getByTestId('state-1')).toHaveText('error');
+ expect(counts).toEqual({ live: 1, stub: 0 });
+ await page.getByRole('button', { name: '다시 시도', exact: true }).first().click();
+ await ui(page.getByTestId('state-0')).toHaveText('ready');
+ await ui(page.getByTestId('state-1')).toHaveText('ready');
+ expect(counts).toEqual({ live: 2, stub: 0 });
+}, 2, 'configured'));
+}
+
+browserTest('explicit local offline mode still uses its deterministic stub', async () => run([], async (page, counts) => {
+ await ui(page.getByTestId('state-0')).toHaveText('ready');
+ expect(counts).toEqual({ live: 0, stub: 1 });
+}, 1, 'local'));
+
+browserTest('unmount releases provider subscriptions and settled script callbacks', async () => run(['success'], async page => {
+ await ui(page.getByTestId('state-0')).toHaveText('ready');
+ expect(await page.evaluate('window.__documentScriptListenerCount()')).toBe(2);
+ expect(await page.evaluate(() => {
+ const script = document.querySelector('script[src*="oapi.map.naver.com"]');
+ return Boolean(script?.onload || script?.onerror);
+ })).toBe(false);
+ await page.evaluate('window.__unmountNaverFixture()');
+ expect(await page.evaluate('window.__documentScriptListenerCount()')).toBe(0);
+}));
+
+browserTest('lazy consumers share one deferred request and one explicit retry', async () => run(['network-error', 'success'], async (page, counts) => {
+ await page.waitForFunction('window.__idleCallbackCount() === 2');
+ await page.evaluate('window.__flushIdle()');
+ await ui(page.getByTestId('state-0')).toHaveText('error');
+ await ui(page.getByTestId('state-1')).toHaveText('error');
+ expect(counts).toEqual({ live: 1, stub: 0 });
+ await page.getByRole('button', { name: '다시 시도', exact: true }).first().click();
+ await page.waitForFunction('window.__idleCallbackCount() === 1');
+ await page.evaluate('window.__flushIdle()');
+ await ui(page.getByTestId('state-0')).toHaveText('ready');
+ await ui(page.getByTestId('state-1')).toHaveText('ready');
+ expect(counts).toEqual({ live: 2, stub: 0 });
+}, 2, 'live', 'public', 'lazyOnload'));
+
+browserTest('unmount cancels deferred provider work before script injection', async () => run(['success'], async (page, counts) => {
+ await page.waitForFunction('window.__idleCallbackCount() === 1');
+ await page.evaluate('window.__unmountNaverFixture()');
+ expect(await page.evaluate('window.__idleCallbackCount()')).toBe(0);
+ await page.evaluate('window.__flushIdle()');
+ expect(counts).toEqual({ live: 0, stub: 0 });
+}, 1, 'live', 'public', 'lazyOnload'));
diff --git a/apps/web/tests-unit/review-media-fixture-ownership-verifier.test.ts b/apps/web/tests-unit/review-media-fixture-ownership-verifier.test.ts
new file mode 100644
index 0000000000..2f483a14cd
--- /dev/null
+++ b/apps/web/tests-unit/review-media-fixture-ownership-verifier.test.ts
@@ -0,0 +1,39 @@
+import { expect, test } from 'bun:test';
+import { createHash } from 'node:crypto';
+import { basename } from 'node:path';
+import {
+ assertFixtureOwnershipBindings,
+ fixtureOwnershipBindings,
+} from '../performance/review-db-successor-20261012/verify-fixture-ownership.mjs';
+
+const sha256 = (bytes: Buffer) => createHash('sha256').update(bytes).digest('hex');
+const sourceBytes = new Map([
+ ['verify-review-media-sql-successor.mjs', Buffer.from('fixture role and database helper')],
+ ['verify-review-media-sql-rehearsal.mjs', Buffer.from('container ownership helper')],
+ ['verify-fixture-ownership.mjs', Buffer.from('ownership verifier')],
+]);
+const readFileImpl = (path: string) => {
+ const bytes = sourceBytes.get(basename(path));
+ if (!bytes) throw new Error('unexpected source path');
+ return bytes;
+};
+
+test('ownership evidence binds both imported implementations and the verifier', () => {
+ const bindings = fixtureOwnershipBindings({ readFileImpl });
+ expect(bindings).toEqual({
+ sourceSha256: sha256(sourceBytes.get('verify-review-media-sql-successor.mjs')!),
+ containerHelperSha256: sha256(sourceBytes.get('verify-review-media-sql-rehearsal.mjs')!),
+ verifierSha256: sha256(sourceBytes.get('verify-fixture-ownership.mjs')!),
+ });
+ expect(assertFixtureOwnershipBindings(bindings, { readFileImpl })).toEqual(bindings);
+});
+
+for (const path of sourceBytes.keys()) {
+ test(`changed ${path} invalidates ownership evidence`, () => {
+ const bindings = fixtureOwnershipBindings({ readFileImpl });
+ const changedReader = (sourcePath: string) => basename(sourcePath) === path
+ ? Buffer.from('changed implementation') : readFileImpl(sourcePath);
+ expect(() => assertFixtureOwnershipBindings(bindings, { readFileImpl: changedReader }))
+ .toThrow('FIXTURE_OWNERSHIP_SOURCE_CHANGED');
+ });
+}
diff --git a/apps/web/tests-unit/review-media-migration-bundle.test.ts b/apps/web/tests-unit/review-media-migration-bundle.test.ts
new file mode 100644
index 0000000000..213ddc23df
--- /dev/null
+++ b/apps/web/tests-unit/review-media-migration-bundle.test.ts
@@ -0,0 +1,162 @@
+import { expect, test } from 'bun:test';
+import { readFileSync } from 'node:fs';
+
+import {
+ bundleReconciliationOutcome,
+ compileElevenMigrationBundle,
+ compileElevenMigrationRehearsalSources,
+ elevenMigrationBundleSuffixRoot,
+ executeMigrationBundle,
+} from '../scripts/supabase-migration-bundle.mjs';
+import { statementSpans } from '../scripts/supabase-migration-transaction.mjs';
+
+const manifest = JSON.parse(readFileSync(
+ new URL('../../../.github/review-media-sql-successor.v1.json', import.meta.url),
+ 'utf8',
+));
+
+function fixture() {
+ const prefixRoot = 'a'.repeat(64);
+ const priorCount = 86;
+ const materials = manifest.migrations.map((migration: any) => ({
+ path: migration.path,
+ bytes: readFileSync(new URL(`../../../${migration.path}`, import.meta.url)),
+ originalVector: migration.originalStatementVector,
+ }));
+ const migrations = manifest.migrations.map((migration: any, index: number) => ({
+ id: migration.id,
+ path: migration.path,
+ sha256: migration.sha256,
+ statementVectorSha256: migration.statementVectorSha256,
+ expectedPriorState: {
+ query: "SELECT '{\"fixture\":true}'::text AS state",
+ expected: { fixture: true, stage: index },
+ },
+ terminalReadback: {
+ query: "SELECT '{\"fixture\":true}'::text AS state",
+ expected: { fixture: true, stage: index + 1 },
+ },
+ }));
+ const suffixRoot = elevenMigrationBundleSuffixRoot(migrations);
+ const query = "SELECT '{\"fixture\":true}'::text AS state";
+ const priorReadback = {
+ query,
+ expected: { bundle: { ledgerCount: 86, prefixRoot, priorCount, suffixRoot: null, targetCount: 0 } },
+ };
+ const finalReadback = {
+ query,
+ expected: { bundle: { ledgerCount: 97, prefixRoot, priorCount, suffixRoot, targetCount: 11 } },
+ };
+ migrations[0].expectedPriorState = priorReadback;
+ return {
+ bundle: {
+ schemaVersion: 1,
+ id: 'review_media_sql_successor_v1',
+ priorCount,
+ prefixRoot,
+ suffixRoot,
+ migrations,
+ priorReadback,
+ finalReadback,
+ },
+ materials,
+ };
+}
+
+test('all eleven exact sources compile into one outer transaction', () => {
+ const input = fixture();
+ const compiled = compileElevenMigrationBundle(input.bundle, input.materials);
+ const spans = statementSpans(compiled.sql);
+ expect(compiled.transactionMode).toBe('single-outer-transaction');
+ expect(compiled.plans).toHaveLength(11);
+ expect(compiled.sql.match(/INSERT INTO supabase_migrations\.schema_migrations/g)).toHaveLength(11);
+ expect(spans.filter(span => /^SELECT\b/i.test(span.token))).toHaveLength(1);
+ expect(spans.some(span => /^(?:BEGIN|COMMIT|ROLLBACK|ABORT|START\s+TRANSACTION)\b/i.test(span.token))).toBe(false);
+ expect(spans.at(-1)?.token).toStartWith("SELECT '{\"fixture\":true}'");
+});
+
+test('eleven-stage compiler rejects source, vector, order and cardinality drift before transport', () => {
+ {
+ const input = fixture();
+ input.materials[0].bytes = Buffer.concat([input.materials[0].bytes, Buffer.from(' ')]);
+ expect(() => compileElevenMigrationBundle(input.bundle, input.materials)).toThrow('MIGRATION_FILE_DIGEST_MISMATCH');
+ }
+ {
+ const input = fixture();
+ input.materials[0].originalVector = [...input.materials[0].originalVector, 'SELECT 1'];
+ expect(() => compileElevenMigrationBundle(input.bundle, input.materials)).toThrow('MIGRATION_VECTOR_MISMATCH');
+ }
+ {
+ const input = fixture();
+ [input.bundle.migrations[0], input.bundle.migrations[1]] = [input.bundle.migrations[1], input.bundle.migrations[0]];
+ [input.materials[0], input.materials[1]] = [input.materials[1], input.materials[0]];
+ expect(() => compileElevenMigrationBundle(input.bundle, input.materials)).toThrow('ORDER_OR_IDENTITY_INVALID');
+ }
+ {
+ const input = fixture();
+ input.bundle.migrations.pop();
+ input.materials.pop();
+ expect(() => compileElevenMigrationBundle(input.bundle, input.materials)).toThrow('MIGRATION_BUNDLE_INVALID');
+ }
+});
+
+test('rollback-only source compiler preserves exact vectors and rejects source identity drift', () => {
+ const input = fixture();
+ const plans = compileElevenMigrationRehearsalSources(input.bundle.migrations, input.materials);
+ expect(plans).toHaveLength(11);
+ expect(plans.map(plan => plan.sourceSha256)).toEqual(manifest.migrations.map((migration: any) => migration.sha256));
+ expect(plans.map(plan => plan.vectorSha256)).toEqual(manifest.migrations.map((migration: any) => migration.statementVectorSha256));
+ {
+ const drifted = fixture();
+ drifted.materials[0].bytes = Buffer.concat([drifted.materials[0].bytes, Buffer.from(' ')]);
+ expect(() => compileElevenMigrationRehearsalSources(
+ drifted.bundle.migrations,
+ drifted.materials,
+ )).toThrow('MIGRATION_FILE_DIGEST_MISMATCH');
+ }
+ {
+ const drifted = fixture();
+ [drifted.bundle.migrations[0], drifted.bundle.migrations[1]] = [
+ drifted.bundle.migrations[1],
+ drifted.bundle.migrations[0],
+ ];
+ [drifted.materials[0], drifted.materials[1]] = [drifted.materials[1], drifted.materials[0]];
+ expect(() => compileElevenMigrationRehearsalSources(
+ drifted.bundle.migrations,
+ drifted.materials,
+ )).toThrow('MIGRATION_BUNDLE_ORDER_OR_IDENTITY_INVALID');
+ }
+});
+
+test('lost acknowledgement reconciles exactly once and never resends the eleven mutations', () => {
+ const input = fixture();
+ const compiled = compileElevenMigrationBundle(input.bundle, input.materials);
+ const calls: string[] = [];
+ expect(() => executeMigrationBundle('fixture-db', compiled, { runPsqlImpl: (_url, sql) => {
+ calls.push(sql);
+ if (calls.length === 1) throw new Error('lost acknowledgement');
+ return JSON.stringify(compiled.finalReadback.expected);
+ } })).toThrow('MIGRATION_BUNDLE_OUTCOME_UNCONFIRMED');
+ expect(calls).toHaveLength(2);
+ expect(calls[0].match(/INSERT INTO supabase_migrations\.schema_migrations/g)).toHaveLength(11);
+ expect(calls[1]).not.toContain('INSERT INTO');
+ expect(bundleReconciliationOutcome(compiled.finalReadback.expected, compiled)).toBe('committed');
+ expect(bundleReconciliationOutcome(compiled.priorReadback.expected, compiled)).toBe('not_applied');
+});
+
+test('post-lock successor expiry survives bounded reconciliation unchanged', () => {
+ const input = fixture();
+ const compiled = compileElevenMigrationBundle(input.bundle, input.materials);
+ const calls: string[] = [];
+ const expired = Object.assign(new Error('provider detail must not escape'), {
+ code: 'SUCCESSOR_ADMISSION_EXPIRED',
+ });
+ expect(() => executeMigrationBundle('fixture-db', compiled, { runPsqlImpl: (_url, sql) => {
+ calls.push(sql);
+ if (calls.length === 1) throw expired;
+ return JSON.stringify(compiled.priorReadback.expected);
+ } })).toThrow('SUCCESSOR_ADMISSION_EXPIRED');
+ expect(calls).toHaveLength(2);
+ expect(calls[0].match(/INSERT INTO supabase_migrations\.schema_migrations/g)).toHaveLength(11);
+ expect(calls[1]).not.toContain('INSERT INTO');
+});
diff --git a/apps/web/tests-unit/review-media-operating-admission.test.ts b/apps/web/tests-unit/review-media-operating-admission.test.ts
new file mode 100644
index 0000000000..413ef82cbc
--- /dev/null
+++ b/apps/web/tests-unit/review-media-operating-admission.test.ts
@@ -0,0 +1,573 @@
+import { afterAll, expect, test } from 'bun:test';
+import { createHash } from 'node:crypto';
+import {
+ chmodSync,
+ existsSync,
+ fsyncSync,
+ mkdtempSync,
+ readFileSync,
+ rmSync,
+ statSync,
+ symlinkSync,
+ unlinkSync,
+ writeFileSync,
+} from 'node:fs';
+import { tmpdir } from 'node:os';
+import { join } from 'node:path';
+
+import {
+ operatingAdmissionFailureResult,
+ parseOperatingAdmissionArguments,
+ prepareReviewMediaApplyAdmission,
+ prepareReviewMediaRehearsalAdmission,
+} from '../scripts/review-media-operating-admission.mjs';
+import {
+ loadSuccessorManifest,
+ validateSuccessorAdmission,
+ validateSuccessorRehearsalAdmission,
+} from '../scripts/review-media-sql-successor.mjs';
+import { elevenMigrationBundleSuffixRoot } from '../scripts/supabase-migration-bundle.mjs';
+
+const manifestRecord = loadSuccessorManifest();
+const { manifest } = manifestRecord;
+const fixedNow = new Date('2026-10-13T08:00:20.000Z');
+const createdAt = '2026-10-13T08:00:00.000Z';
+const expiresAt = '2026-10-13T08:10:00.000Z';
+const observedAt = '2026-10-13T08:00:10.000Z';
+const sourceRevision = '4d4ecf608a7922df5a68beaf3e1ea2dcc250a3b7';
+const roots: string[] = [];
+
+const sha256 = (value: string | Buffer) => createHash('sha256').update(value).digest('hex');
+const canonical = (value: any): any => Array.isArray(value)
+ ? value.map(canonical)
+ : value && typeof value === 'object'
+ ? Object.fromEntries(Object.keys(value).sort().map(key => [key, canonical(value[key])]))
+ : value;
+const canonicalBytes = (value: any) => Buffer.from(`${JSON.stringify(canonical(value))}\n`);
+
+afterAll(() => {
+ for (const root of roots) rmSync(root, { recursive: true, force: true });
+});
+
+function privateRoot() {
+ const root = mkdtempSync(join(tmpdir(), 'tzudong-operating-admission-test-'));
+ roots.push(root);
+ chmodSync(root, 0o700);
+ return root;
+}
+
+function writePrivateCanonical(path: string, value: any) {
+ writeFileSync(path, canonicalBytes(value), { mode: 0o600 });
+ chmodSync(path, 0o600);
+}
+
+function rehearsalInput() {
+ const common = {
+ expiresAt,
+ id: manifest.id,
+ manifestSha256: manifestRecord.manifestSha256,
+ observedAt,
+ projectRef: manifest.projectRef,
+ purpose: manifest.purpose,
+ schemaVersion: 1,
+ sourceRevision,
+ };
+ return {
+ attemptId: '123e4567-e89b-42d3-a456-426614174100',
+ checkoutReadback: {
+ clean: true,
+ detached: true,
+ expiresAt,
+ kind: 'successor-checkout-readback',
+ observedAt,
+ revision: sourceRevision,
+ schemaVersion: 1,
+ },
+ createdAt,
+ expiresAt,
+ kind: 'review-media-successor-rehearsal-admission-input',
+ manifestSha256: manifestRecord.manifestSha256,
+ protectedMainReceipt: {
+ ...common,
+ kind: 'protected-main-readback-receipt',
+ ref: manifest.protectedSource.ref,
+ remote: manifest.protectedSource.remote,
+ repositoryUrl: manifest.protectedSource.repositoryUrl,
+ revision: sourceRevision,
+ },
+ rollbackReadbackReceipt: {
+ ...common,
+ deploymentId: 'dpl_TestOnlyAdmissionEvidence123',
+ deploymentSha: '1884f87e53c4567ead7c9a0fb394b09b91a70396',
+ deploymentUrl: 'https://tzudong-test-only-twoimos-projects.vercel.app/',
+ gitRef: 'main',
+ kind: 'rollback-readback-receipt',
+ productionAliases: ['tzudong.app', 'www.tzudong.app'],
+ projectId: 'prj_sau35J5uUtShIQ9OKofRtOVVnTSl',
+ readyState: 'READY',
+ repository: 'twoimo/tzudong',
+ state: 'ready',
+ target: 'production',
+ teamId: 'team_OUj64KeLxJI3PkEbOaFZnorA',
+ },
+ schemaVersion: 1,
+ sourceReceipt: {
+ ...common,
+ kind: 'successor-source-receipt',
+ sourceRoot: manifest.sourceRoot,
+ },
+ sourceRevision,
+ sourceRoot: manifest.sourceRoot,
+ writerFenceReceipts: Object.fromEntries(Object.entries(manifest.requiredWriterFences).map(
+ ([fence, state]) => [fence, {
+ ...common,
+ fence,
+ kind: 'writer-fence-readback-receipt',
+ state,
+ }],
+ )),
+ };
+}
+
+function prepareRehearsal(input = rehearsalInput()) {
+ const root = privateRoot();
+ const inputPath = join(root, 'rehearsal-input.json');
+ const custodyDirectory = join(root, 'custody');
+ writePrivateCanonical(inputPath, input);
+ const result = prepareReviewMediaRehearsalAdmission({
+ custodyDirectory,
+ inputPath,
+ manifestRecord,
+ now: fixedNow,
+ });
+ return { custodyDirectory: result.custodyDirectory, input, inputPath, result, root };
+}
+
+function stageStates() {
+ const prefixRoot = '1'.repeat(64);
+ return Array.from({ length: 12 }, (_, index) => ({
+ bundle: {
+ ledgerCount: 86 + index,
+ prefixRoot,
+ priorCount: 86,
+ suffixRoot: index === 11 ? elevenMigrationBundleSuffixRoot(manifest.migrations) : null,
+ targetCount: index,
+ },
+ state: {
+ activeReviewItems: 0,
+ currentUser: 'postgres',
+ databaseName: 'postgres',
+ finishCleanupBodySha256: index === 10
+ ? manifest.terminalContract.finishPreimageSha256
+ : index === 11
+ ? manifest.terminalContract.finishPostimageSha256
+ : index === 0 ? null : (index + 2).toString(16).repeat(64),
+ g014Root: (index < 3 ? 'a' : index === 3 ? 'b' : 'c').repeat(64),
+ ledgerRoot: index === 0 ? prefixRoot : (index + 1).toString(16).repeat(64),
+ ownerAuthorityReady: true,
+ reviewItems: 0,
+ reviewPolicyEnabled: false,
+ reviewRuns: 0,
+ schemaRoot: (index + 3).toString(16).repeat(64),
+ serverMajor: 17,
+ sessionUser: 'postgres',
+ storageRoot: (index + 4).toString(16).repeat(64),
+ terminalContractReady: index === 11,
+ },
+ }));
+}
+
+function publishSyntheticRehearsal(custodyDirectory: string) {
+ const admissionPath = join(custodyDirectory, 'rehearsal-admission.json');
+ const admissionBytes = readFileSync(admissionPath);
+ const admission = JSON.parse(admissionBytes.toString('utf8'));
+ const admissionSha256 = sha256(admissionBytes);
+ const stages = stageStates();
+ const rehearsalReceiptPath = join(custodyDirectory, 'rehearsal-receipt.json');
+ const rehearsalJournalPath = join(custodyDirectory, 'rehearsal-journal.jsonl');
+ const receipt = {
+ admissionSha256,
+ attemptId: admission.attemptId,
+ expiresAt: admission.expiresAt,
+ id: admission.id,
+ journalPathSha256: sha256(rehearsalJournalPath),
+ kind: 'successor-rehearsal-receipt',
+ manifestSha256: admission.manifestSha256,
+ observedAt,
+ passed: true,
+ preflightStateSha256: sha256(JSON.stringify(canonical(stages[0]))),
+ projectRef: admission.projectRef,
+ purpose: admission.purpose,
+ restoredStateSha256: sha256(JSON.stringify(canonical(stages[0]))),
+ schemaVersion: 1,
+ serverVersionNum: 170006,
+ sourceRevision: admission.sourceRevision,
+ sourceRoot: admission.sourceRoot,
+ stageStates: stages,
+ stageStatesSha256: sha256(JSON.stringify(canonical(stages))),
+ suffixRoot: elevenMigrationBundleSuffixRoot(manifest.migrations),
+ };
+ const receiptBytes = canonicalBytes(receipt);
+ const receiptSha256 = sha256(receiptBytes);
+ const journal = [
+ {
+ admissionSha256,
+ attemptId: admission.attemptId,
+ event: 'started',
+ manifestSha256: admission.manifestSha256,
+ sourceRevision: admission.sourceRevision,
+ sourceRoot: admission.sourceRoot,
+ startedAt: createdAt,
+ },
+ {
+ admissionSha256,
+ attemptId: admission.attemptId,
+ code: 'SUCCESSOR_REHEARSAL_VERIFIED',
+ event: 'verification',
+ manifestSha256: admission.manifestSha256,
+ receiptSha256,
+ sourceRevision: admission.sourceRevision,
+ sourceRoot: admission.sourceRoot,
+ verifiedAt: observedAt,
+ },
+ {
+ admissionSha256,
+ attemptId: admission.attemptId,
+ code: 'SUCCESSOR_REHEARSAL_PUBLICATION_CONFIRMED',
+ event: 'publication',
+ manifestSha256: admission.manifestSha256,
+ publishedAt: '2026-10-13T08:00:15.000Z',
+ receiptSha256,
+ sourceRevision: admission.sourceRevision,
+ sourceRoot: admission.sourceRoot,
+ },
+ ];
+ const journalBytes = Buffer.from(`${journal.map(entry => JSON.stringify(canonical(entry))).join('\n')}\n`);
+ writeFileSync(rehearsalReceiptPath, receiptBytes, { mode: 0o600 });
+ writeFileSync(rehearsalJournalPath, journalBytes, { mode: 0o600 });
+ chmodSync(rehearsalReceiptPath, 0o600);
+ chmodSync(rehearsalJournalPath, 0o600);
+ return {
+ admission,
+ admissionSha256,
+ journalSha256: sha256(journalBytes),
+ receipt,
+ receiptSha256,
+ };
+}
+
+function applyInput(published: ReturnType, overrides = {}) {
+ return {
+ attemptId: '123e4567-e89b-42d3-a456-426614174101',
+ expiresAt,
+ kind: 'review-media-successor-apply-admission-input',
+ manifestSha256: manifestRecord.manifestSha256,
+ observedAt: '2026-10-13T08:00:20.000Z',
+ rehearsalAdmissionSha256: published.admissionSha256,
+ rehearsalJournalSha256: published.journalSha256,
+ rehearsalReceiptSha256: published.receiptSha256,
+ schemaVersion: 1,
+ sourceRevision,
+ ...overrides,
+ };
+}
+
+test('CLI contract accepts only the explicit rehearsal or apply producer modes', () => {
+ expect(parseOperatingAdmissionArguments([
+ '--mode', 'rehearsal',
+ '--input', '/private/input.json',
+ '--custody-dir', '/private/custody',
+ ])).toEqual({
+ custodyDirectory: '/private/custody',
+ inputPath: '/private/input.json',
+ mode: 'rehearsal',
+ });
+ expect(() => parseOperatingAdmissionArguments([
+ '--mode', 'ready',
+ '--input', '/private/input.json',
+ '--custody-dir', '/private/custody',
+ ])).toThrow('SUCCESSOR_OPERATING_ADMISSION_ARGUMENT_INVALID');
+ const throwingCode = {};
+ Object.defineProperty(throwingCode, 'code', { get() { throw new Error('secret'); } });
+ expect(operatingAdmissionFailureResult(throwingCode)).toEqual({
+ code: 'SUCCESSOR_OPERATING_ADMISSION_INPUT_INVALID',
+ status: 'failed',
+ });
+});
+
+test('rehearsal producer preserves proven receipts and creates a controller-valid private admission', () => {
+ const prepared = prepareRehearsal();
+ const admissionBytes = readFileSync(prepared.result.admissionPath);
+ const admission = JSON.parse(admissionBytes.toString('utf8'));
+ expect(prepared.result.status).toBe('prepared');
+ expect(prepared.result.admissionSha256).toBe(sha256(admissionBytes));
+ expect(statSync(prepared.custodyDirectory).mode & 0o077).toBe(0);
+ expect(statSync(prepared.result.admissionPath).mode & 0o077).toBe(0);
+ expect(readFileSync(join(prepared.custodyDirectory, 'source-receipt.json')))
+ .toEqual(canonicalBytes(prepared.input.sourceReceipt));
+ expect(readFileSync(join(prepared.custodyDirectory, 'checkout-readback.json')))
+ .toEqual(canonicalBytes(prepared.input.checkoutReadback));
+ expect(existsSync(prepared.result.journalPath)).toBe(false);
+ expect(existsSync(prepared.result.receiptPath)).toBe(false);
+ expect(validateSuccessorRehearsalAdmission(admission, manifestRecord, {
+ now: fixedNow,
+ receiptDirectory: prepared.custodyDirectory,
+ })).toEqual({
+ createdAt: Date.parse(createdAt),
+ expiresAt: Date.parse(expiresAt),
+ });
+});
+
+test('unknown, missing, stale, and mismatched evidence is rejected before custody creation', () => {
+ const cases = [
+ (input: any) => { input.writerFenceReceipts.unknownFence = input.writerFenceReceipts.dashboard; },
+ (input: any) => { delete input.writerFenceReceipts.dashboard; },
+ (input: any) => { input.sourceReceipt.observedAt = '2026-10-13T07:59:59.999Z'; },
+ (input: any) => { input.protectedMainReceipt.revision = 'a'.repeat(40); },
+ (input: any) => { input.expiresAt = fixedNow.toISOString(); },
+ (input: any) => { input.expiresAt = '2026-10-13T08:16:00.000Z'; },
+ ];
+ for (const mutate of cases) {
+ const root = privateRoot();
+ const input = rehearsalInput();
+ mutate(input);
+ const inputPath = join(root, 'input.json');
+ const custodyDirectory = join(root, 'custody');
+ writePrivateCanonical(inputPath, input);
+ expect(() => prepareReviewMediaRehearsalAdmission({
+ custodyDirectory,
+ inputPath,
+ manifestRecord,
+ now: fixedNow,
+ })).toThrow('SUCCESSOR_OPERATING_ADMISSION_EVIDENCE_INVALID');
+ expect(existsSync(custodyDirectory)).toBe(false);
+ }
+});
+
+test('rehearsal producer never overwrites an existing custody directory', () => {
+ const prepared = prepareRehearsal();
+ const before = sha256(readFileSync(prepared.result.admissionPath));
+ expect(() => prepareReviewMediaRehearsalAdmission({
+ custodyDirectory: prepared.custodyDirectory,
+ inputPath: prepared.inputPath,
+ manifestRecord,
+ now: fixedNow,
+ })).toThrow('SUCCESSOR_OPERATING_ADMISSION_OUTPUT_EXISTS');
+ expect(sha256(readFileSync(prepared.result.admissionPath))).toBe(before);
+});
+
+test('rehearsal producer fails closed on parent, file, and custody fsync failures', () => {
+ for (const failAt of [1, 2, 14]) {
+ const root = privateRoot();
+ const inputPath = join(root, `input-${failAt}.json`);
+ const custodyDirectory = join(root, `custody-${failAt}`);
+ writePrivateCanonical(inputPath, rehearsalInput());
+ let fsyncCalls = 0;
+ expect(() => prepareReviewMediaRehearsalAdmission({
+ custodyDirectory,
+ fsyncImpl: (descriptor: number) => {
+ fsyncCalls += 1;
+ if (fsyncCalls === failAt) throw new Error('fixture-fsync-failure');
+ fsyncSync(descriptor);
+ },
+ inputPath,
+ manifestRecord,
+ now: fixedNow,
+ })).toThrow('SUCCESSOR_OPERATING_ADMISSION_WRITE_FAILED');
+ expect(fsyncCalls).toBe(failAt);
+ }
+});
+
+test('rehearsal producer rejects symlink and oversized inputs before custody creation', () => {
+ const root = privateRoot();
+ const targetPath = join(root, 'target.json');
+ const symlinkPath = join(root, 'symlink.json');
+ const oversizedPath = join(root, 'oversized.json');
+ writePrivateCanonical(targetPath, rehearsalInput());
+ symlinkSync(targetPath, symlinkPath);
+ writeFileSync(oversizedPath, Buffer.alloc(1024 * 1024 + 1, 0x20), { mode: 0o600 });
+ chmodSync(oversizedPath, 0o600);
+ for (const [inputPath, name] of [[symlinkPath, 'symlink'], [oversizedPath, 'oversized']]) {
+ const custodyDirectory = join(root, `custody-${name}`);
+ expect(() => prepareReviewMediaRehearsalAdmission({
+ custodyDirectory,
+ inputPath,
+ manifestRecord,
+ now: fixedNow,
+ })).toThrow('SUCCESSOR_OPERATING_ADMISSION_INPUT_INVALID');
+ expect(existsSync(custodyDirectory)).toBe(false);
+ }
+});
+
+test('apply producer binds the verified rehearsal artifacts without extending their time window', () => {
+ const prepared = prepareRehearsal();
+ const published = publishSyntheticRehearsal(prepared.custodyDirectory);
+ const applyInputDocument = applyInput(published);
+ const inputPath = join(prepared.root, 'apply-input.json');
+ writePrivateCanonical(inputPath, applyInputDocument);
+ expect(published.receipt.admissionSha256).toBe(published.admissionSha256);
+ expect(published.receipt.journalPathSha256)
+ .toBe(sha256(join(prepared.custodyDirectory, 'rehearsal-journal.jsonl')));
+ expect(published.receipt.sourceRoot).toBe(manifest.sourceRoot);
+ expect({
+ id: published.receipt.id,
+ kind: published.receipt.kind,
+ manifestSha256: published.receipt.manifestSha256,
+ passed: published.receipt.passed,
+ projectRef: published.receipt.projectRef,
+ purpose: published.receipt.purpose,
+ schemaVersion: published.receipt.schemaVersion,
+ serverMajor: Math.floor(published.receipt.serverVersionNum / 10_000),
+ sourceRevision: published.receipt.sourceRevision,
+ }).toEqual({
+ id: manifest.id,
+ kind: 'successor-rehearsal-receipt',
+ manifestSha256: manifestRecord.manifestSha256,
+ passed: true,
+ projectRef: manifest.projectRef,
+ purpose: manifest.purpose,
+ schemaVersion: 1,
+ serverMajor: manifest.serverMajor,
+ sourceRevision,
+ });
+ expect(published.receipt.suffixRoot).toBe(elevenMigrationBundleSuffixRoot(manifest.migrations));
+ expect(published.receipt.stageStatesSha256)
+ .toBe(sha256(JSON.stringify(canonical(published.receipt.stageStates))));
+ expect(published.receipt.preflightStateSha256)
+ .toBe(sha256(JSON.stringify(canonical(published.receipt.stageStates[0]))));
+ const result = prepareReviewMediaApplyAdmission({
+ custodyDirectory: prepared.custodyDirectory,
+ inputPath,
+ manifestRecord,
+ now: fixedNow,
+ });
+ const admission = JSON.parse(readFileSync(result.admissionPath, 'utf8'));
+ expect(published.receipt.attemptId).toBe(published.admission.attemptId);
+ expect(admission.attemptId).toBe(applyInputDocument.attemptId);
+ expect(admission.attemptId).not.toBe(published.admission.attemptId);
+ expect(admission.rehearsalAdmissionSha256).toBe(sha256(readFileSync(
+ join(prepared.custodyDirectory, 'rehearsal-admission.json'),
+ )));
+ expect(admission.rehearsalAdmissionSha256).toBe(published.receipt.admissionSha256);
+ expect(admission.createdAt).toBe(createdAt);
+ expect(admission.expiresAt).toBe(expiresAt);
+ expect(admission.stageStates).toEqual(published.receipt.stageStates);
+ expect(existsSync(result.journalPath)).toBe(false);
+ expect(validateSuccessorAdmission(admission, manifestRecord, {
+ now: fixedNow,
+ receiptDirectory: prepared.custodyDirectory,
+ }).suffixRoot).toBe(elevenMigrationBundleSuffixRoot(manifest.migrations));
+ expect(() => prepareReviewMediaApplyAdmission({
+ custodyDirectory: prepared.custodyDirectory,
+ inputPath,
+ manifestRecord,
+ now: fixedNow,
+ })).toThrow('SUCCESSOR_OPERATING_ADMISSION_OUTPUT_EXISTS');
+});
+
+test('apply producer rejects a relabeled or tampered rehearsal before writing an admission', () => {
+ for (const [name, overrides] of [
+ ['extended', { expiresAt: '2026-10-13T08:11:00.000Z' }],
+ ['tampered', { rehearsalJournalSha256: 'f'.repeat(64) }],
+ ] as const) {
+ const prepared = prepareRehearsal();
+ const published = publishSyntheticRehearsal(prepared.custodyDirectory);
+ const inputPath = join(prepared.root, `apply-input-${name}.json`);
+ writePrivateCanonical(inputPath, applyInput(published, overrides));
+ expect(() => prepareReviewMediaApplyAdmission({
+ custodyDirectory: prepared.custodyDirectory,
+ inputPath,
+ manifestRecord,
+ now: fixedNow,
+ })).toThrow('SUCCESSOR_OPERATING_ADMISSION_EVIDENCE_INVALID');
+ expect(existsSync(join(prepared.custodyDirectory, 'apply-admission.json'))).toBe(false);
+ }
+});
+
+test('apply producer rejects a fully rehashed receipt and journal relabeled to another rehearsal attempt', () => {
+ const prepared = prepareRehearsal();
+ const published = publishSyntheticRehearsal(prepared.custodyDirectory);
+ const relabeledAttemptId = '123e4567-e89b-42d3-a456-426614174102';
+ const receiptPath = join(prepared.custodyDirectory, 'rehearsal-receipt.json');
+ const journalPath = join(prepared.custodyDirectory, 'rehearsal-journal.jsonl');
+ const relabeledReceipt = {
+ ...published.receipt,
+ attemptId: relabeledAttemptId,
+ };
+ const relabeledReceiptBytes = canonicalBytes(relabeledReceipt);
+ const relabeledReceiptSha256 = sha256(relabeledReceiptBytes);
+ const relabeledJournal = readFileSync(journalPath, 'utf8').trimEnd().split('\n').map(line => {
+ const entry = JSON.parse(line);
+ return {
+ ...entry,
+ attemptId: relabeledAttemptId,
+ ...(entry.receiptSha256 ? { receiptSha256: relabeledReceiptSha256 } : {}),
+ };
+ });
+ const relabeledJournalBytes = Buffer.from(
+ `${relabeledJournal.map(entry => JSON.stringify(canonical(entry))).join('\n')}\n`,
+ );
+ writeFileSync(receiptPath, relabeledReceiptBytes, { mode: 0o600 });
+ writeFileSync(journalPath, relabeledJournalBytes, { mode: 0o600 });
+ chmodSync(receiptPath, 0o600);
+ chmodSync(journalPath, 0o600);
+
+ const inputPath = join(prepared.root, 'apply-input-relabeled-attempt.json');
+ writePrivateCanonical(inputPath, applyInput(published, {
+ rehearsalJournalSha256: sha256(relabeledJournalBytes),
+ rehearsalReceiptSha256: relabeledReceiptSha256,
+ }));
+
+ expect(() => prepareReviewMediaApplyAdmission({
+ custodyDirectory: prepared.custodyDirectory,
+ inputPath,
+ manifestRecord,
+ now: fixedNow,
+ })).toThrow('SUCCESSOR_OPERATING_ADMISSION_EVIDENCE_INVALID');
+ expect(existsSync(join(prepared.custodyDirectory, 'apply-admission.json'))).toBe(false);
+});
+
+test('apply producer treats a dangling reserved journal symlink as an existing output', () => {
+ const prepared = prepareRehearsal();
+ const published = publishSyntheticRehearsal(prepared.custodyDirectory);
+ const inputPath = join(prepared.root, 'apply-input.json');
+ writePrivateCanonical(inputPath, applyInput(published));
+ symlinkSync(join(prepared.root, 'missing-target'), join(prepared.custodyDirectory, 'apply-journal.jsonl'));
+ expect(() => prepareReviewMediaApplyAdmission({
+ custodyDirectory: prepared.custodyDirectory,
+ inputPath,
+ manifestRecord,
+ now: fixedNow,
+ })).toThrow('SUCCESSOR_OPERATING_ADMISSION_OUTPUT_EXISTS');
+ expect(existsSync(join(prepared.custodyDirectory, 'apply-admission.json'))).toBe(false);
+});
+
+test('apply producer reads rehearsal journals through the no-follow bounded reader', () => {
+ for (const mode of ['symlink', 'oversized'] as const) {
+ const prepared = prepareRehearsal();
+ const published = publishSyntheticRehearsal(prepared.custodyDirectory);
+ const journalPath = join(prepared.custodyDirectory, 'rehearsal-journal.jsonl');
+ let journalSha256 = published.journalSha256;
+ if (mode === 'symlink') {
+ const targetPath = join(prepared.root, 'journal-target.jsonl');
+ writeFileSync(targetPath, readFileSync(journalPath), { mode: 0o600 });
+ chmodSync(targetPath, 0o600);
+ unlinkSync(journalPath);
+ symlinkSync(targetPath, journalPath);
+ } else {
+ const oversized = Buffer.alloc(1024 * 1024 + 1, 0x20);
+ writeFileSync(journalPath, oversized, { mode: 0o600 });
+ chmodSync(journalPath, 0o600);
+ journalSha256 = sha256(oversized);
+ }
+ const inputPath = join(prepared.root, `apply-input-${mode}.json`);
+ writePrivateCanonical(inputPath, applyInput(published, { rehearsalJournalSha256: journalSha256 }));
+ expect(() => prepareReviewMediaApplyAdmission({
+ custodyDirectory: prepared.custodyDirectory,
+ inputPath,
+ manifestRecord,
+ now: fixedNow,
+ })).toThrow('SUCCESSOR_OPERATING_ADMISSION_EVIDENCE_INVALID');
+ expect(existsSync(join(prepared.custodyDirectory, 'apply-admission.json'))).toBe(false);
+ }
+});
diff --git a/apps/web/tests-unit/review-media-sql-rehearsal-verifier.test.ts b/apps/web/tests-unit/review-media-sql-rehearsal-verifier.test.ts
new file mode 100644
index 0000000000..e27f2446f9
--- /dev/null
+++ b/apps/web/tests-unit/review-media-sql-rehearsal-verifier.test.ts
@@ -0,0 +1,481 @@
+import { expect, test } from 'bun:test';
+import { createHash } from 'node:crypto';
+import { mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
+import { tmpdir } from 'node:os';
+import { basename, join, resolve } from 'node:path';
+
+import {
+ createOwnedContainer,
+ DEFAULT_RETAINED_PROOF_SELECTOR_PATH,
+ injectFinalApplyExpiryFault,
+ inspectRetainedProof,
+ parseArguments,
+ publishCurrentProof,
+ removeOwnedContainer,
+} from '../performance/review-db-rehearsal-20261012/verify-review-media-sql-rehearsal.mjs';
+
+const sha256 = (value: string | Buffer) => createHash('sha256').update(value).digest('hex');
+
+type Ownership = {
+ attempted?: boolean;
+ created: boolean;
+ id: string | null;
+ name: string | null;
+ token: string | null;
+ removed?: boolean;
+ creationAcknowledged?: boolean;
+ creationReconciled?: boolean;
+ removalAcknowledged?: boolean;
+};
+
+type DockerOptions = {
+ ackLostOnCreate?: boolean;
+ ackMismatchOnCreate?: boolean;
+ failBeforeCreate?: boolean;
+ wrongOwnerLabel?: boolean;
+ keepAfterRemoveFailure?: boolean;
+ ackLostOnRemove?: boolean;
+ transientInspectFailures?: number;
+};
+
+function dockerFixture(options: DockerOptions = {}) {
+ const containers = new Map();
+ const calls: string[][] = [];
+ const id = 'a'.repeat(64);
+ let transientInspectFailures = options.transientInspectFailures ?? 0;
+ const dockerImpl = (args: string[]) => {
+ calls.push([...args]);
+ if (args[0] === 'run') {
+ if (options.failBeforeCreate) {
+ return { status: 1, stdout: '', stderr: 'daemon unavailable' };
+ }
+ const name = args[args.indexOf('--name') + 1];
+ const label = args[args.indexOf('--label') + 1];
+ const token = label.slice(label.indexOf('=') + 1);
+ containers.set(id, {
+ id,
+ name,
+ token: options.wrongOwnerLabel ? '0'.repeat(32) : token,
+ });
+ if (options.ackLostOnCreate) {
+ return { status: 1, stdout: '', stderr: 'response lost after create' };
+ }
+ return {
+ status: 0,
+ stdout: `${options.ackMismatchOnCreate ? 'b'.repeat(64) : id}\n`,
+ stderr: '',
+ };
+ }
+ if (args[0] === 'container' && args[1] === 'inspect') {
+ if (transientInspectFailures > 0) {
+ transientInspectFailures -= 1;
+ return { status: 1, stdout: '', stderr: 'temporary daemon read failure' };
+ }
+ const target = args[2];
+ const value = [...containers.values()].find(container => (
+ container.id === target || container.name === target
+ ));
+ if (!value) return { status: 1, stdout: '', stderr: `No such container: ${target}` };
+ return {
+ status: 0,
+ stdout: JSON.stringify([{
+ Id: value.id,
+ Name: `/${value.name}`,
+ Config: { Labels: { 'tzudong.review-rehearsal.owner': value.token } },
+ }]),
+ stderr: '',
+ };
+ }
+ if (args[0] === 'container' && args[1] === 'rm' && args[2] === '--force') {
+ const target = args[3];
+ if (options.keepAfterRemoveFailure) {
+ return { status: 1, stdout: '', stderr: 'response failed before remove' };
+ }
+ containers.delete(target);
+ if (options.ackLostOnRemove) {
+ return { status: 1, stdout: '', stderr: 'response lost after remove' };
+ }
+ return { status: 0, stdout: `${target}\n`, stderr: '' };
+ }
+ throw new Error(`unexpected docker call: ${args.join(' ')}`);
+ };
+ return { calls, containers, dockerImpl, id };
+}
+
+function freshOwnership(): Ownership {
+ return { created: false, id: null, name: null, token: null };
+}
+
+test('default retained proof uses a stable current-proof selector', () => {
+ expect(basename(DEFAULT_RETAINED_PROOF_SELECTOR_PATH)).toBe('retained-current-proof.json');
+ const selector = JSON.parse(readFileSync(DEFAULT_RETAINED_PROOF_SELECTOR_PATH, 'utf8'));
+ expect(selector).toMatchObject({
+ schemaVersion: 1,
+ kind: 'review-media-sql-retained-current-proof',
+ });
+ const retained = inspectRetainedProof();
+ expect(basename(retained.repositoryPath)).toBe(selector.proofPath);
+ expect(retained.actualSha256).toBe(selector.proofSha256);
+ expect(retained.usedAsCurrentProof).toBe(false);
+ expect(parseArguments([])).toEqual({ outputPath: null });
+});
+
+test('selector rejects unauthenticated bytes, path escape, and proof-SHA relabelling', () => {
+ const directory = mkdtempSync(join(tmpdir(), 'tzudong-proof-selector-'));
+ try {
+ const proofPath = join(directory, 'proof.json');
+ const selectorPath = join(directory, 'retained-current-proof.json');
+ const checksumsPath = join(directory, 'SHA256SUMS');
+ const checksumsDigestPath = join(directory, 'SHA256SUMS.sha256');
+ const proof = Buffer.from('{}\n');
+ writeFileSync(proofPath, proof);
+ const writeAuthenticatedMap = (selector: Buffer) => {
+ writeFileSync(selectorPath, selector);
+ const checksums = Buffer.from([
+ `${sha256(proof)} proof.json`,
+ `${sha256(selector)} retained-current-proof.json`,
+ '',
+ ].join('\n'));
+ writeFileSync(checksumsPath, checksums);
+ writeFileSync(checksumsDigestPath, `${sha256(checksums)}\n`);
+ };
+
+ const escaped = Buffer.from(`${JSON.stringify({
+ schemaVersion: 1,
+ kind: 'review-media-sql-retained-current-proof',
+ proofPath: '../proof.json',
+ proofSha256: sha256(proof),
+ })}\n`);
+ writeAuthenticatedMap(escaped);
+ expect(() => inspectRetainedProof({
+ selectorPath,
+ checksumsPath,
+ checksumsDigestPath,
+ repositoryRoot: directory,
+ })).toThrow('LOCAL_REHEARSAL_SELECTOR_INVALID');
+
+ const relabelled = Buffer.from(`${JSON.stringify({
+ schemaVersion: 1,
+ kind: 'review-media-sql-retained-current-proof',
+ proofPath: 'proof.json',
+ proofSha256: '0'.repeat(64),
+ })}\n`);
+ writeAuthenticatedMap(relabelled);
+ expect(() => inspectRetainedProof({
+ selectorPath,
+ checksumsPath,
+ checksumsDigestPath,
+ repositoryRoot: directory,
+ })).toThrow('LOCAL_REHEARSAL_SELECTOR_PROOF_SHA256_MISMATCH');
+
+ const valid = Buffer.from(`${JSON.stringify({
+ schemaVersion: 1,
+ kind: 'review-media-sql-retained-current-proof',
+ proofPath: 'proof.json',
+ proofSha256: sha256(proof),
+ })}\n`);
+ writeAuthenticatedMap(valid);
+ writeFileSync(selectorPath, Buffer.concat([valid, Buffer.from(' ')]));
+ expect(() => inspectRetainedProof({
+ selectorPath,
+ checksumsPath,
+ checksumsDigestPath,
+ repositoryRoot: directory,
+ })).toThrow('LOCAL_REHEARSAL_SELECTOR_SHA256_MISMATCH');
+ } finally {
+ rmSync(directory, { recursive: true, force: true });
+ }
+});
+
+test('retained proof verifies the complete non-circular artifact map from one byte snapshot', () => {
+ const directory = mkdtempSync(join(tmpdir(), 'tzudong-current-proof-map-'));
+ try {
+ const outputPath = join(directory, 'pg17-terminal-boundaries-20261013.json');
+ const selectorPath = join(directory, 'retained-current-proof.json');
+ const dependencyPath = join(directory, 'source.mjs');
+ const checksumsPath = join(directory, 'SHA256SUMS');
+ const checksumsDigestPath = join(directory, 'SHA256SUMS.sha256');
+ const proof = Buffer.from(`${JSON.stringify({
+ manifestSha256: '1'.repeat(64),
+ rehearsalHarnessSha256: '2'.repeat(64),
+ sourceRoot: '3'.repeat(64),
+ })}\n`);
+ const dependency = Buffer.from('export const current = true;\n');
+ const selector = Buffer.from(`${JSON.stringify({
+ schemaVersion: 1,
+ kind: 'review-media-sql-retained-current-proof',
+ proofPath: 'pg17-terminal-boundaries-20261013.json',
+ proofSha256: sha256(proof),
+ })}\n`);
+ writeFileSync(outputPath, proof);
+ writeFileSync(selectorPath, selector);
+ writeFileSync(dependencyPath, dependency);
+ const checksums = Buffer.from([
+ `${sha256(proof)} pg17-terminal-boundaries-20261013.json`,
+ `${sha256(selector)} retained-current-proof.json`,
+ `${sha256(dependency)} source.mjs`,
+ '',
+ ].join('\n'));
+ writeFileSync(checksumsPath, checksums);
+ writeFileSync(checksumsDigestPath, `${sha256(checksums)}\n`);
+
+ const retained = inspectRetainedProof({
+ selectorPath,
+ checksumsPath,
+ checksumsDigestPath,
+ repositoryRoot: directory,
+ bindings: {
+ manifestSha256: '1'.repeat(64),
+ rehearsalHarnessSha256: '2'.repeat(64),
+ sourceRoot: '3'.repeat(64),
+ },
+ });
+ expect(retained).toMatchObject({
+ present: true,
+ classification: 'retained-current-source-evidence',
+ repositoryPath: 'pg17-terminal-boundaries-20261013.json',
+ selector: {
+ repositoryPath: 'retained-current-proof.json',
+ sha256: sha256(selector),
+ proofPath: 'pg17-terminal-boundaries-20261013.json',
+ proofSha256: sha256(proof),
+ },
+ expectedSha256: sha256(proof),
+ actualSha256: sha256(proof),
+ currentSourceMatch: true,
+ usedAsCurrentProof: false,
+ });
+
+ writeFileSync(dependencyPath, 'tampered\n');
+ expect(() => inspectRetainedProof({
+ selectorPath,
+ checksumsPath,
+ checksumsDigestPath,
+ repositoryRoot: directory,
+ })).toThrow('LOCAL_REHEARSAL_ARTIFACT_SHA256_MISMATCH');
+
+ rmSync(dependencyPath);
+ expect(() => inspectRetainedProof({
+ selectorPath,
+ checksumsPath,
+ checksumsDigestPath,
+ repositoryRoot: directory,
+ })).toThrow('LOCAL_REHEARSAL_ARTIFACT_MISSING');
+
+ rmSync(outputPath);
+ expect(() => inspectRetainedProof({
+ selectorPath,
+ checksumsPath,
+ checksumsDigestPath,
+ repositoryRoot: directory,
+ required: true,
+ })).toThrow('LOCAL_REHEARSAL_CURRENT_PROOF_MISSING');
+ } finally {
+ rmSync(directory, { recursive: true, force: true });
+ }
+});
+
+test('retained rollback proof rejects bytes not authorized by SHA256SUMS', () => {
+ const directory = mkdtempSync(join(tmpdir(), 'tzudong-rollback-proof-'));
+ try {
+ const outputPath = join(directory, 'pg17-rollback-rehearsal.json');
+ const checksumsPath = join(directory, 'SHA256SUMS');
+ const proof = Buffer.from(`${JSON.stringify({
+ manifestSha256: '1'.repeat(64),
+ rehearsalHarnessSha256: '2'.repeat(64),
+ sourceRoot: '3'.repeat(64),
+ })}\n`);
+ writeFileSync(outputPath, proof);
+ writeFileSync(checksumsPath, `${sha256(proof)} pg17-rollback-rehearsal.json\n`);
+ writeFileSync(outputPath, Buffer.concat([proof, Buffer.from(' ')]));
+
+ expect(() => inspectRetainedProof({
+ outputPath,
+ checksumsPath,
+ checksumsDigestPath: null,
+ repositoryRoot: directory,
+ bindings: {
+ manifestSha256: '1'.repeat(64),
+ rehearsalHarnessSha256: '2'.repeat(64),
+ sourceRoot: '3'.repeat(64),
+ },
+ })).toThrow('LOCAL_REHEARSAL_RETAINED_SHA256_MISMATCH');
+ } finally {
+ rmSync(directory, { recursive: true, force: true });
+ }
+});
+
+test('explicit current output is exclusive and separate from retained proof', () => {
+ const outputPath = resolve('/tmp/tzudong-current-rehearsal-proof.json');
+ const writes: unknown[][] = [];
+ const stdout: string[] = [];
+ publishCurrentProof(
+ { retainedProof: { present: true }, proof: { status: 'passed' }, outputPath },
+ {
+ writeFileImpl: (...args: unknown[]) => { writes.push(args); },
+ writeStdoutImpl: (value: string) => { stdout.push(value); },
+ },
+ );
+ expect(writes).toHaveLength(1);
+ expect(writes[0][0]).toBe(outputPath);
+ expect(writes[0][2]).toEqual({ flag: 'wx', mode: 0o600 });
+ expect(JSON.parse(stdout[0]).currentOutputPath).toBe(outputPath);
+});
+
+test('apply expiry fault changes only the final wrapper guard after the exported marker', () => {
+ const expiresAt = '9999-12-31T23:59:59.999Z';
+ const marker = '-- successor-pre-commit-expiry-guard';
+ const before = `DO $stage$ BEGIN IF clock_timestamp() >= '${expiresAt}'::timestamptz THEN NULL; END IF; END $stage$;\n`;
+ const final = `DO $final$ BEGIN IF clock_timestamp() >= '${expiresAt}'::timestamptz THEN RAISE EXCEPTION 'SUCCESSOR_ADMISSION_EXPIRED'; END IF; END $final$;\n`;
+ const source = `${before}${marker}\n${final}`;
+ const fault = injectFinalApplyExpiryFault(source, expiresAt);
+ expect(fault).toMatchObject({
+ marker,
+ originalDeadline: expiresAt,
+ injectedDeadline: '1970-01-01T00:00:00.000Z',
+ wrapperOnlyFaultInjection: true,
+ });
+ expect(fault.sql.slice(0, fault.sql.indexOf(marker))).toBe(before);
+ expect(fault.sql.slice(fault.sql.indexOf(marker))).toContain("'1970-01-01T00:00:00.000Z'::timestamptz");
+ expect(fault.sql.match(new RegExp(expiresAt.replaceAll('.', '\\.'), 'g'))).toHaveLength(1);
+ expect(() => injectFinalApplyExpiryFault(before, expiresAt))
+ .toThrow('LOCAL_REHEARSAL_APPLY_EXPIRY_MARKER_INVALID');
+});
+
+test('container create acknowledgement loss is reconciled by unique name and owner label', () => {
+ const fixture = dockerFixture({ ackLostOnCreate: true });
+ const ownership = freshOwnership();
+ const createdId = createOwnedContainer(ownership, {
+ dockerImpl: fixture.dockerImpl,
+ generateToken: () => '1'.repeat(32),
+ processId: 4242,
+ });
+
+ expect(createdId).toBe(fixture.id);
+ expect(ownership).toMatchObject({
+ created: true,
+ id: fixture.id,
+ name: 'tzudong-review-rollback-rehearsal-4242-111111111111',
+ token: '1'.repeat(32),
+ creationAcknowledged: false,
+ creationReconciled: true,
+ });
+ const run = fixture.calls.find(args => args[0] === 'run');
+ expect(run).toContain(`tzudong.review-rehearsal.owner=${'1'.repeat(32)}`);
+});
+
+test('first inspect failure after uncertain create is reconciled and cleaned by exact owned ID', () => {
+ const fixture = dockerFixture({ ackLostOnCreate: true, transientInspectFailures: 1 });
+ const ownership = freshOwnership();
+ expect(() => createOwnedContainer(ownership, {
+ dockerImpl: fixture.dockerImpl,
+ generateToken: () => '9'.repeat(32),
+ processId: 4248,
+ })).toThrow('LOCAL_REHEARSAL_CONTAINER_INSPECTION_FAILED');
+ expect(ownership).toMatchObject({
+ attempted: true,
+ created: false,
+ id: null,
+ name: 'tzudong-review-rollback-rehearsal-4248-999999999999',
+ token: '9'.repeat(32),
+ });
+
+ expect(removeOwnedContainer(ownership, { dockerImpl: fixture.dockerImpl })).toBe(true);
+ expect(ownership).toMatchObject({ created: false, removed: true, id: fixture.id });
+ expect(fixture.calls.filter(args => args[0] === 'run')).toHaveLength(1);
+ expect(fixture.calls.find(args => args[0] === 'container' && args[1] === 'rm'))
+ .toEqual(['container', 'rm', '--force', fixture.id]);
+ expect(fixture.containers.size).toBe(0);
+});
+
+test('same-process rehearsal runs derive distinct container names from their owner tokens', () => {
+ const firstFixture = dockerFixture();
+ const secondFixture = dockerFixture();
+ const first = freshOwnership();
+ const second = freshOwnership();
+ createOwnedContainer(first, {
+ dockerImpl: firstFixture.dockerImpl,
+ generateToken: () => '7'.repeat(32),
+ processId: 4242,
+ });
+ createOwnedContainer(second, {
+ dockerImpl: secondFixture.dockerImpl,
+ generateToken: () => '8'.repeat(32),
+ processId: 4242,
+ });
+ expect(first.name).not.toBe(second.name);
+ expect(first.token).not.toBe(second.token);
+});
+
+test('create failure reconciles proven absence without issuing a removal', () => {
+ const fixture = dockerFixture({ failBeforeCreate: true });
+ const ownership = freshOwnership();
+ expect(() => createOwnedContainer(ownership, {
+ dockerImpl: fixture.dockerImpl,
+ generateToken: () => '2'.repeat(32),
+ processId: 4243,
+ })).toThrow('LOCAL_REHEARSAL_CONTAINER_CREATION_UNCONFIRMED');
+ expect(removeOwnedContainer(ownership, { dockerImpl: fixture.dockerImpl })).toBe(true);
+ expect(ownership).toMatchObject({ created: false, removed: true });
+ expect(fixture.calls.some(args => args[0] === 'container' && args[1] === 'rm')).toBe(false);
+});
+
+test('owner-label mismatch is rejected and the unowned container is not removed', () => {
+ const fixture = dockerFixture({ wrongOwnerLabel: true });
+ const ownership = freshOwnership();
+ expect(() => createOwnedContainer(ownership, {
+ dockerImpl: fixture.dockerImpl,
+ generateToken: () => '3'.repeat(32),
+ processId: 4244,
+ })).toThrow('LOCAL_REHEARSAL_CONTAINER_OWNERSHIP_UNPROVEN');
+ expect(removeOwnedContainer(ownership, { dockerImpl: fixture.dockerImpl })).toBe(false);
+ expect(fixture.containers.size).toBe(1);
+ expect(fixture.calls.some(args => args[0] === 'container' && args[1] === 'rm')).toBe(false);
+});
+
+test('acknowledgement mismatch retains exact owned ID for safe finally cleanup', () => {
+ const fixture = dockerFixture({ ackMismatchOnCreate: true });
+ const ownership = freshOwnership();
+ expect(() => createOwnedContainer(ownership, {
+ dockerImpl: fixture.dockerImpl,
+ generateToken: () => '4'.repeat(32),
+ processId: 4245,
+ })).toThrow('LOCAL_REHEARSAL_CONTAINER_ACK_MISMATCH');
+ expect(ownership).toMatchObject({ created: true, id: fixture.id, token: '4'.repeat(32) });
+ expect(removeOwnedContainer(ownership, { dockerImpl: fixture.dockerImpl })).toBe(true);
+ const removal = fixture.calls.find(args => args[0] === 'container' && args[1] === 'rm');
+ expect(removal).toEqual(['container', 'rm', '--force', fixture.id]);
+ expect(fixture.containers.size).toBe(0);
+});
+
+test('cleanup failure leaves ownership live and cannot be misreported as removed', () => {
+ const fixture = dockerFixture({ keepAfterRemoveFailure: true });
+ const ownership = freshOwnership();
+ createOwnedContainer(ownership, {
+ dockerImpl: fixture.dockerImpl,
+ generateToken: () => '5'.repeat(32),
+ processId: 4246,
+ });
+ expect(() => removeOwnedContainer(ownership, { dockerImpl: fixture.dockerImpl }))
+ .toThrow('LOCAL_REHEARSAL_CONTAINER_REMOVAL_UNCONFIRMED');
+ expect(ownership.created).toBe(true);
+ expect(ownership.removed).not.toBe(true);
+ expect(fixture.containers.size).toBe(1);
+});
+
+test('cleanup acknowledgement loss succeeds only after exact-ID absence readback', () => {
+ const fixture = dockerFixture({ ackLostOnRemove: true });
+ const ownership = freshOwnership();
+ createOwnedContainer(ownership, {
+ dockerImpl: fixture.dockerImpl,
+ generateToken: () => '6'.repeat(32),
+ processId: 4247,
+ });
+ expect(removeOwnedContainer(ownership, { dockerImpl: fixture.dockerImpl })).toBe(true);
+ expect(ownership).toMatchObject({
+ created: false,
+ removed: true,
+ removalAcknowledged: false,
+ });
+ expect(fixture.containers.size).toBe(0);
+});
diff --git a/apps/web/tests-unit/review-media-sql-successor-harness.test.ts b/apps/web/tests-unit/review-media-sql-successor-harness.test.ts
new file mode 100644
index 0000000000..c8c87aa837
--- /dev/null
+++ b/apps/web/tests-unit/review-media-sql-successor-harness.test.ts
@@ -0,0 +1,711 @@
+import { expect, test } from 'bun:test';
+import { createHash } from 'node:crypto';
+import { mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
+import { tmpdir } from 'node:os';
+import { join } from 'node:path';
+
+import {
+ applySuccessorPlanOnce,
+ createFixtureMembership,
+ createOwnedDatabase,
+ dropFixtureMembership,
+ dropOwnedDatabase,
+ inspectRetainedProof,
+ waitForAdvisoryContention,
+} from '../performance/review-db-successor-20261012/verify-review-media-sql-successor.mjs';
+
+const retainedProofRepositoryPath = 'apps/web/performance/review-db-successor-20261012/pg17-local-replay.json';
+const sha256 = (value: string | Buffer) => createHash('sha256').update(value).digest('hex');
+
+test('fixture observes delayed advisory contention before expiry without the old 700ms cutoff', async () => {
+ let time = 0;
+ const observed = await waitForAdvisoryContention(1500, {
+ nowMs: () => time,
+ readWaiting: () => time >= 950 ? '1' : '0',
+ delayImpl: async ms => { time += ms; },
+ });
+ expect(observed).toBe(960);
+ expect(observed).toBeLessThan(1500);
+});
+
+test('fixture rejects contention returned by a slow observation after expiry', async () => {
+ let time = 0;
+ await expect(waitForAdvisoryContention(1500, {
+ nowMs: () => time,
+ readWaiting: () => { time = 1600; return '1'; },
+ delayImpl: async () => {},
+ })).rejects.toThrow('LOCAL_REHEARSAL_CONTENTION_OBSERVED_AFTER_EXPIRY');
+});
+const fixtureParentRole = 'review_successor_fixture_parent_20261012';
+const fixtureMemberRole = 'review_successor_fixture_member_20261012';
+const fixtureOwnerPrefix = 'tzudong-review-successor-fixture';
+
+type FixtureRoleState = {
+ roles: Record[];
+ memberships: Record[];
+};
+
+const emptyFixtureRoleState = (): FixtureRoleState => ({ roles: [], memberships: [] });
+
+function ownedFixtureRoleState(token: string): FixtureRoleState {
+ const common = {
+ rolcanlogin: false,
+ rolsuper: false,
+ rolcreatedb: false,
+ rolcreaterole: false,
+ rolreplication: false,
+ rolbypassrls: false,
+ rolconnlimit: -1,
+ rolvaliduntil: null,
+ rolconfig: null,
+ };
+ return {
+ roles: [
+ {
+ ...common,
+ rolname: fixtureMemberRole,
+ rolinherit: true,
+ owner_marker: `${fixtureOwnerPrefix}:${token}:member`,
+ },
+ {
+ ...common,
+ rolname: fixtureParentRole,
+ rolinherit: false,
+ owner_marker: `${fixtureOwnerPrefix}:${token}:parent`,
+ },
+ ],
+ memberships: [{
+ parent: fixtureParentRole,
+ member: fixtureMemberRole,
+ grantor: 'supabase_admin',
+ admin_option: false,
+ inherit_option: true,
+ set_option: true,
+ }],
+ };
+}
+
+function fixtureOwnership() {
+ return {
+ database: false,
+ fixtureMembership: false,
+ fixtureMembershipAttempted: false,
+ fixtureMembershipToken: null,
+ fixtureMembershipBaseline: null,
+ };
+}
+
+type DatabaseMetadata = {
+ name: string;
+ owner: string;
+ encoding: string;
+ collate: string;
+ ctype: string;
+ localeProvider: string;
+ allowConnections: boolean;
+ isTemplate: boolean;
+};
+
+type DatabaseState = {
+ target: DatabaseMetadata | null;
+ template: DatabaseMetadata;
+};
+
+const templateDatabaseMetadata: DatabaseMetadata = {
+ name: 'ranking_clone',
+ owner: 'postgres',
+ encoding: 'UTF8',
+ collate: 'C',
+ ctype: 'C',
+ localeProvider: 'c',
+ allowConnections: true,
+ isTemplate: false,
+};
+
+function databaseNameFromSql(sql: string) {
+ const queryMatch = sql.match(/target_database\.datname='([^']+)'/);
+ const commandMatch = sql.match(/(?:CREATE|DROP) DATABASE ([a-z0-9_]+)/);
+ const databaseName = queryMatch?.[1] || commandMatch?.[1];
+ if (!databaseName) throw new Error(`database name missing from SQL: ${sql.slice(0, 80)}`);
+ return databaseName;
+}
+
+function databaseState(databaseName: string, targetOwner = 'postgres'): DatabaseState {
+ return {
+ target: {
+ ...templateDatabaseMetadata,
+ name: databaseName,
+ owner: targetOwner,
+ isTemplate: false,
+ },
+ template: { ...templateDatabaseMetadata },
+ };
+}
+
+function emptyDatabaseState(): DatabaseState {
+ return { target: null, template: { ...templateDatabaseMetadata } };
+}
+
+function databaseOwnership() {
+ return {
+ database: false,
+ databaseAttempted: false,
+ databaseName: null,
+ databaseBaseline: null,
+ };
+}
+
+test('successor acknowledgement loss reconciles a committed terminal state without SQL replay', () => {
+ const baselineState = { bundle: { targetCount: 0 }, state: { terminalContractReady: false } };
+ const terminalState = { bundle: { targetCount: 11 }, state: { terminalContractReady: true } };
+ let state = baselineState;
+ const sqlCalls: string[] = [];
+ let readbacks = 0;
+
+ const application = applySuccessorPlanOnce({
+ database: 'fixture_database',
+ sql: 'BEGIN; SELECT successor_mutation(); COMMIT;',
+ stateQuery: 'SELECT successor_state();',
+ baselineState,
+ terminalState,
+ psqlImpl: (_database: string, sql: string) => {
+ sqlCalls.push(sql);
+ state = terminalState;
+ throw new Error('response lost after commit');
+ },
+ readStateImpl: (_database: string, query: string) => {
+ expect(query).toBe('SELECT successor_state();');
+ readbacks += 1;
+ return state;
+ },
+ });
+
+ expect(application.outcome).toBe('committed-after-acknowledgement-loss');
+ expect(JSON.parse(application.result.stdout.trim())).toEqual(terminalState);
+ expect(sqlCalls).toEqual(['BEGIN; SELECT successor_mutation(); COMMIT;']);
+ expect(readbacks).toBe(1);
+});
+
+test('successor failure at the baseline is classified without diagnostic SQL replay', () => {
+ const baselineState = { bundle: { targetCount: 0 }, state: { terminalContractReady: false } };
+ const terminalState = { bundle: { targetCount: 11 }, state: { terminalContractReady: true } };
+ const sqlCalls: string[] = [];
+ let readbacks = 0;
+
+ expect(() => applySuccessorPlanOnce({
+ database: 'fixture_database',
+ sql: 'BEGIN; SELECT successor_mutation(); COMMIT;',
+ stateQuery: 'SELECT successor_state();',
+ baselineState,
+ terminalState,
+ psqlImpl: (_database: string, sql: string) => {
+ sqlCalls.push(sql);
+ throw new Error('transaction rolled back before acknowledgement');
+ },
+ readStateImpl: () => {
+ readbacks += 1;
+ return baselineState;
+ },
+ })).toThrow('LOCAL_REHEARSAL_APPLICATION_ROLLED_BACK');
+
+ expect(sqlCalls).toEqual(['BEGIN; SELECT successor_mutation(); COMMIT;']);
+ expect(readbacks).toBe(1);
+});
+
+test('retained proof is checksum-verified and distinguished from the current harness source', () => {
+ const manifestBytes = readFileSync(new URL('../../../.github/review-media-sql-successor.v1.json', import.meta.url));
+ const manifest = JSON.parse(manifestBytes.toString('utf8'));
+ const harnessBytes = readFileSync(new URL(
+ '../performance/review-db-successor-20261012/verify-review-media-sql-successor.mjs',
+ import.meta.url,
+ ));
+ const retained = inspectRetainedProof({
+ currentBindings: {
+ manifestSha256: sha256(manifestBytes),
+ rehearsalHarnessSha256: sha256(harnessBytes),
+ sourceRoot: manifest.sourceRoot,
+ },
+ });
+
+ expect(retained).toMatchObject({
+ present: true,
+ classification: 'retained-historical-evidence',
+ repositoryPath: retainedProofRepositoryPath,
+ expectedSha256: '6f5dc5c5603c8a4d8b601d3edb28009e454e955292a65707747e66f274557b3f',
+ actualSha256: '6f5dc5c5603c8a4d8b601d3edb28009e454e955292a65707747e66f274557b3f',
+ currentSourceMatch: false,
+ usedAsCurrentProof: false,
+ });
+ expect(retained.bindingDifferences.some(({ field }: { field: string }) => (
+ field === 'rehearsalHarnessSha256'
+ ))).toBe(true);
+});
+
+test('retained proof with bytes not authorized by SHA256SUMS is rejected', () => {
+ const directory = mkdtempSync(join(tmpdir(), 'tzudong-retained-proof-'));
+ try {
+ const outputPath = join(directory, 'pg17-local-replay.json');
+ const checksumsPath = join(directory, 'SHA256SUMS');
+ const proof = Buffer.from(`${JSON.stringify({
+ status: 'passed',
+ temporaryDatabaseDropped: true,
+ hostedWrites: false,
+ operatingWrites: false,
+ manifestSha256: '1'.repeat(64),
+ rehearsalHarnessSha256: '2'.repeat(64),
+ sourceRoot: '3'.repeat(64),
+ })}\n`);
+ writeFileSync(outputPath, proof);
+ const checksumBytes = `${sha256(proof)} ${retainedProofRepositoryPath}\n`;
+ writeFileSync(checksumsPath, checksumBytes);
+ writeFileSync(`${checksumsPath}.sha256`, `${sha256(checksumBytes)}\n`);
+ writeFileSync(outputPath, Buffer.concat([proof, Buffer.from(' ')]));
+
+ expect(() => inspectRetainedProof({
+ outputPath,
+ checksumsPath,
+ currentBindings: {
+ manifestSha256: '1'.repeat(64),
+ rehearsalHarnessSha256: '2'.repeat(64),
+ sourceRoot: '3'.repeat(64),
+ },
+ })).toThrow('LOCAL_REHEARSAL_RETAINED_SHA256_MISMATCH');
+ } finally {
+ rmSync(directory, { recursive: true, force: true });
+ }
+});
+
+test('jointly altered retained proof and checksum map are rejected by the independent digest', () => {
+ const directory = mkdtempSync(join(tmpdir(), 'tzudong-retained-map-tamper-'));
+ try {
+ const outputPath = join(directory, 'pg17-local-replay.json');
+ const checksumsPath = join(directory, 'SHA256SUMS');
+ const proof = { status: 'passed', temporaryDatabaseDropped: true, hostedWrites: false,
+ operatingWrites: false, manifestSha256: '1'.repeat(64),
+ rehearsalHarnessSha256: '2'.repeat(64), sourceRoot: '3'.repeat(64) };
+ const original = Buffer.from(`${JSON.stringify(proof)}\n`);
+ const originalMap = `${sha256(original)} ${retainedProofRepositoryPath}\n`;
+ writeFileSync(`${checksumsPath}.sha256`, `${sha256(originalMap)}\n`);
+ const tampered = Buffer.from(`${JSON.stringify({ ...proof, sourceRoot: '4'.repeat(64) })}\n`);
+ writeFileSync(outputPath, tampered);
+ writeFileSync(checksumsPath, `${sha256(tampered)} ${retainedProofRepositoryPath}\n`);
+ expect(() => inspectRetainedProof({ outputPath, checksumsPath, currentBindings: {
+ manifestSha256: '1'.repeat(64), rehearsalHarnessSha256: '2'.repeat(64), sourceRoot: '4'.repeat(64),
+ } })).toThrow('LOCAL_REHEARSAL_CHECKSUMS_SHA256_MISMATCH');
+ } finally {
+ rmSync(directory, { recursive: true, force: true });
+ }
+});
+
+for (const [label, failingDatabaseCall, expectedCode] of [
+ ['predecessor', 1, 'LOCAL_REHEARSAL_PREDECESSOR_FAILED'],
+ ['fixture', 7, 'fixture failed'],
+] as const) {
+ test(`owned database is cleaned after ${label} SQL fails`, () => {
+ const ownership = databaseOwnership();
+ let databaseExists = false;
+ let databaseName = '';
+ let databaseCalls = 0;
+ let drops = 0;
+ const dependencies = {
+ scalarImpl: (_database: string, sql: string) => {
+ const requestedName = databaseNameFromSql(sql);
+ return JSON.stringify(databaseExists ? databaseState(requestedName) : emptyDatabaseState());
+ },
+ readFileImpl: () => Buffer.from('-- predecessor fixture'),
+ psqlImpl: (database: string, sql: string | Buffer) => {
+ const statement = sql.toString();
+ if (database === 'postgres' && statement.startsWith('CREATE DATABASE ')) {
+ databaseName = databaseNameFromSql(statement);
+ databaseExists = true;
+ return {};
+ }
+ if (database === 'postgres' && statement.startsWith('DROP DATABASE ')) {
+ databaseExists = false;
+ drops += 1;
+ return {};
+ }
+ databaseCalls += 1;
+ if (databaseCalls === failingDatabaseCall) throw new Error(`${label} failed`);
+ return {};
+ },
+ };
+
+ try {
+ createOwnedDatabase(ownership, dependencies);
+ throw new Error('expected fixture construction to fail');
+ } catch (error) {
+ expect((error as Error).message).toContain(expectedCode);
+ expect(ownership.database).toBe(true);
+ } finally {
+ dropOwnedDatabase(ownership, dependencies);
+ }
+
+ expect(databaseExists).toBe(false);
+ expect(ownership.database).toBe(false);
+ expect(drops).toBe(1);
+ });
+}
+
+test('pre-existing database is never acquired or dropped', () => {
+ const ownership = databaseOwnership();
+ let databaseExists = true;
+ let scalarCalls = 0;
+ let psqlCalls = 0;
+ const dependencies = {
+ scalarImpl: (_database: string, sql: string) => {
+ scalarCalls += 1;
+ const databaseName = databaseNameFromSql(sql);
+ return JSON.stringify(databaseExists ? databaseState(databaseName) : emptyDatabaseState());
+ },
+ readFileImpl: () => Buffer.from(''),
+ psqlImpl: () => {
+ psqlCalls += 1;
+ databaseExists = false;
+ return {};
+ },
+ };
+
+ expect(() => createOwnedDatabase(ownership, dependencies)).toThrow('LOCAL_REHEARSAL_TARGET_ALREADY_EXISTS');
+ expect(dropOwnedDatabase(ownership, dependencies)).toBe(false);
+ expect(databaseExists).toBe(true);
+ expect(ownership.database).toBe(false);
+ expect(scalarCalls).toBe(1);
+ expect(psqlCalls).toBe(0);
+});
+
+test('database CREATE acknowledgement loss is reconciled without replay and cleaned by exact ownership', () => {
+ const ownership = databaseOwnership();
+ let state = emptyDatabaseState();
+ let createCalls = 0;
+ let dropCalls = 0;
+ const dependencies = {
+ scalarImpl: () => JSON.stringify(state),
+ readFileImpl: () => Buffer.from('-- predecessor fixture'),
+ psqlImpl: (database: string, sql: string | Buffer) => {
+ const statement = sql.toString();
+ if (database === 'postgres' && statement.startsWith('CREATE DATABASE ')) {
+ createCalls += 1;
+ state = databaseState(databaseNameFromSql(statement));
+ throw new Error('response lost after CREATE commit');
+ }
+ if (database === 'postgres' && statement.startsWith('DROP DATABASE ')) {
+ dropCalls += 1;
+ state = emptyDatabaseState();
+ return {};
+ }
+ return {};
+ },
+ };
+
+ expect(createOwnedDatabase(ownership, dependencies)).toBe(true);
+ expect(ownership).toMatchObject({
+ database: true,
+ databaseAttempted: true,
+ databaseCreationAcknowledged: false,
+ databaseCreationReconciled: true,
+ });
+ expect(ownership.databaseName).toMatch(/^review_successor_local_[0-9a-f]{32}$/);
+ expect(createCalls).toBe(1);
+ expect(dropOwnedDatabase(ownership, dependencies)).toBe(true);
+ expect(createCalls).toBe(1);
+ expect(dropCalls).toBe(1);
+ expect(state).toEqual(emptyDatabaseState());
+ expect(ownership.database).toBe(false);
+ expect(ownership.databaseAttempted).toBe(false);
+});
+
+test('cleanup re-reads and removes the exact owned database after the first CREATE readback fails', () => {
+ const ownership = databaseOwnership();
+ let state = emptyDatabaseState();
+ let readbacks = 0;
+ let createCalls = 0;
+ let dropCalls = 0;
+ const dependencies = {
+ scalarImpl: () => {
+ readbacks += 1;
+ if (readbacks === 2) throw new Error('readback transport failed');
+ return JSON.stringify(state);
+ },
+ readFileImpl: () => Buffer.from('-- predecessor fixture'),
+ psqlImpl: (database: string, sql: string | Buffer) => {
+ const statement = sql.toString();
+ if (database === 'postgres' && statement.startsWith('CREATE DATABASE ')) {
+ createCalls += 1;
+ state = databaseState(databaseNameFromSql(statement));
+ throw new Error('response lost after CREATE commit');
+ }
+ if (database === 'postgres' && statement.startsWith('DROP DATABASE ')) {
+ dropCalls += 1;
+ state = emptyDatabaseState();
+ return {};
+ }
+ throw new Error('unexpected SQL');
+ },
+ };
+
+ expect(() => createOwnedDatabase(ownership, dependencies))
+ .toThrow('LOCAL_REHEARSAL_DATABASE_READBACK_FAILED');
+ expect(ownership).toMatchObject({ database: false, databaseAttempted: true });
+ expect(dropOwnedDatabase(ownership, dependencies)).toBe(true);
+ expect(createCalls).toBe(1);
+ expect(dropCalls).toBe(1);
+ expect(readbacks).toBe(4);
+ expect(state).toEqual(emptyDatabaseState());
+});
+
+test('cleanup refuses a database whose exact owner metadata does not match', () => {
+ const ownership = databaseOwnership();
+ let state = emptyDatabaseState();
+ let dropCalls = 0;
+ const dependencies = {
+ scalarImpl: () => JSON.stringify(state),
+ readFileImpl: () => Buffer.from('-- predecessor fixture'),
+ psqlImpl: (database: string, sql: string | Buffer) => {
+ const statement = sql.toString();
+ if (database === 'postgres' && statement.startsWith('CREATE DATABASE ')) {
+ state = databaseState(databaseNameFromSql(statement));
+ return {};
+ }
+ if (database === 'postgres' && statement.startsWith('DROP DATABASE ')) {
+ dropCalls += 1;
+ state = emptyDatabaseState();
+ return {};
+ }
+ return {};
+ },
+ };
+
+ createOwnedDatabase(ownership, dependencies);
+ state = databaseState(String(ownership.databaseName), 'foreign_owner');
+ expect(() => dropOwnedDatabase(ownership, dependencies))
+ .toThrow('LOCAL_REHEARSAL_DATABASE_OWNERSHIP_UNPROVEN');
+ expect(dropCalls).toBe(0);
+ expect(state.target?.owner).toBe('foreign_owner');
+});
+
+test('fixture role creation acknowledgement loss is reconciled and exact owned roles are cleaned', () => {
+ const token = 'a'.repeat(32);
+ const ownership = fixtureOwnership();
+ let state = emptyFixtureRoleState();
+ const statements: string[] = [];
+ const dependencies = {
+ scalarImpl: () => JSON.stringify(state),
+ psqlImpl: (_database: string, sql: string) => {
+ statements.push(sql);
+ if (sql.includes(`CREATE ROLE ${fixtureParentRole}`)) {
+ state = ownedFixtureRoleState(token);
+ throw new Error('response lost after commit');
+ }
+ if (sql.includes(`DROP ROLE ${fixtureMemberRole}`)) {
+ state = emptyFixtureRoleState();
+ throw new Error('response lost after drop commit');
+ }
+ throw new Error('unexpected SQL');
+ },
+ generateToken: () => token,
+ };
+
+ expect(createFixtureMembership(ownership, dependencies)).toBe(true);
+ expect(ownership).toMatchObject({
+ fixtureMembership: true,
+ fixtureMembershipToken: token,
+ fixtureMembershipCreationAcknowledged: false,
+ fixtureMembershipCreationReconciled: true,
+ });
+ expect(dropFixtureMembership(ownership, dependencies)).toBe(true);
+ expect(ownership).toMatchObject({
+ fixtureMembership: false,
+ fixtureMembershipDropped: true,
+ fixtureMembershipRemovalAcknowledged: false,
+ });
+ expect(state).toEqual(emptyFixtureRoleState());
+ expect(statements).toHaveLength(2);
+ expect(statements[0]).toContain(
+ `COMMENT ON ROLE ${fixtureParentRole} IS '${fixtureOwnerPrefix}:${token}:parent'`,
+ );
+ expect(statements[0]).toContain(
+ `COMMENT ON ROLE ${fixtureMemberRole} IS '${fixtureOwnerPrefix}:${token}:member'`,
+ );
+ expect(statements[1]).toContain(`DROP ROLE ${fixtureMemberRole}`);
+ expect(statements[1]).toContain(`DROP ROLE ${fixtureParentRole}`);
+});
+
+test('cleanup re-reads exact owned roles when the first post-create readback fails', () => {
+ const token = '4'.repeat(32);
+ const ownership = fixtureOwnership();
+ let state = emptyFixtureRoleState();
+ let readbacks = 0;
+ const statements: string[] = [];
+ const dependencies = {
+ scalarImpl: () => {
+ readbacks += 1;
+ if (readbacks === 2) throw new Error('readback transport failed');
+ return JSON.stringify(state);
+ },
+ psqlImpl: (_database: string, sql: string) => {
+ statements.push(sql);
+ if (sql.includes(`CREATE ROLE ${fixtureParentRole}`)) {
+ state = ownedFixtureRoleState(token);
+ throw new Error('response lost after create commit');
+ }
+ if (sql.includes(`DROP ROLE ${fixtureMemberRole}`)) {
+ state = emptyFixtureRoleState();
+ return {};
+ }
+ throw new Error('unexpected SQL');
+ },
+ generateToken: () => token,
+ };
+
+ expect(() => createFixtureMembership(ownership, dependencies))
+ .toThrow('LOCAL_REHEARSAL_FIXTURE_ROLE_READBACK_FAILED');
+ expect(ownership).toMatchObject({
+ fixtureMembership: false,
+ fixtureMembershipAttempted: true,
+ fixtureMembershipToken: token,
+ fixtureMembershipBaseline: emptyFixtureRoleState(),
+ });
+ expect(dropFixtureMembership(ownership, dependencies)).toBe(true);
+ expect(ownership).toMatchObject({
+ fixtureMembership: false,
+ fixtureMembershipAttempted: false,
+ fixtureMembershipDropped: true,
+ });
+ expect(state).toEqual(emptyFixtureRoleState());
+ expect(readbacks).toBe(4);
+ expect(statements).toHaveLength(2);
+});
+
+test('fixture role creation failure without owned readback never authorizes cleanup', () => {
+ const ownership = fixtureOwnership();
+ let state = emptyFixtureRoleState();
+ const statements: string[] = [];
+ const dependencies = {
+ scalarImpl: () => JSON.stringify(state),
+ psqlImpl: (_database: string, sql: string) => {
+ statements.push(sql);
+ throw new Error('transaction failed before commit');
+ },
+ generateToken: () => 'b'.repeat(32),
+ };
+
+ expect(() => createFixtureMembership(ownership, dependencies))
+ .toThrow('LOCAL_REHEARSAL_FIXTURE_ROLE_CREATION_UNCONFIRMED');
+ expect(ownership.fixtureMembership).toBe(false);
+ expect(ownership.fixtureMembershipAttempted).toBe(false);
+ expect(ownership.fixtureMembershipToken).toBe('b'.repeat(32));
+ expect(dropFixtureMembership(ownership, dependencies)).toBe(false);
+ expect(statements).toHaveLength(1);
+ expect(statements.some(statement => statement.includes('DROP ROLE'))).toBe(false);
+ expect(state).toEqual(emptyFixtureRoleState());
+});
+
+test('pre-existing fixture role collision is preserved and never acquired for cleanup', () => {
+ const ownership = fixtureOwnership();
+ const state: FixtureRoleState = {
+ roles: [{
+ ...ownedFixtureRoleState('c'.repeat(32)).roles[0],
+ owner_marker: 'foreign-owner',
+ }],
+ memberships: [],
+ };
+ let psqlCalls = 0;
+ const dependencies = {
+ scalarImpl: () => JSON.stringify(state),
+ psqlImpl: () => {
+ psqlCalls += 1;
+ return {};
+ },
+ generateToken: () => 'd'.repeat(32),
+ };
+
+ expect(() => createFixtureMembership(ownership, dependencies))
+ .toThrow('LOCAL_REHEARSAL_FIXTURE_ROLE_EXISTS');
+ expect(dropFixtureMembership(ownership, dependencies)).toBe(false);
+ expect(psqlCalls).toBe(0);
+ expect(state.roles[0].owner_marker).toBe('foreign-owner');
+});
+
+test('uncertain create collision with another owner is not guessed or removed', () => {
+ const ownership = fixtureOwnership();
+ let state = emptyFixtureRoleState();
+ const statements: string[] = [];
+ const dependencies = {
+ scalarImpl: () => JSON.stringify(state),
+ psqlImpl: (_database: string, sql: string) => {
+ statements.push(sql);
+ state = ownedFixtureRoleState('e'.repeat(32));
+ throw new Error('ambiguous create response');
+ },
+ generateToken: () => 'f'.repeat(32),
+ };
+
+ expect(() => createFixtureMembership(ownership, dependencies))
+ .toThrow('LOCAL_REHEARSAL_FIXTURE_ROLE_OWNERSHIP_UNPROVEN');
+ expect(ownership.fixtureMembership).toBe(false);
+ expect(ownership.fixtureMembershipAttempted).toBe(false);
+ expect(ownership.fixtureMembershipToken).toBe('f'.repeat(32));
+ expect(dropFixtureMembership(ownership, dependencies)).toBe(false);
+ expect(statements).toHaveLength(1);
+ expect(statements.some(statement => statement.includes('DROP ROLE'))).toBe(false);
+ expect(state).toEqual(ownedFixtureRoleState('e'.repeat(32)));
+});
+
+test('cleanup refuses roles whose ownership marker changed after creation', () => {
+ const token = '1'.repeat(32);
+ const ownership = fixtureOwnership();
+ let state = emptyFixtureRoleState();
+ const statements: string[] = [];
+ const dependencies = {
+ scalarImpl: () => JSON.stringify(state),
+ psqlImpl: (_database: string, sql: string) => {
+ statements.push(sql);
+ state = ownedFixtureRoleState(token);
+ return {};
+ },
+ generateToken: () => token,
+ };
+
+ createFixtureMembership(ownership, dependencies);
+ state = ownedFixtureRoleState('2'.repeat(32));
+ expect(() => dropFixtureMembership(ownership, dependencies))
+ .toThrow('LOCAL_REHEARSAL_FIXTURE_ROLE_OWNERSHIP_UNPROVEN');
+ expect(ownership.fixtureMembership).toBe(true);
+ expect(statements).toHaveLength(1);
+ expect(statements.some(statement => statement.includes('DROP ROLE'))).toBe(false);
+ expect(state).toEqual(ownedFixtureRoleState('2'.repeat(32)));
+});
+
+test('cleanup refuses to remove owned roles after an out-of-scope membership is added', () => {
+ const token = '3'.repeat(32);
+ const ownership = fixtureOwnership();
+ let state = emptyFixtureRoleState();
+ const statements: string[] = [];
+ const dependencies = {
+ scalarImpl: () => JSON.stringify(state),
+ psqlImpl: (_database: string, sql: string) => {
+ statements.push(sql);
+ state = ownedFixtureRoleState(token);
+ return {};
+ },
+ generateToken: () => token,
+ };
+
+ createFixtureMembership(ownership, dependencies);
+ state = ownedFixtureRoleState(token);
+ state.memberships.push({
+ parent: 'foreign_parent_role',
+ member: fixtureMemberRole,
+ grantor: 'postgres',
+ admin_option: false,
+ inherit_option: true,
+ set_option: true,
+ });
+ expect(() => dropFixtureMembership(ownership, dependencies))
+ .toThrow('LOCAL_REHEARSAL_FIXTURE_ROLE_OWNERSHIP_UNPROVEN');
+ expect(ownership.fixtureMembership).toBe(true);
+ expect(statements).toHaveLength(1);
+ expect(statements.some(statement => statement.includes('DROP ROLE'))).toBe(false);
+ expect(state.memberships).toHaveLength(2);
+});
diff --git a/apps/web/tests-unit/review-media-sql-successor.test.ts b/apps/web/tests-unit/review-media-sql-successor.test.ts
new file mode 100644
index 0000000000..16a1647038
--- /dev/null
+++ b/apps/web/tests-unit/review-media-sql-successor.test.ts
@@ -0,0 +1,1943 @@
+import { afterAll, expect, test } from 'bun:test';
+import { createHash } from 'node:crypto';
+import {
+ chmodSync,
+ closeSync,
+ existsSync,
+ fsyncSync,
+ linkSync,
+ mkdtempSync,
+ openSync,
+ readFileSync,
+ rmSync,
+ writeFileSync,
+ writeSync,
+} from 'node:fs';
+import { tmpdir } from 'node:os';
+import { join } from 'node:path';
+
+import {
+ appendJournal,
+ successorFailureResult,
+ compileSuccessorRehearsalPlan,
+ compileSuccessorStateReadQuery,
+ compileSuccessorPlan,
+ createSuccessorPsqlRunner,
+ loadSuccessorManifest,
+ protectedSourceReadbackBinding,
+ runReviewMediaSuccessor,
+ runReviewMediaSuccessorRehearsal,
+ SUCCESSOR_PRE_COMMIT_EXPIRY_MARKER,
+ validateSuccessorAdmission,
+ validateSuccessorRehearsalAdmission,
+} from '../scripts/review-media-sql-successor.mjs';
+import { parseRehearsalArguments } from '../scripts/review-media-sql-rehearsal.mjs';
+import { elevenMigrationBundleSuffixRoot } from '../scripts/supabase-migration-bundle.mjs';
+import { statementSpans } from '../scripts/supabase-migration-transaction.mjs';
+
+const manifestBytes = readFileSync(new URL('../../../.github/review-media-sql-successor.v1.json', import.meta.url));
+const manifest = JSON.parse(manifestBytes.toString('utf8'));
+const record = Object.freeze({
+ bytes: manifestBytes,
+ manifest,
+ manifestSha256: createHash('sha256').update(manifestBytes).digest('hex'),
+ materials: Object.freeze(manifest.migrations.map((migration: any) => ({
+ path: migration.path,
+ bytes: readFileSync(new URL(`../../../${migration.path}`, import.meta.url)),
+ originalVector: migration.originalStatementVector,
+ }))),
+});
+const executableRecord = Object.freeze({
+ ...record,
+ manifest: Object.freeze({
+ ...record.manifest,
+ launchPolicy: Object.freeze({ ...record.manifest.launchPolicy, state: 'ready' }),
+ }),
+});
+const fixedNow = new Date('2026-10-12T08:00:00.000Z');
+const revision = '056a86600f412eb42df4304e3206841679012f3e';
+// This injected transport has no credential. Keep its project/role/database
+// identity bound to the same manifest that the controller validates.
+const databaseUrl = new URL(
+ `postgresql://${record.manifest.executorRole}.${record.manifest.projectRef}@aws-0-ap-southeast-1.pooler.supabase.com:6543/${record.manifest.databaseName}`,
+).toString();
+const rollback = Object.freeze({
+ deploymentId: 'dpl_EUxJnWmDDozCSMWb2sqNSPgKJLM5',
+ deploymentSha: '1884f87e53c4567ead7c9a0fb394b09b91a70396',
+ deploymentUrl: 'https://tzudong-fixture-twoimos-projects.vercel.app/',
+ gitRef: 'main',
+ productionAliases: ['tzudong.app', 'www.tzudong.app'],
+ projectId: 'prj_sau35J5uUtShIQ9OKofRtOVVnTSl',
+ readyState: 'READY',
+ repository: 'twoimo/tzudong',
+ state: 'ready',
+ target: 'production',
+ teamId: 'team_OUj64KeLxJI3PkEbOaFZnorA',
+});
+const directories: string[] = [];
+const sha256 = (value: string | Buffer) => createHash('sha256').update(value).digest('hex');
+const canonical = (value: any): any => Array.isArray(value)
+ ? value.map(canonical)
+ : value && typeof value === 'object'
+ ? Object.fromEntries(Object.keys(value).sort().map(key => [key, canonical(value[key])]))
+ : value;
+const canonicalBytes = (value: any) => Buffer.from(`${JSON.stringify(canonical(value))}\n`);
+const writeCanonicalLine = (descriptor: number, entry: any, { sync = true } = {}) => {
+ const bytes = canonicalBytes(entry);
+ expect(writeSync(descriptor, bytes)).toBe(bytes.length);
+ if (sync) fsyncSync(descriptor);
+};
+
+afterAll(() => {
+ for (const directory of directories) rmSync(directory, { recursive: true, force: true });
+});
+
+function stageStates() {
+ const prefixRoot = '1'.repeat(64);
+ return Array.from({ length: 12 }, (_, index) => ({
+ bundle: {
+ ledgerCount: 86 + index,
+ prefixRoot,
+ priorCount: 86,
+ suffixRoot: index === 11 ? elevenMigrationBundleSuffixRoot(manifest.migrations) : null,
+ targetCount: index,
+ },
+ state: {
+ activeReviewItems: 0,
+ currentUser: 'postgres',
+ databaseName: 'postgres',
+ finishCleanupBodySha256: index === 10
+ ? manifest.terminalContract.finishPreimageSha256
+ : index === 11
+ ? manifest.terminalContract.finishPostimageSha256
+ : index === 0 ? null : (index + 2).toString(16).repeat(64),
+ g014Root: (index < 3 ? 'a' : index === 3 ? 'b' : 'c').repeat(64),
+ ledgerRoot: (index + 1).toString(16).repeat(64),
+ ownerAuthorityReady: true,
+ reviewItems: 0,
+ reviewPolicyEnabled: false,
+ reviewRuns: 0,
+ schemaRoot: (index + 3).toString(16).repeat(64),
+ serverMajor: 17,
+ sessionUser: 'postgres',
+ storageRoot: (index + 4).toString(16).repeat(64),
+ terminalContractReady: index === 11,
+ },
+ }));
+}
+
+function admission() {
+ const stages = stageStates();
+ const writerFences = Object.fromEntries(Object.entries(manifest.requiredWriterFences).map(
+ ([key, state], index) => [key, { evidenceSha256: (index + 8).toString(16).repeat(64), state }],
+ ));
+ return {
+ attemptId: '123e4567-e89b-42d3-a456-426614174000',
+ createdAt: '2026-10-12T07:59:00.000Z',
+ expiresAt: '2026-10-12T08:10:00.000Z',
+ id: manifest.id,
+ journalPathSha256: 'f'.repeat(64),
+ manifestSha256: record.manifestSha256,
+ projectRef: manifest.projectRef,
+ protectedMainReceiptSha256: '1'.repeat(64),
+ purpose: manifest.purpose,
+ rehearsalAdmissionSha256: '6'.repeat(64),
+ rehearsalJournalSha256: '5'.repeat(64),
+ rehearsalReceiptSha256: '2'.repeat(64),
+ rollback: { ...structuredClone(rollback), readbackSha256: '3'.repeat(64) },
+ schemaVersion: 1,
+ sourceReceiptSha256: '4'.repeat(64),
+ sourceRevision: revision,
+ sourceRoot: manifest.sourceRoot,
+ stageStates: stages,
+ writerFences,
+ };
+}
+
+function custodyFiles(document = admission()) {
+ const directory = mkdtempSync(join(tmpdir(), 'tzudong-review-successor-'));
+ directories.push(directory);
+ chmodSync(directory, 0o700);
+ const admissionPath = join(directory, 'admission.json');
+ const journalPath = join(directory, 'attempt.jsonl');
+ const rehearsalJournalPath = join(directory, 'rehearsal-journal.jsonl');
+ document.journalPathSha256 = sha256(journalPath);
+ const common = {
+ schemaVersion: 1,
+ id: document.id,
+ projectRef: document.projectRef,
+ purpose: document.purpose,
+ manifestSha256: document.manifestSha256,
+ sourceRevision: document.sourceRevision,
+ observedAt: document.createdAt,
+ expiresAt: document.expiresAt,
+ };
+ const rehearsalAttemptId = '123e4567-e89b-42d3-a456-426614174001';
+ let rehearsalAdmissionSha256 = '';
+ const receipts: Record = {
+ 'source-receipt.json': { ...common, kind: 'successor-source-receipt', sourceRoot: document.sourceRoot },
+ 'protected-main-receipt.json': {
+ ...common,
+ kind: 'protected-main-readback-receipt',
+ ref: manifest.protectedSource.ref,
+ remote: manifest.protectedSource.remote,
+ repositoryUrl: manifest.protectedSource.repositoryUrl,
+ revision: document.sourceRevision,
+ },
+ 'rehearsal-receipt.json': {
+ ...common,
+ admissionSha256: rehearsalAdmissionSha256,
+ attemptId: rehearsalAttemptId,
+ kind: 'successor-rehearsal-receipt',
+ journalPathSha256: sha256(rehearsalJournalPath),
+ observedAt: new Date(Date.parse(document.createdAt) - 60_000).toISOString(),
+ passed: true,
+ preflightStateSha256: sha256(JSON.stringify(canonical(document.stageStates[0]))),
+ restoredStateSha256: sha256(JSON.stringify(canonical(document.stageStates[0]))),
+ serverVersionNum: 170006,
+ sourceRoot: document.sourceRoot,
+ stageStates: document.stageStates,
+ stageStatesSha256: sha256(JSON.stringify(canonical(document.stageStates))),
+ suffixRoot: elevenMigrationBundleSuffixRoot(manifest.migrations),
+ },
+ 'rollback-readback-receipt.json': {
+ ...common,
+ kind: 'rollback-readback-receipt',
+ deploymentId: document.rollback.deploymentId,
+ deploymentSha: document.rollback.deploymentSha,
+ deploymentUrl: document.rollback.deploymentUrl,
+ gitRef: document.rollback.gitRef,
+ productionAliases: document.rollback.productionAliases,
+ projectId: document.rollback.projectId,
+ readyState: document.rollback.readyState,
+ repository: document.rollback.repository,
+ state: document.rollback.state,
+ target: document.rollback.target,
+ teamId: document.rollback.teamId,
+ },
+ };
+ document.sourceReceiptSha256 = sha256(canonicalBytes(receipts['source-receipt.json']));
+ document.protectedMainReceiptSha256 = sha256(canonicalBytes(receipts['protected-main-receipt.json']));
+ document.rehearsalReceiptSha256 = sha256(canonicalBytes(receipts['rehearsal-receipt.json']));
+ const rehearsalJournal = [
+ {
+ admissionSha256: rehearsalAdmissionSha256,
+ attemptId: rehearsalAttemptId,
+ event: 'started',
+ manifestSha256: document.manifestSha256,
+ sourceRevision: document.sourceRevision,
+ sourceRoot: document.sourceRoot,
+ startedAt: new Date(Date.parse(document.createdAt) - 120_000).toISOString(),
+ },
+ {
+ admissionSha256: rehearsalAdmissionSha256,
+ attemptId: rehearsalAttemptId,
+ code: 'SUCCESSOR_REHEARSAL_VERIFIED',
+ event: 'verification',
+ manifestSha256: document.manifestSha256,
+ receiptSha256: document.rehearsalReceiptSha256,
+ sourceRevision: document.sourceRevision,
+ sourceRoot: document.sourceRoot,
+ verifiedAt: new Date(Date.parse(document.createdAt) - 60_000).toISOString(),
+ },
+ {
+ admissionSha256: rehearsalAdmissionSha256,
+ attemptId: rehearsalAttemptId,
+ code: 'SUCCESSOR_REHEARSAL_PUBLICATION_CONFIRMED',
+ event: 'publication',
+ manifestSha256: document.manifestSha256,
+ publishedAt: new Date(Date.parse(document.createdAt) - 30_000).toISOString(),
+ receiptSha256: document.rehearsalReceiptSha256,
+ sourceRevision: document.sourceRevision,
+ sourceRoot: document.sourceRoot,
+ },
+ ];
+ const rehearsalJournalBytes = Buffer.from(rehearsalJournal
+ .map(entry => JSON.stringify(canonical(entry)))
+ .join('\n') + '\n');
+ document.rehearsalJournalSha256 = sha256(rehearsalJournalBytes);
+ document.rollback.readbackSha256 = sha256(canonicalBytes(receipts['rollback-readback-receipt.json']));
+ for (const [key, state] of Object.entries(manifest.requiredWriterFences)) {
+ const name = `writer-fence-${key}.json`;
+ receipts[name] = { ...common, fence: key, kind: 'writer-fence-readback-receipt', state };
+ document.writerFences[key].evidenceSha256 = sha256(canonicalBytes(receipts[name]));
+ }
+ const storedRehearsalAdmission = {
+ ...rehearsalAdmission(),
+ attemptId: rehearsalAttemptId,
+ createdAt: new Date(Math.max(
+ Date.parse(document.createdAt) - 120_000, Date.parse(document.expiresAt) - 15 * 60_000,
+ )).toISOString(),
+ expiresAt: document.expiresAt,
+ journalPathSha256: sha256(rehearsalJournalPath),
+ receiptPathSha256: sha256(join(directory, 'rehearsal-receipt.json')),
+ protectedMainReceiptSha256: document.protectedMainReceiptSha256,
+ rollback: structuredClone(document.rollback),
+ sourceReceiptSha256: document.sourceReceiptSha256,
+ writerFences: structuredClone(document.writerFences),
+ };
+ const storedRehearsalBytes = canonicalBytes(storedRehearsalAdmission);
+ rehearsalAdmissionSha256 = sha256(storedRehearsalBytes);
+ document.rehearsalAdmissionSha256 = rehearsalAdmissionSha256;
+ receipts['rehearsal-receipt.json'].admissionSha256 = rehearsalAdmissionSha256;
+ document.rehearsalReceiptSha256 = sha256(canonicalBytes(receipts['rehearsal-receipt.json']));
+ for (const entry of rehearsalJournal) {
+ entry.admissionSha256 = rehearsalAdmissionSha256;
+ if ('receiptSha256' in entry) entry.receiptSha256 = document.rehearsalReceiptSha256;
+ }
+ const boundJournalBytes = Buffer.from(`${rehearsalJournal.map(entry => JSON.stringify(canonical(entry))).join('\n')}\n`);
+ document.rehearsalJournalSha256 = sha256(boundJournalBytes);
+ writeFileSync(join(directory, 'rehearsal-admission.json'), storedRehearsalBytes, { mode: 0o600 });
+ for (const [name, receipt] of Object.entries(receipts)) {
+ const path = join(directory, name);
+ writeFileSync(path, canonicalBytes(receipt), { mode: 0o600 });
+ chmodSync(path, 0o600);
+ }
+ writeFileSync(rehearsalJournalPath, boundJournalBytes, { mode: 0o600 });
+ chmodSync(rehearsalJournalPath, 0o600);
+ writeFileSync(admissionPath, canonicalBytes(document), { mode: 0o600 });
+ chmodSync(admissionPath, 0o600);
+ return { admissionPath, directory, document, journalPath, rehearsalJournalPath };
+}
+
+function rewriteRehearsalTimeline(files: ReturnType, {
+ expiresAt = files.document.expiresAt,
+ observedAt,
+ publishedAt,
+ startedAt,
+ verifiedAt,
+}: {
+ expiresAt?: string;
+ observedAt: string;
+ publishedAt: string;
+ startedAt: string;
+ verifiedAt: string;
+}) {
+ const receiptPath = join(files.directory, 'rehearsal-receipt.json');
+ const receipt = JSON.parse(readFileSync(receiptPath, 'utf8'));
+ receipt.expiresAt = expiresAt;
+ receipt.observedAt = observedAt;
+ const receiptBytes = canonicalBytes(receipt);
+ writeFileSync(receiptPath, receiptBytes, { mode: 0o600 });
+ files.document.rehearsalReceiptSha256 = sha256(receiptBytes);
+
+ const journal = readFileSync(files.rehearsalJournalPath, 'utf8').trim().split('\n').map(JSON.parse);
+ journal[0].startedAt = startedAt;
+ journal[1].verifiedAt = verifiedAt;
+ journal[1].receiptSha256 = files.document.rehearsalReceiptSha256;
+ journal[2].publishedAt = publishedAt;
+ journal[2].receiptSha256 = files.document.rehearsalReceiptSha256;
+ const journalBytes = Buffer.from(`${journal.map(entry => JSON.stringify(canonical(entry))).join('\n')}\n`);
+ writeFileSync(files.rehearsalJournalPath, journalBytes, { mode: 0o600 });
+ files.document.rehearsalJournalSha256 = sha256(journalBytes);
+}
+
+function rebindRehearsalEvidence(files: ReturnType, stored: any) {
+ const admissionBytes = canonicalBytes(stored);
+ writeFileSync(join(files.directory, 'rehearsal-admission.json'), admissionBytes, { mode: 0o600 });
+ const digest = sha256(admissionBytes);
+ const receiptPath = join(files.directory, 'rehearsal-receipt.json');
+ const receipt = JSON.parse(readFileSync(receiptPath, 'utf8'));
+ Object.assign(receipt, { admissionSha256: digest, attemptId: stored.attemptId });
+ const receiptBytes = canonicalBytes(receipt);
+ writeFileSync(receiptPath, receiptBytes, { mode: 0o600 });
+ files.document.rehearsalReceiptSha256 = sha256(receiptBytes);
+ const journal = readFileSync(files.rehearsalJournalPath, 'utf8').trim().split('\n').map(JSON.parse);
+ for (const entry of journal) {
+ entry.admissionSha256 = digest;
+ entry.attemptId = stored.attemptId;
+ if ('receiptSha256' in entry) entry.receiptSha256 = files.document.rehearsalReceiptSha256;
+ }
+ const journalBytes = Buffer.from(`${journal.map(entry => JSON.stringify(canonical(entry))).join('\n')}\n`);
+ writeFileSync(files.rehearsalJournalPath, journalBytes, { mode: 0o600 });
+ files.document.rehearsalJournalSha256 = sha256(journalBytes);
+ return digest;
+}
+
+function rehearsalAdmission() {
+ const writerFences = Object.fromEntries(Object.entries(manifest.requiredWriterFences).map(
+ ([key, state], index) => [key, { evidenceSha256: (index + 8).toString(16).repeat(64), state }],
+ ));
+ return {
+ attemptId: '123e4567-e89b-42d3-a456-426614174001',
+ createdAt: '2026-10-12T07:59:00.000Z',
+ expiresAt: '2026-10-12T08:10:00.000Z',
+ id: manifest.id,
+ journalPathSha256: 'f'.repeat(64),
+ kind: 'successor-rehearsal-admission',
+ manifestSha256: record.manifestSha256,
+ projectRef: manifest.projectRef,
+ protectedMainReceiptSha256: '1'.repeat(64),
+ purpose: manifest.purpose,
+ receiptPathSha256: '2'.repeat(64),
+ rollback: { ...structuredClone(rollback), readbackSha256: '3'.repeat(64) },
+ schemaVersion: 1,
+ sourceReceiptSha256: '4'.repeat(64),
+ sourceRevision: revision,
+ sourceRoot: manifest.sourceRoot,
+ writerFences,
+ };
+}
+
+function rehearsalCustodyFiles(document = rehearsalAdmission()) {
+ const directory = mkdtempSync(join(tmpdir(), 'tzudong-review-rehearsal-'));
+ directories.push(directory);
+ chmodSync(directory, 0o700);
+ const admissionPath = join(directory, 'rehearsal-admission.json');
+ const journalPath = join(directory, 'rehearsal-journal.jsonl');
+ const receiptPath = join(directory, 'rehearsal-receipt.json');
+ const pendingReceiptPath = `${receiptPath}.${document.attemptId}.pending`;
+ document.journalPathSha256 = sha256(journalPath);
+ document.receiptPathSha256 = sha256(receiptPath);
+ const common = {
+ schemaVersion: 1,
+ id: document.id,
+ projectRef: document.projectRef,
+ purpose: document.purpose,
+ manifestSha256: document.manifestSha256,
+ sourceRevision: document.sourceRevision,
+ observedAt: document.createdAt,
+ expiresAt: document.expiresAt,
+ };
+ const receipts: Record = {
+ 'source-receipt.json': { ...common, kind: 'successor-source-receipt', sourceRoot: document.sourceRoot },
+ 'protected-main-receipt.json': {
+ ...common,
+ kind: 'protected-main-readback-receipt',
+ ref: manifest.protectedSource.ref,
+ remote: manifest.protectedSource.remote,
+ repositoryUrl: manifest.protectedSource.repositoryUrl,
+ revision: document.sourceRevision,
+ },
+ 'rollback-readback-receipt.json': {
+ ...common,
+ kind: 'rollback-readback-receipt',
+ deploymentId: document.rollback.deploymentId,
+ deploymentSha: document.rollback.deploymentSha,
+ deploymentUrl: document.rollback.deploymentUrl,
+ gitRef: document.rollback.gitRef,
+ productionAliases: document.rollback.productionAliases,
+ projectId: document.rollback.projectId,
+ readyState: document.rollback.readyState,
+ repository: document.rollback.repository,
+ state: document.rollback.state,
+ target: document.rollback.target,
+ teamId: document.rollback.teamId,
+ },
+ };
+ document.sourceReceiptSha256 = sha256(canonicalBytes(receipts['source-receipt.json']));
+ document.protectedMainReceiptSha256 = sha256(canonicalBytes(receipts['protected-main-receipt.json']));
+ document.rollback.readbackSha256 = sha256(canonicalBytes(receipts['rollback-readback-receipt.json']));
+ for (const [key, state] of Object.entries(manifest.requiredWriterFences)) {
+ const name = `writer-fence-${key}.json`;
+ receipts[name] = { ...common, fence: key, kind: 'writer-fence-readback-receipt', state };
+ document.writerFences[key].evidenceSha256 = sha256(canonicalBytes(receipts[name]));
+ }
+ for (const [name, receipt] of Object.entries(receipts)) {
+ const path = join(directory, name);
+ writeFileSync(path, canonicalBytes(receipt), { mode: 0o600 });
+ chmodSync(path, 0o600);
+ }
+ writeFileSync(admissionPath, canonicalBytes(document), { mode: 0o600 });
+ chmodSync(admissionPath, 0o600);
+ return { admissionPath, directory, document, journalPath, pendingReceiptPath, receiptPath };
+}
+
+function rehearsalReadback(state = stageStates()[0]) {
+ return JSON.stringify({
+ kind: 'successor-rehearsal-readback',
+ serverVersionNum: 170006,
+ state,
+ });
+}
+
+function rehearsalStageOutput(stages = stageStates()) {
+ return stages.map((state, index) => JSON.stringify({
+ index,
+ kind: 'successor-rehearsal-stage',
+ serverVersionNum: 170006,
+ state,
+ })).join('\n');
+}
+
+function rollbackReadback(now = fixedNow) {
+ return {
+ schemaVersion: 1,
+ kind: 'vercel-rollback-readback',
+ projectId: rollback.projectId,
+ teamId: rollback.teamId,
+ deploymentId: rollback.deploymentId,
+ deploymentUrl: rollback.deploymentUrl,
+ readyState: rollback.readyState,
+ target: rollback.target,
+ repository: rollback.repository,
+ gitSha: rollback.deploymentSha,
+ gitRef: rollback.gitRef,
+ productionAliases: [...rollback.productionAliases],
+ observedAt: now.toISOString(),
+ };
+}
+
+test('manifest binds the exact eleven sources and remains held without fresh admission', () => {
+ const loaded = loadSuccessorManifest();
+ expect(loaded.manifest.migrations).toHaveLength(11);
+ expect(loaded.manifest.toolchain[0].path).toBe('apps/web/scripts/review-media-sql-successor.mjs');
+ expect(loaded.manifest.operatingTransition).toEqual({ beforeEleven: 86, afterEleven: 97 });
+ expect(loaded.manifest.launchPolicy.state).toBe('held');
+ expect(loaded.manifest.sourceRoot).toBe('61f9d7637c0714a7b0d9108c6f7959ea820175a7870dba681498b570296a1544');
+ expect(() => loadSuccessorManifest({ readVectorImpl: () => ['drift'] })).toThrow('SUCCESSOR_SOURCE_DRIFT');
+});
+
+test('rehearsal CLI requires one admission, journal and receipt path', () => {
+ expect(parseRehearsalArguments([
+ '--admission', '/private/admission.json',
+ '--journal', '/private/journal.jsonl',
+ '--receipt', '/private/rehearsal-receipt.json',
+ ])).toEqual({
+ admission: '/private/admission.json',
+ journal: '/private/journal.jsonl',
+ receipt: '/private/rehearsal-receipt.json',
+ });
+ expect(() => parseRehearsalArguments(['--admission', '/private/admission.json'])).toThrow(
+ 'SUCCESSOR_REHEARSAL_ARGUMENT_INVALID',
+ );
+ expect(() => parseRehearsalArguments([
+ '--admission', '/private/admission.json',
+ '--admission', '/private/duplicate.json',
+ '--receipt', '/private/rehearsal-receipt.json',
+ ])).toThrow('SUCCESSOR_REHEARSAL_ARGUMENT_INVALID');
+});
+
+test('compiled plan has one advisory lock, one outer transaction and eleven ledger writes', () => {
+ const admitted = admission();
+ const plan = compileSuccessorPlan(executableRecord, admitted);
+ expect(plan.transactionMode).toBe('single-outer-transaction');
+ expect(plan.sql.match(/pg_advisory_xact_lock\(7311754282260211::bigint\)/g)).toHaveLength(1);
+ expect(plan.sql.match(/INSERT INTO supabase_migrations\.schema_migrations/g)).toHaveLength(11);
+ expect(plan.sql.match(/SUCCESSOR_ADMISSION_EXPIRED/g)).toHaveLength(13);
+ expect(plan.sql.match(/set_config\('transaction_timeout'/g)).toHaveLength(1);
+ expect(plan.sql).toContain('transaction_timestamp()');
+ expect(plan.sql.match(new RegExp(SUCCESSOR_PRE_COMMIT_EXPIRY_MARKER, 'g'))).toHaveLength(1);
+ expect(statementSpans(plan.sql).some(span => /^(?:BEGIN|COMMIT|ROLLBACK)\b/i.test(span.token))).toBe(false);
+ const lockTimeout = plan.sql.indexOf("SET LOCAL lock_timeout='2s'");
+ const statementTimeout = plan.sql.indexOf("SET LOCAL statement_timeout='15s'");
+ const advisoryLock = plan.sql.indexOf('pg_advisory_xact_lock(7311754282260211::bigint)');
+ const expiryCheck = plan.sql.indexOf('SUCCESSOR_ADMISSION_EXPIRED');
+ const firstMigration = plan.sql.indexOf('INSERT INTO supabase_migrations.schema_migrations');
+ expect(lockTimeout).toBeGreaterThanOrEqual(0);
+ expect(statementTimeout).toBeGreaterThan(lockTimeout);
+ expect(advisoryLock).toBeGreaterThan(statementTimeout);
+ expect(expiryCheck).toBeGreaterThan(advisoryLock);
+ expect(firstMigration).toBeGreaterThan(expiryCheck);
+ const ledgerWrites = [...plan.sql.matchAll(/INSERT INTO supabase_migrations\.schema_migrations/g)]
+ .map(match => match.index ?? -1);
+ const expiryChecks = [...plan.sql.matchAll(/SUCCESSOR_ADMISSION_EXPIRED/g)]
+ .map(match => match.index ?? -1);
+ ledgerWrites.forEach((writeAt, index) => {
+ expect(expiryChecks[index + 1]).toBeGreaterThan(writeAt);
+ if (index + 1 < ledgerWrites.length) expect(expiryChecks[index + 1]).toBeLessThan(ledgerWrites[index + 1]);
+ });
+ const preCommitMarker = plan.sql.indexOf(SUCCESSOR_PRE_COMMIT_EXPIRY_MARKER);
+ expect(preCommitMarker).toBeGreaterThan(ledgerWrites.at(-1) ?? -1);
+ expect(expiryChecks.at(-1) ?? -1).toBeGreaterThan(preCommitMarker);
+ expect(statementSpans(plan.sql).at(-1)?.token).toContain('SUCCESSOR_ADMISSION_EXPIRED');
+ expect(plan.sql).toContain(`clock_timestamp() >= '${admitted.expiresAt}'::timestamptz`);
+ expect(plan.stateQuery).toContain('terminalContractReady');
+ expect(plan.stateQuery).toContain(manifest.terminalContract.finishPostimageSha256);
+ expect(plan.stateQuery).toContain("c.relkind IN('v','m') THEN pg_get_viewdef(c.oid,true)");
+ expect(plan.stateQuery).toContain("unnest(coalesce(c.reloptions,'{}'))");
+ expect(plan.stateQuery).toContain("unnest(coalesce(a.attacl,'{}'))");
+ expect(plan.stateQuery).toContain('m.inherit_option,m.set_option');
+ const storageState = plan.stateQuery.slice(
+ plan.stateQuery.indexOf('storage_material AS'),
+ plan.stateQuery.indexOf('contract AS'),
+ );
+ expect(storageState).toContain('lifecycle_configuration_generation');
+ expect(storageState).not.toContain('created_at');
+ expect(storageState).not.toContain('updated_at');
+});
+
+test('apply accepts rehearsal evidence completed after thirty seconds within its original lease', () => {
+ const document = admission();
+ document.createdAt = '2026-10-12T07:55:00.000Z';
+ document.expiresAt = '2026-10-12T08:10:00.000Z';
+ const files = custodyFiles(document);
+ rewriteRehearsalTimeline(files, {
+ startedAt: '2026-10-12T07:55:00.000Z',
+ observedAt: '2026-10-12T08:08:00.000Z',
+ verifiedAt: '2026-10-12T08:08:05.000Z',
+ publishedAt: '2026-10-12T08:08:10.000Z',
+ });
+ expect(validateSuccessorAdmission(files.document, executableRecord, {
+ now: new Date('2026-10-12T08:09:00.000Z'),
+ receiptDirectory: files.directory,
+ })).toMatchObject({
+ createdAt: Date.parse(document.createdAt),
+ expiresAt: Date.parse(document.expiresAt),
+ });
+});
+
+test('apply rejects rehearsal evidence outside the original fifteen-minute lease', () => {
+ const document = admission();
+ document.createdAt = '2026-10-12T07:55:00.000Z';
+ document.expiresAt = '2026-10-12T08:10:00.000Z';
+ const files = custodyFiles(document);
+ rewriteRehearsalTimeline(files, {
+ startedAt: '2026-10-12T07:54:59.999Z',
+ observedAt: '2026-10-12T08:08:00.000Z',
+ verifiedAt: '2026-10-12T08:08:05.000Z',
+ publishedAt: '2026-10-12T08:08:10.000Z',
+ });
+ expect(() => validateSuccessorAdmission(files.document, executableRecord, {
+ now: new Date('2026-10-12T08:09:00.000Z'),
+ receiptDirectory: files.directory,
+ })).toThrow('SUCCESSOR_ADMISSION_INVALID');
+});
+
+test('actual psql runner with a spy transport preserves the post-lock expiry code', () => {
+ let spawnCalls = 0;
+ const runner = createSuccessorPsqlRunner(databaseUrl, {
+ environment: {},
+ psql: '/fixture/psql',
+ spawnImpl: (command: string, args: string[], options: any) => {
+ spawnCalls += 1;
+ expect(command).toBe('/fixture/psql');
+ expect(args).toContain('--file=-');
+ expect(options.input).toContain('SUCCESSOR_ADMISSION_EXPIRED');
+ return {
+ status: 1,
+ stderr: 'ERROR: P0001: SUCCESSOR_ADMISSION_EXPIRED\n',
+ stdout: '',
+ };
+ },
+ });
+ let observedCode = '';
+ try {
+ runner(databaseUrl, "DO $$ BEGIN RAISE EXCEPTION 'SUCCESSOR_ADMISSION_EXPIRED'; END $$;", false);
+ } catch (error: any) {
+ observedCode = error?.code;
+ }
+ expect(spawnCalls).toBe(1);
+ expect(observedCode).toBe('SUCCESSOR_ADMISSION_EXPIRED');
+});
+
+test('rollback-only rehearsal plan owns one transaction and captures twelve safe stages', () => {
+ const admitted = rehearsalAdmission();
+ const stages = stageStates();
+ const plan = compileSuccessorRehearsalPlan(record, admitted, stages[0]);
+ expect(plan.transactionMode).toBe('explicit-rollback-only');
+ expect(plan.sql.match(/^BEGIN;$/gm)).toHaveLength(1);
+ expect(plan.sql.match(/^ROLLBACK;$/gm)).toHaveLength(1);
+ expect(plan.sql.trim().endsWith('ROLLBACK;')).toBe(true);
+ expect(plan.sql.match(/pg_advisory_xact_lock\(7311754282260211::bigint\)/g)).toHaveLength(1);
+ expect(plan.sql.match(/INSERT INTO supabase_migrations\.schema_migrations/g)).toHaveLength(11);
+ expect(plan.sql.match(/'successor-rehearsal-stage'/g)).toHaveLength(12);
+ expect(plan.sql.match(/^\\o \/dev\/null$/gm)).toHaveLength(11);
+ expect(plan.sql.match(/^\\o$/gm)).toHaveLength(11);
+ const lockTimeout = plan.sql.indexOf("SET LOCAL lock_timeout='2s'");
+ const statementTimeout = plan.sql.indexOf("SET LOCAL statement_timeout='15s'");
+ const advisoryLock = plan.sql.indexOf('pg_advisory_xact_lock(7311754282260211::bigint)');
+ const expiryCheck = plan.sql.indexOf('SUCCESSOR_ADMISSION_EXPIRED');
+ const baselineGuard = plan.sql.indexOf('SUCCESSOR_REHEARSAL_BASELINE_INVALID');
+ expect(lockTimeout).toBeGreaterThanOrEqual(0);
+ expect(statementTimeout).toBeGreaterThan(lockTimeout);
+ expect(advisoryLock).toBeGreaterThan(statementTimeout);
+ expect(expiryCheck).toBeGreaterThan(advisoryLock);
+ expect(baselineGuard).toBeGreaterThan(expiryCheck);
+ expect(plan.sql).toContain(`clock_timestamp() >= '${admitted.expiresAt}'::timestamptz`);
+ expect(plan.sql).toContain(manifest.migrations[0].originalStatementVector[1]);
+ expect(plan.sql).toContain(manifest.migrations.at(-1).originalStatementVector.at(-2));
+ expect(compileSuccessorStateReadQuery(record).sql).toContain('successor-rehearsal-readback');
+});
+
+test('rehearsal admission is distinct and rejects stale source or any wrong writer fence', () => {
+ {
+ const files = rehearsalCustodyFiles();
+ expect(validateSuccessorRehearsalAdmission(files.document, record, {
+ now: fixedNow,
+ receiptDirectory: files.directory,
+ })).toMatchObject({ expiresAt: Date.parse(files.document.expiresAt) });
+ }
+ {
+ const files = rehearsalCustodyFiles();
+ files.document.sourceRoot = '0'.repeat(64);
+ expect(() => validateSuccessorRehearsalAdmission(files.document, record, {
+ now: fixedNow,
+ receiptDirectory: files.directory,
+ })).toThrow('SUCCESSOR_ADMISSION_INVALID');
+ }
+ for (const key of Object.keys(manifest.requiredWriterFences)) {
+ const files = rehearsalCustodyFiles();
+ files.document.writerFences[key].state = 'wrong';
+ expect(() => validateSuccessorRehearsalAdmission(files.document, record, {
+ now: fixedNow,
+ receiptDirectory: files.directory,
+ })).toThrow('SUCCESSOR_ADMISSION_INVALID');
+ }
+});
+
+test('rehearsal rolls back, separately confirms restoration and emits an apply-bound receipt', () => {
+ const files = rehearsalCustodyFiles();
+ const calls: string[] = [];
+ let rollbackReads = 0;
+ const result = runReviewMediaSuccessorRehearsal({
+ admissionPath: files.admissionPath,
+ journalPath: files.journalPath,
+ receiptPath: files.receiptPath,
+ environment: { SUPABASE_DB_URL: databaseUrl },
+ }, {
+ now: () => fixedNow,
+ gitFactsImpl: () => ({ clean: true, detached: true, revision }),
+ loadManifestImpl: () => record,
+ protectedMainReadbackImpl: () => protectedSourceReadbackBinding(manifest, revision),
+ rollbackReadbackImpl: () => { rollbackReads += 1; return rollbackReadback(); },
+ runPsqlImpl: (_url, sql) => {
+ calls.push(sql);
+ if (calls.length === 2) return rehearsalStageOutput();
+ return rehearsalReadback();
+ },
+ });
+ expect(result).toMatchObject({
+ code: 'SUCCESSOR_REHEARSAL_PASSED',
+ publicationAuditReadback: 'confirmed',
+ publicationReadback: 'confirmed',
+ status: 'rolled_back',
+ });
+ expect(rollbackReads).toBe(2);
+ expect(calls).toHaveLength(3);
+ expect(calls[0]).toContain('READ ONLY');
+ expect(calls[1].match(/INSERT INTO supabase_migrations\.schema_migrations/g)).toHaveLength(11);
+ expect(calls[1].trim().endsWith('ROLLBACK;')).toBe(true);
+ expect(calls[2]).toContain('READ ONLY');
+ const receipt = JSON.parse(readFileSync(files.receiptPath, 'utf8'));
+ expect(receipt.stageStates).toEqual(stageStates());
+ expect(receipt.attemptId).toBe(files.document.attemptId);
+ expect(receipt.admissionSha256).toBe(sha256(readFileSync(files.admissionPath)));
+ expect(receipt.journalPathSha256).toBe(sha256(files.journalPath));
+ expect(receipt.preflightStateSha256).toBe(receipt.restoredStateSha256);
+ expect(receipt.stageStatesSha256).toBe(sha256(JSON.stringify(canonical(receipt.stageStates))));
+ const journal = readFileSync(files.journalPath, 'utf8').trim().split('\n').map(JSON.parse);
+ expect(journal).toHaveLength(3);
+ expect(journal[1]).toMatchObject({
+ admissionSha256: receipt.admissionSha256,
+ attemptId: receipt.attemptId,
+ code: 'SUCCESSOR_REHEARSAL_VERIFIED',
+ event: 'verification',
+ manifestSha256: record.manifestSha256,
+ receiptSha256: result.receiptSha256,
+ sourceRevision: revision,
+ sourceRoot: manifest.sourceRoot,
+ });
+ expect(journal[2]).toMatchObject({
+ admissionSha256: receipt.admissionSha256,
+ attemptId: receipt.attemptId,
+ code: 'SUCCESSOR_REHEARSAL_PUBLICATION_CONFIRMED',
+ event: 'publication',
+ manifestSha256: record.manifestSha256,
+ receiptSha256: result.receiptSha256,
+ sourceRevision: revision,
+ sourceRoot: manifest.sourceRoot,
+ });
+ expect(existsSync(files.pendingReceiptPath)).toBe(true);
+});
+
+test('lost rehearsal response performs restoration readback once and never resends', () => {
+ const files = rehearsalCustodyFiles();
+ const calls: string[] = [];
+ expect(() => runReviewMediaSuccessorRehearsal({
+ admissionPath: files.admissionPath,
+ journalPath: files.journalPath,
+ receiptPath: files.receiptPath,
+ environment: { SUPABASE_DB_URL: databaseUrl },
+ }, {
+ now: () => fixedNow,
+ gitFactsImpl: () => ({ clean: true, detached: true, revision }),
+ loadManifestImpl: () => record,
+ protectedMainReadbackImpl: () => protectedSourceReadbackBinding(manifest, revision),
+ rollbackReadbackImpl: () => rollbackReadback(),
+ runPsqlImpl: (_url, sql) => {
+ calls.push(sql);
+ if (calls.length === 2) throw new Error('lost response');
+ return rehearsalReadback();
+ },
+ })).toThrow('SUCCESSOR_REHEARSAL_OUTCOME_UNCONFIRMED');
+ expect(calls).toHaveLength(3);
+ expect(calls.filter(sql => /INSERT INTO supabase_migrations\.schema_migrations/.test(sql))).toHaveLength(1);
+ expect(() => readFileSync(files.receiptPath)).toThrow();
+ expect(readFileSync(files.journalPath, 'utf8')).toContain('SUCCESSOR_REHEARSAL_OUTCOME_UNCONFIRMED');
+});
+
+test('post-lock rehearsal expiry keeps its exact bounded code after restoration readback', () => {
+ const files = rehearsalCustodyFiles();
+ let calls = 0;
+ expect(() => runReviewMediaSuccessorRehearsal({
+ admissionPath: files.admissionPath,
+ journalPath: files.journalPath,
+ receiptPath: files.receiptPath,
+ environment: { SUPABASE_DB_URL: databaseUrl },
+ }, {
+ now: () => fixedNow,
+ gitFactsImpl: () => ({ clean: true, detached: true, revision }),
+ loadManifestImpl: () => record,
+ protectedMainReadbackImpl: () => protectedSourceReadbackBinding(manifest, revision),
+ rollbackReadbackImpl: () => rollbackReadback(),
+ runPsqlImpl: () => {
+ calls += 1;
+ if (calls === 2) {
+ throw Object.assign(new Error('provider diagnostic must not escape'), {
+ code: 'SUCCESSOR_ADMISSION_EXPIRED',
+ });
+ }
+ return rehearsalReadback();
+ },
+ })).toThrow('SUCCESSOR_ADMISSION_EXPIRED');
+ expect(calls).toBe(3);
+ const journal = readFileSync(files.journalPath, 'utf8');
+ expect(journal).toContain('SUCCESSOR_ADMISSION_EXPIRED');
+ expect(journal).not.toContain('SUCCESSOR_REHEARSAL_OUTCOME_UNCONFIRMED');
+});
+
+test('incomplete rehearsal output cannot create an apply admission receipt', () => {
+ const files = rehearsalCustodyFiles();
+ let calls = 0;
+ expect(() => runReviewMediaSuccessorRehearsal({
+ admissionPath: files.admissionPath,
+ journalPath: files.journalPath,
+ receiptPath: files.receiptPath,
+ environment: { SUPABASE_DB_URL: databaseUrl },
+ }, {
+ now: () => fixedNow,
+ gitFactsImpl: () => ({ clean: true, detached: true, revision }),
+ loadManifestImpl: () => record,
+ protectedMainReadbackImpl: () => protectedSourceReadbackBinding(manifest, revision),
+ rollbackReadbackImpl: () => rollbackReadback(),
+ runPsqlImpl: () => {
+ calls += 1;
+ if (calls === 2) return rehearsalStageOutput().split('\n').slice(0, 11).join('\n');
+ return rehearsalReadback();
+ },
+ })).toThrow('SUCCESSOR_REHEARSAL_OUTPUT_INVALID');
+ expect(calls).toBe(3);
+ expect(() => readFileSync(files.receiptPath)).toThrow();
+});
+
+test('verification line write-success then fsync-failure never publishes the canonical receipt', () => {
+ const files = rehearsalCustodyFiles();
+ let calls = 0;
+ expect(() => runReviewMediaSuccessorRehearsal({
+ admissionPath: files.admissionPath,
+ journalPath: files.journalPath,
+ receiptPath: files.receiptPath,
+ environment: { SUPABASE_DB_URL: databaseUrl },
+ }, {
+ now: () => fixedNow,
+ gitFactsImpl: () => ({ clean: true, detached: true, revision }),
+ loadManifestImpl: () => record,
+ protectedMainReadbackImpl: () => protectedSourceReadbackBinding(manifest, revision),
+ rollbackReadbackImpl: () => rollbackReadback(),
+ runPsqlImpl: () => {
+ calls += 1;
+ return calls === 2 ? rehearsalStageOutput() : rehearsalReadback();
+ },
+ appendJournalImpl: (descriptor: number, entry: any) => {
+ const bytes = Buffer.from(`${JSON.stringify(canonical(entry))}\n`);
+ expect(writeSync(descriptor, bytes)).toBe(bytes.length);
+ throw new Error('verification fsync failed after full write');
+ },
+ })).toThrow('SUCCESSOR_JOURNAL_APPEND_FAILED');
+ expect(existsSync(files.receiptPath)).toBe(false);
+ expect(JSON.parse(readFileSync(files.pendingReceiptPath, 'utf8')).passed).toBe(true);
+ const orphanJournal = readFileSync(files.journalPath);
+ const journalEntries = orphanJournal.toString('utf8').trim().split('\n').map(JSON.parse);
+ expect(journalEntries).toHaveLength(2);
+ expect(journalEntries[1]).toMatchObject({
+ code: 'SUCCESSOR_REHEARSAL_VERIFIED',
+ event: 'verification',
+ });
+
+ const applyFiles = custodyFiles();
+ writeFileSync(applyFiles.rehearsalJournalPath, orphanJournal, { mode: 0o600 });
+ applyFiles.document.rehearsalJournalSha256 = sha256(orphanJournal);
+ expect(() => validateSuccessorAdmission(applyFiles.document, executableRecord, {
+ now: fixedNow,
+ receiptDirectory: applyFiles.directory,
+ })).toThrow('SUCCESSOR_ADMISSION_INVALID');
+});
+
+test('pending receipt full-write then fsync-failure records failure without canonical publication', () => {
+ const files = rehearsalCustodyFiles();
+ let calls = 0;
+ expect(() => runReviewMediaSuccessorRehearsal({
+ admissionPath: files.admissionPath,
+ journalPath: files.journalPath,
+ receiptPath: files.receiptPath,
+ environment: { SUPABASE_DB_URL: databaseUrl },
+ }, {
+ now: () => fixedNow,
+ gitFactsImpl: () => ({ clean: true, detached: true, revision }),
+ loadManifestImpl: () => record,
+ protectedMainReadbackImpl: () => protectedSourceReadbackBinding(manifest, revision),
+ rollbackReadbackImpl: () => rollbackReadback(),
+ runPsqlImpl: () => {
+ calls += 1;
+ return calls === 2 ? rehearsalStageOutput() : rehearsalReadback();
+ },
+ writePendingReceiptImpl: (path: string, receipt: any) => {
+ const descriptor = openSync(path, 'wx', 0o600);
+ const bytes = canonicalBytes(receipt);
+ expect(writeSync(descriptor, bytes)).toBe(bytes.length);
+ closeSync(descriptor);
+ throw new Error('pending receipt fsync failed after full write');
+ },
+ })).toThrow('SUCCESSOR_REHEARSAL_RECEIPT_WRITE_FAILED');
+ expect(existsSync(files.receiptPath)).toBe(false);
+ expect(JSON.parse(readFileSync(files.pendingReceiptPath, 'utf8')).passed).toBe(true);
+ const journal = readFileSync(files.journalPath, 'utf8');
+ expect(journal).toContain('SUCCESSOR_REHEARSAL_RECEIPT_WRITE_FAILED');
+ expect(journal).not.toContain('SUCCESSOR_REHEARSAL_VERIFIED');
+ const applyFiles = custodyFiles();
+ const failedJournal = Buffer.from(journal);
+ writeFileSync(applyFiles.rehearsalJournalPath, failedJournal, { mode: 0o600 });
+ applyFiles.document.rehearsalJournalSha256 = sha256(failedJournal);
+ expect(() => validateSuccessorAdmission(applyFiles.document, executableRecord, {
+ now: fixedNow,
+ receiptDirectory: applyFiles.directory,
+ })).toThrow('SUCCESSOR_ADMISSION_INVALID');
+});
+
+test('pending receipt storage delay cannot verify after the admission expires', () => {
+ const document = rehearsalAdmission();
+ const expiresAt = new Date(fixedNow.getTime() + 1_000);
+ document.expiresAt = expiresAt.toISOString();
+ const files = rehearsalCustodyFiles(document);
+ let clock = fixedNow;
+ let calls = 0;
+ expect(() => runReviewMediaSuccessorRehearsal({
+ admissionPath: files.admissionPath,
+ journalPath: files.journalPath,
+ receiptPath: files.receiptPath,
+ environment: { SUPABASE_DB_URL: databaseUrl },
+ }, {
+ now: () => clock,
+ gitFactsImpl: () => ({ clean: true, detached: true, revision }),
+ loadManifestImpl: () => record,
+ protectedMainReadbackImpl: () => protectedSourceReadbackBinding(manifest, revision),
+ rollbackReadbackImpl: () => rollbackReadback(),
+ runPsqlImpl: () => {
+ calls += 1;
+ return calls === 2 ? rehearsalStageOutput() : rehearsalReadback();
+ },
+ writePendingReceiptImpl: (path: string, receipt: any) => {
+ const descriptor = openSync(path, 'wx', 0o600);
+ const bytes = canonicalBytes(receipt);
+ expect(writeSync(descriptor, bytes)).toBe(bytes.length);
+ fsyncSync(descriptor);
+ closeSync(descriptor);
+ clock = expiresAt;
+ },
+ })).toThrow('SUCCESSOR_ADMISSION_EXPIRED');
+ expect(existsSync(files.pendingReceiptPath)).toBe(true);
+ expect(existsSync(files.receiptPath)).toBe(false);
+ const journal = readFileSync(files.journalPath, 'utf8');
+ expect(journal).toContain('SUCCESSOR_ADMISSION_EXPIRED');
+ expect(journal).not.toContain('SUCCESSOR_REHEARSAL_VERIFIED');
+ expect(journal).not.toContain('SUCCESSOR_REHEARSAL_PUBLICATION_CONFIRMED');
+});
+
+test('expiry terminal audit failure preserves the bounded rehearsal outcome', () => {
+ const document = rehearsalAdmission();
+ const expiresAt = new Date(fixedNow.getTime() + 1_000);
+ document.expiresAt = expiresAt.toISOString();
+ const files = rehearsalCustodyFiles(document);
+ let clock = fixedNow;
+ let calls = 0;
+ let observed: any;
+ try {
+ runReviewMediaSuccessorRehearsal({
+ admissionPath: files.admissionPath,
+ journalPath: files.journalPath,
+ receiptPath: files.receiptPath,
+ environment: { SUPABASE_DB_URL: databaseUrl },
+ }, {
+ now: () => clock,
+ gitFactsImpl: () => ({ clean: true, detached: true, revision }),
+ loadManifestImpl: () => record,
+ protectedMainReadbackImpl: () => protectedSourceReadbackBinding(manifest, revision),
+ rollbackReadbackImpl: () => rollbackReadback(),
+ runPsqlImpl: () => {
+ calls += 1;
+ return calls === 2 ? rehearsalStageOutput() : rehearsalReadback();
+ },
+ writePendingReceiptImpl: (path: string, receipt: any) => {
+ writeFileSync(path, canonicalBytes(receipt), { flag: 'wx', mode: 0o600 });
+ clock = expiresAt;
+ },
+ appendJournalImpl: () => { throw new Error('fixture expiry audit unavailable'); },
+ });
+ } catch (error) {
+ observed = successorFailureResult(error);
+ }
+ expect(observed).toEqual({
+ code: 'SUCCESSOR_JOURNAL_APPEND_FAILED',
+ operationCode: 'SUCCESSOR_ADMISSION_EXPIRED',
+ status: 'failed',
+ });
+ expect(existsSync(files.receiptPath)).toBe(false);
+});
+
+test('verification journal storage delay cannot publish after the admission expires', () => {
+ const document = rehearsalAdmission();
+ const expiresAt = new Date(fixedNow.getTime() + 1_000);
+ document.expiresAt = expiresAt.toISOString();
+ const files = rehearsalCustodyFiles(document);
+ let clock = fixedNow;
+ let calls = 0;
+ let appendCalls = 0;
+ expect(() => runReviewMediaSuccessorRehearsal({
+ admissionPath: files.admissionPath,
+ journalPath: files.journalPath,
+ receiptPath: files.receiptPath,
+ environment: { SUPABASE_DB_URL: databaseUrl },
+ }, {
+ now: () => clock,
+ gitFactsImpl: () => ({ clean: true, detached: true, revision }),
+ loadManifestImpl: () => record,
+ protectedMainReadbackImpl: () => protectedSourceReadbackBinding(manifest, revision),
+ rollbackReadbackImpl: () => rollbackReadback(),
+ runPsqlImpl: () => {
+ calls += 1;
+ return calls === 2 ? rehearsalStageOutput() : rehearsalReadback();
+ },
+ appendJournalImpl: (descriptor: number, entry: any, options: any) => {
+ appendCalls += 1;
+ const result = appendJournal(descriptor, entry, options);
+ if (appendCalls === 1) clock = expiresAt;
+ return result;
+ },
+ })).toThrow('SUCCESSOR_ADMISSION_EXPIRED');
+ expect(appendCalls).toBe(2);
+ expect(existsSync(files.pendingReceiptPath)).toBe(true);
+ expect(existsSync(files.receiptPath)).toBe(false);
+ const journal = readFileSync(files.journalPath, 'utf8');
+ expect(journal).toContain('SUCCESSOR_REHEARSAL_VERIFIED');
+ expect(journal).toContain('SUCCESSOR_ADMISSION_EXPIRED');
+ expect(journal).not.toContain('SUCCESSOR_REHEARSAL_PUBLICATION_CONFIRMED');
+});
+
+test('canonical publication storage delay cannot record an expired passed result', () => {
+ const document = rehearsalAdmission();
+ const expiresAt = new Date(fixedNow.getTime() + 1_000);
+ document.expiresAt = expiresAt.toISOString();
+ const files = rehearsalCustodyFiles(document);
+ let clock = fixedNow;
+ let calls = 0;
+ expect(() => runReviewMediaSuccessorRehearsal({
+ admissionPath: files.admissionPath,
+ journalPath: files.journalPath,
+ receiptPath: files.receiptPath,
+ environment: { SUPABASE_DB_URL: databaseUrl },
+ }, {
+ now: () => clock,
+ gitFactsImpl: () => ({ clean: true, detached: true, revision }),
+ loadManifestImpl: () => record,
+ protectedMainReadbackImpl: () => protectedSourceReadbackBinding(manifest, revision),
+ rollbackReadbackImpl: () => rollbackReadback(),
+ runPsqlImpl: () => {
+ calls += 1;
+ return calls === 2 ? rehearsalStageOutput() : rehearsalReadback();
+ },
+ publishReceiptImpl: (pendingPath: string, receiptPath: string) => {
+ linkSync(pendingPath, receiptPath);
+ const directoryDescriptor = openSync(files.directory, 'r');
+ fsyncSync(directoryDescriptor);
+ closeSync(directoryDescriptor);
+ clock = expiresAt;
+ },
+ })).toThrow('SUCCESSOR_ADMISSION_EXPIRED');
+ expect(readFileSync(files.receiptPath)).toEqual(readFileSync(files.pendingReceiptPath));
+ const journal = readFileSync(files.journalPath, 'utf8');
+ expect(journal).toContain('SUCCESSOR_REHEARSAL_VERIFIED');
+ expect(journal).toContain('SUCCESSOR_ADMISSION_EXPIRED');
+ expect(journal).not.toContain('SUCCESSOR_REHEARSAL_PUBLICATION_CONFIRMED');
+});
+
+test('publication journal completion cannot return passed after the admission expires', () => {
+ const document = rehearsalAdmission();
+ const expiresAt = new Date(fixedNow.getTime() + 1_000);
+ document.expiresAt = expiresAt.toISOString();
+ const files = rehearsalCustodyFiles(document);
+ let clock = fixedNow;
+ let calls = 0;
+ let appendCalls = 0;
+ expect(() => runReviewMediaSuccessorRehearsal({
+ admissionPath: files.admissionPath,
+ journalPath: files.journalPath,
+ receiptPath: files.receiptPath,
+ environment: { SUPABASE_DB_URL: databaseUrl },
+ }, {
+ now: () => clock,
+ gitFactsImpl: () => ({ clean: true, detached: true, revision }),
+ loadManifestImpl: () => record,
+ protectedMainReadbackImpl: () => protectedSourceReadbackBinding(manifest, revision),
+ rollbackReadbackImpl: () => rollbackReadback(),
+ runPsqlImpl: () => {
+ calls += 1;
+ return calls === 2 ? rehearsalStageOutput() : rehearsalReadback();
+ },
+ appendJournalImpl: (descriptor: number, entry: any, options: any) => {
+ appendCalls += 1;
+ const result = appendJournal(descriptor, entry, options);
+ if (appendCalls === 2) clock = expiresAt;
+ return result;
+ },
+ })).toThrow('SUCCESSOR_ADMISSION_EXPIRED');
+ expect(appendCalls).toBe(2);
+ expect(readFileSync(files.receiptPath)).toEqual(readFileSync(files.pendingReceiptPath));
+ const journal = readFileSync(files.journalPath, 'utf8').trim().split('\n').map(JSON.parse);
+ expect(journal).toHaveLength(3);
+ expect(journal[2].code).toBe('SUCCESSOR_REHEARSAL_PUBLICATION_CONFIRMED');
+});
+
+test('prepublication interruption leaves verified journal and pending receipt apply-invalid', () => {
+ const files = rehearsalCustodyFiles();
+ let calls = 0;
+ let appendCalls = 0;
+ expect(() => runReviewMediaSuccessorRehearsal({
+ admissionPath: files.admissionPath,
+ journalPath: files.journalPath,
+ receiptPath: files.receiptPath,
+ environment: { SUPABASE_DB_URL: databaseUrl },
+ }, {
+ now: () => fixedNow,
+ gitFactsImpl: () => ({ clean: true, detached: true, revision }),
+ loadManifestImpl: () => record,
+ protectedMainReadbackImpl: () => protectedSourceReadbackBinding(manifest, revision),
+ rollbackReadbackImpl: () => rollbackReadback(),
+ runPsqlImpl: () => {
+ calls += 1;
+ return calls === 2 ? rehearsalStageOutput() : rehearsalReadback();
+ },
+ appendJournalImpl: (descriptor: number, entry: any) => {
+ appendCalls += 1;
+ if (appendCalls > 1) throw new Error('interrupted before failure audit');
+ const bytes = Buffer.from(`${JSON.stringify(canonical(entry))}\n`);
+ expect(writeSync(descriptor, bytes)).toBe(bytes.length);
+ fsyncSync(descriptor);
+ },
+ publishReceiptImpl: () => { throw new Error('interrupted before atomic publication'); },
+ })).toThrow('SUCCESSOR_JOURNAL_APPEND_FAILED');
+ expect(appendCalls).toBe(2);
+ expect(existsSync(files.pendingReceiptPath)).toBe(true);
+ expect(existsSync(files.receiptPath)).toBe(false);
+ expect(readFileSync(files.journalPath, 'utf8')).toContain('SUCCESSOR_REHEARSAL_VERIFIED');
+});
+
+test('failed canonical publication is audited as failure, never as passed', () => {
+ const files = rehearsalCustodyFiles();
+ let calls = 0;
+ expect(() => runReviewMediaSuccessorRehearsal({
+ admissionPath: files.admissionPath,
+ journalPath: files.journalPath,
+ receiptPath: files.receiptPath,
+ environment: { SUPABASE_DB_URL: databaseUrl },
+ }, {
+ now: () => fixedNow,
+ gitFactsImpl: () => ({ clean: true, detached: true, revision }),
+ loadManifestImpl: () => record,
+ protectedMainReadbackImpl: () => protectedSourceReadbackBinding(manifest, revision),
+ rollbackReadbackImpl: () => rollbackReadback(),
+ runPsqlImpl: () => {
+ calls += 1;
+ return calls === 2 ? rehearsalStageOutput() : rehearsalReadback();
+ },
+ publishReceiptImpl: () => { throw new Error('canonical publication failed'); },
+ })).toThrow('SUCCESSOR_REHEARSAL_RECEIPT_PUBLISH_FAILED');
+ expect(existsSync(files.pendingReceiptPath)).toBe(true);
+ expect(existsSync(files.receiptPath)).toBe(false);
+ const journal = readFileSync(files.journalPath, 'utf8');
+ expect(journal).toContain('SUCCESSOR_REHEARSAL_VERIFIED');
+ expect(journal).toContain('SUCCESSOR_REHEARSAL_RECEIPT_PUBLISH_FAILED');
+ expect(journal).not.toContain('SUCCESSOR_REHEARSAL_PASSED');
+});
+
+test('pre-existing exact canonical bytes do not make an unperformed link durable', () => {
+ const files = rehearsalCustodyFiles();
+ let calls = 0;
+ expect(() => runReviewMediaSuccessorRehearsal({
+ admissionPath: files.admissionPath,
+ journalPath: files.journalPath,
+ receiptPath: files.receiptPath,
+ environment: { SUPABASE_DB_URL: databaseUrl },
+ }, {
+ now: () => fixedNow,
+ gitFactsImpl: () => ({ clean: true, detached: true, revision }),
+ loadManifestImpl: () => record,
+ protectedMainReadbackImpl: () => protectedSourceReadbackBinding(manifest, revision),
+ rollbackReadbackImpl: () => rollbackReadback(),
+ runPsqlImpl: () => {
+ calls += 1;
+ return calls === 2 ? rehearsalStageOutput() : rehearsalReadback();
+ },
+ writePendingReceiptImpl: (pendingPath: string, receipt: any) => {
+ const bytes = canonicalBytes(receipt);
+ writeFileSync(pendingPath, bytes, { flag: 'wx', mode: 0o600 });
+ writeFileSync(files.receiptPath, bytes, { flag: 'wx', mode: 0o600 });
+ },
+ })).toThrow('SUCCESSOR_REHEARSAL_RECEIPT_PUBLISH_FAILED');
+ expect(readFileSync(files.receiptPath)).toEqual(readFileSync(files.pendingReceiptPath));
+ const journal = readFileSync(files.journalPath, 'utf8');
+ expect(journal).toContain('SUCCESSOR_REHEARSAL_RECEIPT_PUBLISH_FAILED');
+ expect(journal).not.toContain('SUCCESSOR_REHEARSAL_PUBLICATION_CONFIRMED');
+});
+
+test('unconfirmed directory durability cannot be reconciled from visible canonical bytes', () => {
+ const files = rehearsalCustodyFiles();
+ let calls = 0;
+ expect(() => runReviewMediaSuccessorRehearsal({
+ admissionPath: files.admissionPath,
+ journalPath: files.journalPath,
+ receiptPath: files.receiptPath,
+ environment: { SUPABASE_DB_URL: databaseUrl },
+ }, {
+ now: () => fixedNow,
+ gitFactsImpl: () => ({ clean: true, detached: true, revision }),
+ loadManifestImpl: () => record,
+ protectedMainReadbackImpl: () => protectedSourceReadbackBinding(manifest, revision),
+ rollbackReadbackImpl: () => rollbackReadback(),
+ runPsqlImpl: () => {
+ calls += 1;
+ return calls === 2 ? rehearsalStageOutput() : rehearsalReadback();
+ },
+ publishReceiptImpl: (pendingPath: string, receiptPath: string) => {
+ linkSync(pendingPath, receiptPath);
+ throw new Error('directory fsync not confirmed');
+ },
+ })).toThrow('SUCCESSOR_REHEARSAL_RECEIPT_PUBLISH_FAILED');
+ expect(existsSync(files.receiptPath)).toBe(true);
+ const journal = readFileSync(files.journalPath, 'utf8');
+ expect(journal).toContain('SUCCESSOR_REHEARSAL_RECEIPT_PUBLISH_FAILED');
+
+ const applyFiles = custodyFiles();
+ const entries = readFileSync(applyFiles.rehearsalJournalPath, 'utf8').trim().split('\n').map(JSON.parse);
+ entries.push({
+ attemptId: entries[0].attemptId,
+ code: 'SUCCESSOR_REHEARSAL_RECEIPT_PUBLISH_FAILED',
+ completedAt: fixedNow.toISOString(),
+ event: 'terminal',
+ });
+ const failed = Buffer.from(entries.map(entry => JSON.stringify(canonical(entry))).join('\n') + '\n');
+ writeFileSync(applyFiles.rehearsalJournalPath, failed, { mode: 0o600 });
+ applyFiles.document.rehearsalJournalSha256 = sha256(failed);
+ expect(() => validateSuccessorAdmission(applyFiles.document, executableRecord, {
+ now: fixedNow,
+ receiptDirectory: applyFiles.directory,
+ })).toThrow('SUCCESSOR_ADMISSION_INVALID');
+});
+
+test('directory fsync failure plus failure-audit loss leaves visible receipt apply-invalid', () => {
+ const files = rehearsalCustodyFiles();
+ let transportCalls = 0;
+ let appendCalls = 0;
+ expect(() => runReviewMediaSuccessorRehearsal({
+ admissionPath: files.admissionPath,
+ journalPath: files.journalPath,
+ receiptPath: files.receiptPath,
+ environment: { SUPABASE_DB_URL: databaseUrl },
+ }, {
+ now: () => fixedNow,
+ gitFactsImpl: () => ({ clean: true, detached: true, revision }),
+ loadManifestImpl: () => record,
+ protectedMainReadbackImpl: () => protectedSourceReadbackBinding(manifest, revision),
+ rollbackReadbackImpl: () => rollbackReadback(),
+ runPsqlImpl: () => {
+ transportCalls += 1;
+ return transportCalls === 2 ? rehearsalStageOutput() : rehearsalReadback();
+ },
+ appendJournalImpl: (descriptor: number, entry: any) => {
+ appendCalls += 1;
+ if (appendCalls === 1) return writeCanonicalLine(descriptor, entry);
+ throw new Error('failure audit unavailable after directory fsync failure');
+ },
+ publishReceiptImpl: (pendingPath: string, receiptPath: string) => {
+ linkSync(pendingPath, receiptPath);
+ const error: Error & { publicationReadbackEligible?: boolean } = new Error(
+ 'directory fsync failed after canonical link',
+ );
+ error.publicationReadbackEligible = false;
+ throw error;
+ },
+ })).toThrow('SUCCESSOR_JOURNAL_APPEND_FAILED');
+ expect(appendCalls).toBe(2);
+ expect(readFileSync(files.receiptPath)).toEqual(readFileSync(files.pendingReceiptPath));
+ const orphanJournal = readFileSync(files.journalPath);
+ expect(orphanJournal.toString('utf8').trim().split('\n')).toHaveLength(2);
+
+ const applyDocument = admission();
+ applyDocument.createdAt = fixedNow.toISOString();
+ const applyFiles = custodyFiles(applyDocument);
+ const rehearsalReceipt = readFileSync(files.receiptPath);
+ writeFileSync(join(applyFiles.directory, 'rehearsal-receipt.json'), rehearsalReceipt, { mode: 0o600 });
+ writeFileSync(applyFiles.rehearsalJournalPath, orphanJournal, { mode: 0o600 });
+ applyFiles.document.rehearsalReceiptSha256 = sha256(rehearsalReceipt);
+ applyFiles.document.rehearsalJournalSha256 = sha256(orphanJournal);
+ expect(() => validateSuccessorAdmission(applyFiles.document, executableRecord, {
+ now: fixedNow,
+ receiptDirectory: applyFiles.directory,
+ })).toThrow('SUCCESSOR_ADMISSION_INVALID');
+});
+
+for (const failureAt of ['fsync', 'close'] as const) {
+ test(`publication audit ${failureAt} failure requires proven durability before reconciliation`, () => {
+ const files = rehearsalCustodyFiles();
+ let transportCalls = 0;
+ let appendCalls = 0;
+ const run = () => runReviewMediaSuccessorRehearsal({
+ admissionPath: files.admissionPath,
+ journalPath: files.journalPath,
+ receiptPath: files.receiptPath,
+ environment: { SUPABASE_DB_URL: databaseUrl },
+ }, {
+ now: () => fixedNow,
+ gitFactsImpl: () => ({ clean: true, detached: true, revision }),
+ loadManifestImpl: () => record,
+ protectedMainReadbackImpl: () => protectedSourceReadbackBinding(manifest, revision),
+ rollbackReadbackImpl: () => rollbackReadback(),
+ runPsqlImpl: () => {
+ transportCalls += 1;
+ return transportCalls === 2 ? rehearsalStageOutput() : rehearsalReadback();
+ },
+ appendJournalImpl: (descriptor: number, entry: any, options: any) => {
+ appendCalls += 1;
+ if (appendCalls === 1) return appendJournal(descriptor, entry, options);
+ return appendJournal(descriptor, entry, {
+ fsyncImpl: (fd: number) => {
+ if (failureAt === 'fsync') throw new Error('fixture sync failure');
+ fsyncSync(fd);
+ },
+ closeImpl: () => { throw new Error('fixture close acknowledgement loss'); },
+ });
+ },
+ });
+ if (failureAt === 'fsync') expect(run).toThrow('SUCCESSOR_JOURNAL_APPEND_FAILED');
+ else expect(run()).toMatchObject({
+ code: 'SUCCESSOR_REHEARSAL_PASSED',
+ publicationAuditReadback: 'reconciled',
+ publicationReadback: 'confirmed',
+ });
+ expect(transportCalls).toBe(3);
+ expect(appendCalls).toBe(2);
+ // Both failures leave visible bytes; only the completed fsync can reconcile.
+ const journal = readFileSync(files.journalPath, 'utf8').trim().split('\n').map(JSON.parse);
+ expect(journal).toHaveLength(3);
+ expect(journal[2].code).toBe('SUCCESSOR_REHEARSAL_PUBLICATION_CONFIRMED');
+ });
+}
+
+test('publication audit failure before any bytes leaves durable receipt apply-invalid', () => {
+ const files = rehearsalCustodyFiles();
+ let transportCalls = 0;
+ let appendCalls = 0;
+ expect(() => runReviewMediaSuccessorRehearsal({
+ admissionPath: files.admissionPath,
+ journalPath: files.journalPath,
+ receiptPath: files.receiptPath,
+ environment: { SUPABASE_DB_URL: databaseUrl },
+ }, {
+ now: () => fixedNow,
+ gitFactsImpl: () => ({ clean: true, detached: true, revision }),
+ loadManifestImpl: () => record,
+ protectedMainReadbackImpl: () => protectedSourceReadbackBinding(manifest, revision),
+ rollbackReadbackImpl: () => rollbackReadback(),
+ runPsqlImpl: () => {
+ transportCalls += 1;
+ return transportCalls === 2 ? rehearsalStageOutput() : rehearsalReadback();
+ },
+ appendJournalImpl: (descriptor: number, entry: any) => {
+ appendCalls += 1;
+ if (appendCalls === 1) return writeCanonicalLine(descriptor, entry);
+ throw new Error('publication audit unavailable before write');
+ },
+ })).toThrow('SUCCESSOR_JOURNAL_APPEND_FAILED');
+ expect(appendCalls).toBe(2);
+ expect(readFileSync(files.receiptPath)).toEqual(readFileSync(files.pendingReceiptPath));
+ const journal = readFileSync(files.journalPath);
+ expect(journal.toString('utf8').trim().split('\n')).toHaveLength(2);
+
+ const applyFiles = custodyFiles();
+ const validEntries = readFileSync(applyFiles.rehearsalJournalPath, 'utf8')
+ .trim().split('\n').map(JSON.parse);
+ const missingPublication = Buffer.from(validEntries.slice(0, 2)
+ .map(entry => JSON.stringify(canonical(entry))).join('\n') + '\n');
+ writeFileSync(applyFiles.rehearsalJournalPath, missingPublication, { mode: 0o600 });
+ applyFiles.document.rehearsalJournalSha256 = sha256(missingPublication);
+ expect(() => validateSuccessorAdmission(applyFiles.document, executableRecord, {
+ now: fixedNow,
+ receiptDirectory: applyFiles.directory,
+ })).toThrow('SUCCESSOR_ADMISSION_INVALID');
+});
+
+test('atomic publication acknowledgement loss reconciles exact receipt without retry', () => {
+ const files = rehearsalCustodyFiles();
+ let calls = 0;
+ let publishCalls = 0;
+ const result = runReviewMediaSuccessorRehearsal({
+ admissionPath: files.admissionPath,
+ journalPath: files.journalPath,
+ receiptPath: files.receiptPath,
+ environment: { SUPABASE_DB_URL: databaseUrl },
+ }, {
+ now: () => fixedNow,
+ gitFactsImpl: () => ({ clean: true, detached: true, revision }),
+ loadManifestImpl: () => record,
+ protectedMainReadbackImpl: () => protectedSourceReadbackBinding(manifest, revision),
+ rollbackReadbackImpl: () => rollbackReadback(),
+ runPsqlImpl: () => {
+ calls += 1;
+ return calls === 2 ? rehearsalStageOutput() : rehearsalReadback();
+ },
+ publishReceiptImpl: (pendingPath: string, receiptPath: string) => {
+ publishCalls += 1;
+ linkSync(pendingPath, receiptPath);
+ const directoryDescriptor = openSync(files.directory, 'r');
+ fsyncSync(directoryDescriptor);
+ closeSync(directoryDescriptor);
+ const error: Error & { publicationReadbackEligible?: boolean } = new Error(
+ 'publication acknowledgement lost',
+ );
+ error.publicationReadbackEligible = true;
+ throw error;
+ },
+ });
+ expect(publishCalls).toBe(1);
+ expect(result).toMatchObject({
+ code: 'SUCCESSOR_REHEARSAL_PASSED',
+ publicationAuditReadback: 'confirmed',
+ publicationReadback: 'reconciled',
+ });
+ expect(readFileSync(files.receiptPath)).toEqual(readFileSync(files.pendingReceiptPath));
+});
+
+test('apply rejects orphan and semantically tampered rehearsal journals even when rehashed', () => {
+ {
+ const files = custodyFiles();
+ const firstLine = readFileSync(files.rehearsalJournalPath, 'utf8').split('\n')[0];
+ const orphan = Buffer.from(`${firstLine}\n`);
+ writeFileSync(files.rehearsalJournalPath, orphan, { mode: 0o600 });
+ files.document.rehearsalJournalSha256 = sha256(orphan);
+ expect(() => validateSuccessorAdmission(files.document, executableRecord, {
+ now: fixedNow,
+ receiptDirectory: files.directory,
+ })).toThrow('SUCCESSOR_ADMISSION_INVALID');
+ }
+ {
+ const files = custodyFiles();
+ const entries = readFileSync(files.rehearsalJournalPath, 'utf8').trim().split('\n').map(JSON.parse);
+ entries[1].receiptSha256 = '0'.repeat(64);
+ const tampered = Buffer.from(entries.map(entry => JSON.stringify(canonical(entry))).join('\n') + '\n');
+ writeFileSync(files.rehearsalJournalPath, tampered, { mode: 0o600 });
+ files.document.rehearsalJournalSha256 = sha256(tampered);
+ expect(() => validateSuccessorAdmission(files.document, executableRecord, {
+ now: fixedNow,
+ receiptDirectory: files.directory,
+ })).toThrow('SUCCESSOR_ADMISSION_INVALID');
+ }
+ {
+ const files = custodyFiles();
+ const entries = readFileSync(files.rehearsalJournalPath, 'utf8').trim().split('\n').map(JSON.parse);
+ entries[2] = {
+ attemptId: entries[0].attemptId,
+ code: 'SUCCESSOR_REHEARSAL_RECEIPT_PUBLISH_FAILED',
+ completedAt: fixedNow.toISOString(),
+ event: 'terminal',
+ };
+ const failed = Buffer.from(entries.map(entry => JSON.stringify(canonical(entry))).join('\n') + '\n');
+ writeFileSync(files.rehearsalJournalPath, failed, { mode: 0o600 });
+ files.document.rehearsalJournalSha256 = sha256(failed);
+ expect(() => validateSuccessorAdmission(files.document, executableRecord, {
+ now: fixedNow,
+ receiptDirectory: files.directory,
+ })).toThrow('SUCCESSOR_ADMISSION_INVALID');
+ }
+});
+
+test('apply pins the stored admission against a valid fully rehashed rehearsal from another attempt', () => {
+ const files = custodyFiles();
+ expect(validateSuccessorAdmission(files.document, executableRecord, {
+ now: fixedNow, receiptDirectory: files.directory,
+ }).expiresAt).toBe(Date.parse(files.document.expiresAt));
+ const storedPath = join(files.directory, 'rehearsal-admission.json');
+ const originalBytes = readFileSync(storedPath);
+ const other = JSON.parse(originalBytes.toString('utf8'));
+ other.attemptId = '123e4567-e89b-42d3-a456-426614174002';
+ const otherDigest = rebindRehearsalEvidence(files, other);
+ expect(otherDigest).not.toBe(files.document.rehearsalAdmissionSha256);
+ expect(validateSuccessorRehearsalAdmission(other, record, {
+ now: fixedNow, receiptDirectory: files.directory,
+ }).expiresAt).toBe(Date.parse(other.expiresAt));
+ expect(() => validateSuccessorAdmission(files.document, executableRecord, {
+ now: fixedNow, receiptDirectory: files.directory,
+ })).toThrow('SUCCESSOR_ADMISSION_INVALID');
+ // Restore the pinned file, leaving the internally consistent other receipt/journal.
+ writeFileSync(storedPath, originalBytes, { mode: 0o600 });
+ expect(() => validateSuccessorAdmission(files.document, executableRecord, {
+ now: fixedNow, receiptDirectory: files.directory,
+ })).toThrow('SUCCESSOR_ADMISSION_INVALID');
+});
+
+for (const changed of ['attempt', 'rollback', 'writerFence'] as const) {
+ test(`apply rejects a rehashed rehearsal with mismatched ${changed} binding`, () => {
+ const files = custodyFiles();
+ const stored = JSON.parse(readFileSync(join(files.directory, 'rehearsal-admission.json'), 'utf8'));
+ if (changed === 'attempt') {
+ // Receipt and all journal entries agree on B, but the actual admission is A.
+ const other = { ...stored, attemptId: '123e4567-e89b-42d3-a456-426614174002' };
+ rebindRehearsalEvidence(files, other);
+ const receiptPath = join(files.directory, 'rehearsal-receipt.json');
+ const receipt = JSON.parse(readFileSync(receiptPath, 'utf8'));
+ receipt.admissionSha256 = files.document.rehearsalAdmissionSha256;
+ writeFileSync(receiptPath, canonicalBytes(receipt), { mode: 0o600 });
+ files.document.rehearsalReceiptSha256 = sha256(readFileSync(receiptPath));
+ const journal = readFileSync(files.rehearsalJournalPath, 'utf8').trim().split('\n').map(JSON.parse);
+ for (const entry of journal) {
+ entry.admissionSha256 = files.document.rehearsalAdmissionSha256;
+ if ('receiptSha256' in entry) entry.receiptSha256 = files.document.rehearsalReceiptSha256;
+ }
+ const bytes = Buffer.from(`${journal.map(entry => JSON.stringify(canonical(entry))).join('\n')}\n`);
+ writeFileSync(files.rehearsalJournalPath, bytes, { mode: 0o600 });
+ files.document.rehearsalJournalSha256 = sha256(bytes);
+ writeFileSync(join(files.directory, 'rehearsal-admission.json'), canonicalBytes(stored), { mode: 0o600 });
+ } else {
+ stored.attemptId = '123e4567-e89b-42d3-a456-426614174002';
+ const fileName = changed === 'rollback' ? 'rollback-readback-receipt.json' : 'writer-fence-directSql.json';
+ const receiptPath = join(files.directory, fileName);
+ const receipt = JSON.parse(readFileSync(receiptPath, 'utf8'));
+ if (changed === 'rollback') {
+ receipt.deploymentSha = '2'.repeat(40);
+ stored.rollback.deploymentSha = receipt.deploymentSha;
+ } else receipt.observedAt = fixedNow.toISOString();
+ const bytes = canonicalBytes(receipt);
+ writeFileSync(receiptPath, bytes, { mode: 0o600 });
+ if (changed === 'rollback') stored.rollback.readbackSha256 = sha256(bytes);
+ else stored.writerFences.directSql.evidenceSha256 = sha256(bytes);
+ // Even rebinding the apply digest cannot mix B's rollback/fence with A's.
+ files.document.rehearsalAdmissionSha256 = rebindRehearsalEvidence(files, stored);
+ }
+ expect(validateSuccessorRehearsalAdmission(stored, record, {
+ now: fixedNow, receiptDirectory: files.directory,
+ }).expiresAt).toBe(Date.parse(stored.expiresAt));
+ expect(() => validateSuccessorAdmission(files.document, executableRecord, {
+ now: fixedNow, receiptDirectory: files.directory,
+ })).toThrow('SUCCESSOR_ADMISSION_INVALID');
+ });
+}
+
+test('apply requires the pinned private canonical rehearsal admission before any external read', () => {
+ for (const invalid of ['missing', 'public', 'noncanonical', 'wrongDigest'] as const) {
+ const files = custodyFiles();
+ const path = join(files.directory, 'rehearsal-admission.json');
+ if (invalid === 'missing') rmSync(path);
+ if (invalid === 'public') chmodSync(path, 0o644);
+ if (invalid === 'noncanonical') {
+ const stored = JSON.parse(readFileSync(path, 'utf8'));
+ writeFileSync(path, JSON.stringify(stored, null, 2), { mode: 0o600 });
+ files.document.rehearsalAdmissionSha256 = sha256(readFileSync(path));
+ }
+ if (invalid === 'wrongDigest') files.document.rehearsalAdmissionSha256 = '0'.repeat(64);
+ writeFileSync(files.admissionPath, canonicalBytes(files.document), { mode: 0o600 });
+ let externalCalls = 0;
+ const unexpected = () => { externalCalls += 1; throw new Error('unexpected external read'); };
+ expect(() => runReviewMediaSuccessor({
+ admissionPath: files.admissionPath, journalPath: files.journalPath, environment: {},
+ }, {
+ now: () => fixedNow,
+ loadManifestImpl: () => executableRecord,
+ gitFactsImpl: unexpected,
+ protectedMainReadbackImpl: unexpected,
+ rollbackReadbackImpl: unexpected,
+ runPsqlImpl: unexpected,
+ })).toThrow('SUCCESSOR_ADMISSION_INVALID');
+ expect(externalCalls).toBe(0);
+ expect(existsSync(files.journalPath)).toBe(false);
+ }
+});
+
+test('admission rejects wrong preimage and every wrong writer fence', () => {
+ {
+ const files = custodyFiles();
+ files.document.stageStates[10].state.finishCleanupBodySha256 = '0'.repeat(64);
+ expect(() => validateSuccessorAdmission(files.document, executableRecord, {
+ now: fixedNow,
+ receiptDirectory: files.directory,
+ })).toThrow('SUCCESSOR_ADMISSION_INVALID');
+ }
+ {
+ const files = custodyFiles();
+ files.document.stageStates[0].state.ownerAuthorityReady = false;
+ expect(() => validateSuccessorAdmission(files.document, executableRecord, {
+ now: fixedNow,
+ receiptDirectory: files.directory,
+ })).toThrow('SUCCESSOR_ADMISSION_INVALID');
+ }
+ for (const key of Object.keys(manifest.requiredWriterFences)) {
+ const files = custodyFiles();
+ files.document.writerFences[key].state = 'wrong';
+ expect(() => validateSuccessorAdmission(files.document, executableRecord, {
+ now: fixedNow,
+ receiptDirectory: files.directory,
+ })).toThrow('SUCCESSOR_ADMISSION_INVALID');
+ }
+});
+
+test('success path performs preflight, rechecks rollback and commits all eleven once', () => {
+ const files = custodyFiles();
+ const plan = compileSuccessorPlan(executableRecord, files.document);
+ const calls: string[] = [];
+ let rollbackReads = 0;
+ const result = runReviewMediaSuccessor({
+ admissionPath: files.admissionPath,
+ journalPath: files.journalPath,
+ environment: { SUPABASE_DB_URL: databaseUrl },
+ }, {
+ now: () => fixedNow,
+ gitFactsImpl: () => ({ clean: true, detached: true, revision }),
+ loadManifestImpl: () => executableRecord,
+ protectedMainReadbackImpl: () => protectedSourceReadbackBinding(manifest, revision),
+ rollbackReadbackImpl: () => { rollbackReads += 1; return rollbackReadback(); },
+ runPsqlImpl: (_url, sql) => {
+ calls.push(sql);
+ return JSON.stringify(calls.length === 1 ? plan.priorReadback.expected : plan.finalReadback.expected);
+ },
+ });
+ expect(result).toMatchObject({ code: 'SUCCESSOR_COMMITTED', status: 'committed' });
+ expect(rollbackReads).toBe(2);
+ expect(calls).toHaveLength(2);
+ expect(calls[0]).toStartWith('SET TRANSACTION ISOLATION LEVEL REPEATABLE READ READ ONLY;');
+ expect(calls[0]).not.toMatch(/^INSERT INTO supabase_migrations\.schema_migrations/gm);
+ expect(calls[1].match(/INSERT INTO supabase_migrations\.schema_migrations/g)).toHaveLength(11);
+ expect(readFileSync(files.journalPath, 'utf8')).toContain('SUCCESSOR_COMMITTED');
+});
+
+for (const remainingMs of [600_000, 5_000]) {
+ for (const failure of ['clientStall', 'statementTimeout', 'lockTimeout', 'expiry'] as const) {
+ test(`bounded preflight ${failure} with ${remainingMs}ms left cannot journal or write`, () => {
+ const document = admission();
+ document.expiresAt = new Date(fixedNow.getTime() + remainingMs).toISOString();
+ const files = custodyFiles(document);
+ const plan = compileSuccessorPlan(executableRecord, files.document);
+ let spawnCalls = 0;
+ let rollbackReads = 0;
+ const runner = createSuccessorPsqlRunner(databaseUrl, {
+ environment: {}, psql: '/fixture/psql',
+ spawnImpl: (_command: string, args: string[], options: any) => {
+ spawnCalls += 1;
+ expect(args).toContain('--single-transaction');
+ expect(options.timeout).toBe(Math.min(30_000, remainingMs));
+ expect(options.killSignal).toBe('SIGKILL');
+ const sql = options.input;
+ const queryAt = sql.indexOf(plan.stateQuery);
+ for (const setting of [
+ 'REPEATABLE READ READ ONLY', 'standard_conforming_strings=on',
+ "lock_timeout='2s'", "statement_timeout='15s'",
+ "idle_in_transaction_session_timeout='30s'", "set_config('transaction_timeout',least(30000",
+ ]) {
+ expect(sql.indexOf(setting)).toBeGreaterThanOrEqual(0);
+ expect(sql.indexOf(setting)).toBeLessThan(queryAt);
+ }
+ expect(sql).toContain('transaction_timestamp()');
+ expect(sql).toContain(`clock_timestamp() >= '${document.expiresAt}'::timestamptz`);
+ expect(sql.match(/SUCCESSOR_ADMISSION_EXPIRED/g)).toHaveLength(2);
+ expect(sql).not.toMatch(/INSERT INTO supabase_migrations\.schema_migrations/);
+ if (failure === 'clientStall') return {
+ error: Object.assign(new Error('private transport diagnostic'), { code: 'ETIMEDOUT' }),
+ status: null, stdout: '', stderr: 'private transport diagnostic',
+ };
+ const sqlstate = failure === 'statementTimeout' ? '57014' : failure === 'lockTimeout' ? '55P03' : 'P0001';
+ return { status: 1, stdout: '', stderr: `ERROR: ${sqlstate}: ${failure === 'expiry' ? 'SUCCESSOR_ADMISSION_EXPIRED' : 'private diagnostic'}\n` };
+ },
+ });
+ const expectedCode = failure === 'clientStall' ? 'MIGRATION_PSQL_EXECUTION_FAILED'
+ : failure === 'statementTimeout' ? 'MIGRATION_PSQL_FAILED_57014'
+ : failure === 'lockTimeout' ? 'MIGRATION_PSQL_FAILED_55P03' : 'SUCCESSOR_ADMISSION_EXPIRED';
+ expect(() => runReviewMediaSuccessor({
+ admissionPath: files.admissionPath, journalPath: files.journalPath,
+ environment: { SUPABASE_DB_URL: databaseUrl },
+ }, {
+ now: () => fixedNow,
+ gitFactsImpl: () => ({ clean: true, detached: true, revision }),
+ loadManifestImpl: () => executableRecord,
+ protectedMainReadbackImpl: () => protectedSourceReadbackBinding(manifest, revision),
+ rollbackReadbackImpl: () => { rollbackReads += 1; return rollbackReadback(); },
+ runPsqlImpl: runner,
+ })).toThrow(expectedCode);
+ expect(spawnCalls).toBe(1);
+ expect(rollbackReads).toBe(1);
+ expect(existsSync(files.journalPath)).toBe(false);
+ });
+ }
+}
+
+test('psql runner keeps a finite mutation/reconciliation cap and rejects unbounded overrides', () => {
+ let calls = 0;
+ const runner = createSuccessorPsqlRunner(databaseUrl, {
+ environment: {},
+ spawnImpl: (_command: string, _args: string[], options: any) => {
+ calls += 1;
+ expect(options.timeout).toBe(930_000);
+ expect(options.killSignal).toBe('SIGKILL');
+ return { status: 0, stdout: 'fixture', stderr: '' };
+ },
+ });
+ expect(runner(databaseUrl, 'SELECT 1;', true)).toBe('fixture');
+ for (const timeoutMs of [0, -1, Infinity, NaN, 930_001]) {
+ expect(() => runner(databaseUrl, 'SELECT 1;', true, { timeoutMs })).toThrow('SUCCESSOR_ARGUMENT_INVALID');
+ }
+ expect(calls).toBe(1);
+});
+
+for (const failureAt of ['fsync', 'close'] as const) {
+ test(`committed terminal audit ${failureAt} failure preserves the committed outcome`, () => {
+ const files = custodyFiles();
+ const plan = compileSuccessorPlan(executableRecord, files.document);
+ const calls: string[] = [];
+ let observed: any;
+ try {
+ observed = runReviewMediaSuccessor({
+ admissionPath: files.admissionPath,
+ journalPath: files.journalPath,
+ environment: { SUPABASE_DB_URL: databaseUrl },
+ }, {
+ now: () => fixedNow,
+ gitFactsImpl: () => ({ clean: true, detached: true, revision }),
+ loadManifestImpl: () => executableRecord,
+ protectedMainReadbackImpl: () => protectedSourceReadbackBinding(manifest, revision),
+ rollbackReadbackImpl: () => rollbackReadback(),
+ runPsqlImpl: (_url, sql) => {
+ calls.push(sql);
+ return JSON.stringify(calls.length === 1
+ ? plan.priorReadback.expected
+ : plan.finalReadback.expected);
+ },
+ appendJournalImpl: (descriptor: number, entry: any) => appendJournal(descriptor, entry, {
+ fsyncImpl: (fd: number) => {
+ if (failureAt === 'fsync') throw new Error('fixture committed audit sync failure');
+ fsyncSync(fd);
+ },
+ closeImpl: () => { throw new Error('fixture committed audit close acknowledgement loss'); },
+ }),
+ });
+ } catch (error) {
+ observed = successorFailureResult(error);
+ }
+ if (failureAt === 'fsync') {
+ expect(observed).toEqual({
+ code: 'SUCCESSOR_JOURNAL_APPEND_FAILED',
+ operationCode: 'SUCCESSOR_COMMITTED',
+ status: 'failed',
+ });
+ } else {
+ expect(observed).toMatchObject({
+ code: 'SUCCESSOR_COMMITTED',
+ status: 'committed',
+ terminalAuditReadback: 'reconciled',
+ });
+ }
+ expect(calls).toHaveLength(2);
+ expect(calls[1].match(/INSERT INTO supabase_migrations\.schema_migrations/g)).toHaveLength(11);
+ const journal = readFileSync(files.journalPath, 'utf8').trim().split('\n').map(JSON.parse);
+ expect(journal).toHaveLength(2);
+ expect(journal[1].code).toBe('SUCCESSOR_COMMITTED');
+ });
+}
+
+test('committed terminal audit readback requires both durability and exact bytes', () => {
+ const files = custodyFiles();
+ const plan = compileSuccessorPlan(executableRecord, files.document);
+ const calls: string[] = [];
+ let observed: any;
+ try {
+ runReviewMediaSuccessor({
+ admissionPath: files.admissionPath,
+ journalPath: files.journalPath,
+ environment: { SUPABASE_DB_URL: databaseUrl },
+ }, {
+ now: () => fixedNow,
+ gitFactsImpl: () => ({ clean: true, detached: true, revision }),
+ loadManifestImpl: () => executableRecord,
+ protectedMainReadbackImpl: () => protectedSourceReadbackBinding(manifest, revision),
+ rollbackReadbackImpl: () => rollbackReadback(),
+ runPsqlImpl: (_url, sql) => {
+ calls.push(sql);
+ return JSON.stringify(calls.length === 1
+ ? plan.priorReadback.expected
+ : plan.finalReadback.expected);
+ },
+ appendJournalImpl: () => {
+ const error: Error & { publicationAuditReadbackEligible?: boolean } = new Error(
+ 'fixture durable acknowledgement without terminal bytes',
+ );
+ error.publicationAuditReadbackEligible = true;
+ throw error;
+ },
+ });
+ } catch (error) {
+ observed = successorFailureResult(error);
+ }
+ expect(observed).toEqual({
+ code: 'SUCCESSOR_JOURNAL_APPEND_FAILED',
+ operationCode: 'SUCCESSOR_COMMITTED',
+ status: 'failed',
+ });
+ expect(calls).toHaveLength(2);
+ const journal = readFileSync(files.journalPath, 'utf8').trim().split('\n').map(JSON.parse);
+ expect(journal).toHaveLength(1);
+ expect(journal[0].event).toBe('started');
+});
+
+test('concurrent rollback deployment change rejects before journal or mutation', () => {
+ const files = custodyFiles();
+ const plan = compileSuccessorPlan(executableRecord, files.document);
+ let rollbackReads = 0;
+ const calls: string[] = [];
+ expect(() => runReviewMediaSuccessor({
+ admissionPath: files.admissionPath,
+ journalPath: files.journalPath,
+ environment: { SUPABASE_DB_URL: databaseUrl },
+ }, {
+ now: () => fixedNow,
+ gitFactsImpl: () => ({ clean: true, detached: true, revision }),
+ loadManifestImpl: () => executableRecord,
+ protectedMainReadbackImpl: () => protectedSourceReadbackBinding(manifest, revision),
+ rollbackReadbackImpl: () => {
+ rollbackReads += 1;
+ return rollbackReads === 1 ? rollbackReadback() : { ...rollbackReadback(), gitSha: '0'.repeat(40) };
+ },
+ runPsqlImpl: (_url, sql) => { calls.push(sql); return JSON.stringify(plan.priorReadback.expected); },
+ })).toThrow('SUCCESSOR_ROLLBACK_READBACK_INVALID');
+ expect(rollbackReads).toBe(2);
+ expect(calls).toHaveLength(1);
+ expect(calls[0]).toStartWith('SET TRANSACTION ISOLATION LEVEL REPEATABLE READ READ ONLY;');
+ expect(calls[0]).not.toMatch(/^INSERT INTO supabase_migrations\.schema_migrations/gm);
+});
+
+test('prior readback mismatch fails before journal and mutating transport', () => {
+ const files = custodyFiles();
+ const plan = compileSuccessorPlan(executableRecord, files.document);
+ const mismatch = structuredClone(plan.priorReadback.expected);
+ mismatch.bundle.ledgerCount += 1;
+ const calls: string[] = [];
+ expect(() => runReviewMediaSuccessor({
+ admissionPath: files.admissionPath,
+ journalPath: files.journalPath,
+ environment: { SUPABASE_DB_URL: databaseUrl },
+ }, {
+ now: () => fixedNow,
+ gitFactsImpl: () => ({ clean: true, detached: true, revision }),
+ loadManifestImpl: () => executableRecord,
+ protectedMainReadbackImpl: () => protectedSourceReadbackBinding(manifest, revision),
+ rollbackReadbackImpl: () => rollbackReadback(),
+ runPsqlImpl: (_url, sql) => { calls.push(sql); return JSON.stringify(mismatch); },
+ })).toThrow('SUCCESSOR_PREFLIGHT_MISMATCH');
+ expect(calls).toHaveLength(1);
+ expect(calls[0]).not.toMatch(/^INSERT INTO supabase_migrations\.schema_migrations/gm);
+ expect(() => readFileSync(files.journalPath)).toThrow();
+});
+
+test('lost apply acknowledgement reconciles once and never resends eleven mutations', () => {
+ const files = custodyFiles();
+ const plan = compileSuccessorPlan(executableRecord, files.document);
+ const calls: string[] = [];
+ expect(() => runReviewMediaSuccessor({
+ admissionPath: files.admissionPath,
+ journalPath: files.journalPath,
+ environment: { SUPABASE_DB_URL: databaseUrl },
+ }, {
+ now: () => fixedNow,
+ gitFactsImpl: () => ({ clean: true, detached: true, revision }),
+ loadManifestImpl: () => executableRecord,
+ protectedMainReadbackImpl: () => protectedSourceReadbackBinding(manifest, revision),
+ rollbackReadbackImpl: () => rollbackReadback(),
+ runPsqlImpl: (_url, sql) => {
+ calls.push(sql);
+ if (calls.length === 1) return JSON.stringify(plan.priorReadback.expected);
+ if (calls.length === 2) throw new Error('lost acknowledgement');
+ return JSON.stringify(plan.finalReadback.expected);
+ },
+ })).toThrow('MIGRATION_BUNDLE_OUTCOME_UNCONFIRMED');
+ expect(calls).toHaveLength(3);
+ expect(calls[1].match(/INSERT INTO supabase_migrations\.schema_migrations/g)).toHaveLength(11);
+ expect(calls[2]).not.toMatch(/^INSERT INTO supabase_migrations\.schema_migrations/gm);
+ expect(readFileSync(files.journalPath, 'utf8')).toContain('MIGRATION_BUNDLE_OUTCOME_UNCONFIRMED');
+});
+
+for (const failureAt of [1, 2, 3, 4, 5, 6]) {
+ test(`apply fsync failure at custody/start boundary ${failureAt} prevents database mutation`, () => {
+ const files = custodyFiles();
+ const plan = compileSuccessorPlan(executableRecord, files.document);
+ let syncs = 0;
+ let transports = 0;
+ expect(() => runReviewMediaSuccessor({
+ admissionPath: files.admissionPath,
+ journalPath: files.journalPath,
+ environment: { SUPABASE_DB_URL: databaseUrl },
+ }, {
+ now: () => fixedNow,
+ gitFactsImpl: () => ({ clean: true, detached: true, revision }),
+ loadManifestImpl: () => executableRecord,
+ protectedMainReadbackImpl: () => protectedSourceReadbackBinding(manifest, revision),
+ rollbackReadbackImpl: () => rollbackReadback(),
+ fsyncImpl: (fd: number) => {
+ syncs += 1;
+ if (syncs === failureAt) throw new Error('fixture durability failure');
+ fsyncSync(fd);
+ },
+ runPsqlImpl: (_url, sql) => {
+ transports += 1;
+ expect(sql).toStartWith('SET TRANSACTION ISOLATION LEVEL REPEATABLE READ READ ONLY;');
+ return JSON.stringify(plan.priorReadback.expected);
+ },
+ })).toThrow(failureAt <= 4 ? 'SUCCESSOR_ADMISSION_INVALID' : 'SUCCESSOR_JOURNAL_INVALID');
+ expect(transports).toBe(1);
+ expect(syncs).toBe(failureAt);
+ });
+}
+
+test('apply preserves expiry and audit failure as separate bounded outcomes', () => {
+ const files = custodyFiles();
+ const plan = compileSuccessorPlan(executableRecord, files.document);
+ let transports = 0;
+ let observed: any;
+ try {
+ runReviewMediaSuccessor({
+ admissionPath: files.admissionPath,
+ journalPath: files.journalPath,
+ environment: { SUPABASE_DB_URL: databaseUrl },
+ }, {
+ now: () => fixedNow,
+ gitFactsImpl: () => ({ clean: true, detached: true, revision }),
+ loadManifestImpl: () => executableRecord,
+ protectedMainReadbackImpl: () => protectedSourceReadbackBinding(manifest, revision),
+ rollbackReadbackImpl: () => rollbackReadback(),
+ runPsqlImpl: () => {
+ transports += 1;
+ if (transports === 2) throw Object.assign(new Error('fixture diagnostic omitted'), {
+ code: 'SUCCESSOR_ADMISSION_EXPIRED',
+ });
+ return JSON.stringify(plan.priorReadback.expected);
+ },
+ appendJournalImpl: () => { throw new Error('fixture audit failure omitted'); },
+ });
+ } catch (error) { observed = successorFailureResult(error); }
+ expect(observed).toEqual({
+ code: 'SUCCESSOR_JOURNAL_APPEND_FAILED',
+ operationCode: 'SUCCESSOR_ADMISSION_EXPIRED',
+ status: 'failed',
+ });
+ expect(transports).toBe(3);
+ expect(successorFailureResult({
+ code: 'SUCCESSOR_JOURNAL_APPEND_FAILED', operationCode: 'arbitrary diagnostic',
+ })).toEqual({ code: 'SUCCESSOR_JOURNAL_APPEND_FAILED', status: 'failed' });
+ expect(successorFailureResult({
+ code: 'SUCCESSOR_JOURNAL_APPEND_FAILED',
+ get operationCode() { throw new Error('fixture diagnostic must not escape'); },
+ })).toEqual({ code: 'SUCCESSOR_JOURNAL_APPEND_FAILED', status: 'failed' });
+});
diff --git a/docs/evidence/review-admission-fixes-20261014-validation-v1.json b/docs/evidence/review-admission-fixes-20261014-validation-v1.json
new file mode 100644
index 0000000000..9c490991d2
--- /dev/null
+++ b/docs/evidence/review-admission-fixes-20261014-validation-v1.json
@@ -0,0 +1,26 @@
+{
+ "schemaVersion": 1,
+ "observedAtUTC": "2026-10-13T20:18:46.314544+00:00",
+ "verified": true,
+ "checksumMaps": [
+ {
+ "path": "apps/web/performance/review-db-successor-20261012/SHA256SUMS",
+ "sha256": "aff9374a0e6a6964b2d32f6cb5c6069296c5155b94c2ec1015460d2386fb4e88",
+ "files": 25
+ },
+ {
+ "path": "apps/web/performance/review-media-operating-admission-20261013/SHA256SUMS",
+ "sha256": "f7345de87e22378811fb0bfef0f57461702a4178ec92cd13e82812dcb921138e",
+ "files": 13
+ }
+ ],
+ "primaryUnitTests": 98,
+ "subsequentHarnessTests": 20,
+ "overlapNotSummed": true,
+ "pg17ProofSha256": "9a4985689d121979e3d7559c32b8f27c88ae3d1bb82d559fb9bb8ada6e706ac2",
+ "originalProofPreserved": true,
+ "heldLaunchPolicy": true,
+ "hostedWrites": false,
+ "operatingWrites": false,
+ "performanceEvidence": false
+}
diff --git a/docs/evidence/review-admission-fixes-20261014-validation-v2.json b/docs/evidence/review-admission-fixes-20261014-validation-v2.json
new file mode 100644
index 0000000000..c8096a8c88
--- /dev/null
+++ b/docs/evidence/review-admission-fixes-20261014-validation-v2.json
@@ -0,0 +1,28 @@
+{
+ "observedAtUTC": "2026-10-13T20:34:16.074395+00:00",
+ "verified": true,
+ "checksumMaps": [
+ {
+ "path": "apps/web/performance/review-db-rehearsal-20261012/SHA256SUMS",
+ "sha256": "c1af1afd832bd2b0bbdcd4419589de778525e1b18a4dff196e457b82381c97fb",
+ "files": 36
+ },
+ {
+ "path": "apps/web/performance/review-db-successor-20261012/SHA256SUMS",
+ "sha256": "aff9374a0e6a6964b2d32f6cb5c6069296c5155b94c2ec1015460d2386fb4e88",
+ "files": 25
+ },
+ {
+ "path": "apps/web/performance/review-media-operating-admission-20261013/SHA256SUMS",
+ "sha256": "f7345de87e22378811fb0bfef0f57461702a4178ec92cd13e82812dcb921138e",
+ "files": 13
+ }
+ ],
+ "relatedIndexTests": 34,
+ "assertions": 153,
+ "currentRollbackProofSha256": "23fb9b471a06dbd36aafab3a678152ca897893b3cdc968f2d623035d217b1e38",
+ "fullSuiteFailedPriorHead": "71dcfc98ab03a016d05449780574a4b9edd81e36",
+ "fullSuiteNewHeadPending": true,
+ "operatingEvidence": false,
+ "performanceEvidence": false
+}
diff --git a/docs/operations/review-media-operating-admission-20261013.md b/docs/operations/review-media-operating-admission-20261013.md
new file mode 100644
index 0000000000..ce7c69760c
--- /dev/null
+++ b/docs/operations/review-media-operating-admission-20261013.md
@@ -0,0 +1,28 @@
+# Review-media 운영 입력 준비
+
+`apps/web/scripts/review-media-operating-admission.mjs`는 별도 수집기가 제공한 근거 파일을 검증하고 기존 적용기의 private admission 형식으로 변환한다. GitHub·Vercel·DB를 조회하거나 SQL을 실행하지 않는다. 형식·해시 검증 성공은 입력에 적힌 외부 상태를 직접 확인했다는 뜻이 아니다.
+
+이 도구는 호출자가 제공한 JSON의 관측 출처를 인증하지 않는다. 실제 관측을 수집하는 adapter의 출처 검증 없이 결과를 운영 승인으로 사용하지 않는다. 합성 단위 검사의 receipt·journal은 형식 검증용이며 운영 증빙으로 사용하지 않는다.
+
+## 입력과 실행 순서
+
+1. 보호된 `main`의 깨끗한 detached checkout, 현재 production rollback 대상, manifest의 writer fence 7개를 실제로 확인한다. 실행 창과 원래 `observedAt`을 포함한 canonical JSON 입력을 저장소 밖 private 디렉터리에 저장한다. 프로바이더 원문·인증 정보는 입력에 넣지 않는다.
+2. `--mode rehearsal --input --custody-dir `로 리허설 admission을 만든다. 입력은 schema, source revision/root, manifest hash, checkout readback, source/protected-main/rollback receipt와 writer-fence receipt 전체를 포함해야 한다.
+3. 준비된 admission을 기존 `review-media-sql-rehearsal.mjs`에 전달한다. 기존 적용기는 source와 rollback을 다시 읽고 DB 대상·중지 조건을 확인한 뒤 rollback-only 리허설을 수행한다. 이 생성기만 실행해서는 리허설 receipt가 생기지 않는다.
+4. 성공한 리허설의 admission·receipt·3행 journal SHA와 원래 만료 시각을 결속한 apply 입력을 준비한다. `--mode apply --input --custody-dir `로 apply admission을 만든다. 적용 admission의 `rehearsalAdmissionSha256`는 저장된 `rehearsal-admission.json`의 실제 바이트이며 receipt·journal의 원래 실행과 source·rollback·writer fence가 같아야 한다.
+5. 기존 적용기의 launch policy, 보호 source, 실제 rollback과 writer-fence 근거가 충족된 경우에만 `review-media-sql-successor.mjs`를 실행한다. 출력 존재·실패·응답 유실은 기존 산출물을 확인한 뒤 처리하며, 파일을 지워 같은 작업을 자동 재시도하지 않는다.
+
+두 mode 모두 Node 24로 `apps/web/scripts/review-media-operating-admission.mjs`를 실행한다. 성공 출력은 준비한 경로·SHA·만료 시각이며, 운영 적용 완료 영수증이 아니다.
+
+## 보존 조건
+
+- 전체 창은 최대 15분이다. apply 준비는 rehearsal의 `createdAt`·`expiresAt`을 그대로 유지하며 창을 연장하지 않는다. 만료됐으면 실제 상태를 새로 확인하고 새로운 시도 전체를 준비해야 한다.
+- 적용 admission의 stage states는 검증된 rehearsal receipt에서만 가져온다. 임의 기대값이나 로컬 fixture 값을 운영 결과로 승격하지 않는다.
+- 파일은 배타적으로 생성한다. 기존 custody, admission, journal을 덮거나 삭제하지 않는다. private 경로와 파일·디렉터리 저장 검증 실패 시 준비 성공을 반환하지 않는다.
+- 입력을 수집하는 운영 adapter와 live 인증은 별도다. 저장소에 Vercel 토큰이 없다는 사실만으로 새 토큰 생성·저장 권한을 추정하지 않는다. 현재 manifest의 `held` 상태를 이 도구가 변경하지 않는다.
+
+현재 통과한 PG17.6 proof는 controller와 SQL의 격리 재생이다. 이 생성기의 검증은 별도 단위 검사로 기록하며 hosted DB 적용·Storage 물리 삭제·운영 성능 증거로 합산하지 않는다.
+
+최신 단위 검사 기록은 `apps/web/performance/review-media-operating-admission-20261013/current-evidence.json`이 선택한다. 이전 `verification.json`과 `controller-reverification.json`은 해당 시점의 기록이며 현재 소스 증명으로 사용하지 않는다. 새 기록은 검사 로그와 source 바이트를 결속한다.
+
+적용 전 state read는 lock 2초, statement 15초, transaction 최대 30초와 남은 admission 시간, client 최대 30초로 제한한다. 실패·만료는 apply journal 및 mutation 전에 거부한다. 다른 psql 작업도 유한 client 제한을 사용하지만 이 로컬 검사만으로 운영 timeout 동작을 입증하지 않는다.