From afc8af5b8e48adcb844b3929888f9178879282e6 Mon Sep 17 00:00:00 2001 From: Jesse_Chen Date: Fri, 14 Aug 2026 16:52:35 +0800 Subject: [PATCH] test(skill): enforce schema owner and receipt immutability --- .../20260814010000_immutable_skill_registry.sql | 9 +++++++++ frontend/tests/skill-registry-database.test.ts | 10 +++++++++- frontend/tests/skill-registry-migration.test.ts | 7 +++++++ 3 files changed, 25 insertions(+), 1 deletion(-) diff --git a/frontend/supabase/migrations/20260814010000_immutable_skill_registry.sql b/frontend/supabase/migrations/20260814010000_immutable_skill_registry.sql index 9aa3bede..7be728cc 100644 --- a/frontend/supabase/migrations/20260814010000_immutable_skill_registry.sql +++ b/frontend/supabase/migrations/20260814010000_immutable_skill_registry.sql @@ -3,6 +3,15 @@ -- New Agentic cases bind full identity through the v2 RPC; new Personal Reports -- bind it through the server-owned persistence service. +do $migration$ +begin + if current_user <> 'schema_owner' then + raise exception 'immutable_skill_registry_requires_schema_owner' + using errcode = '42501'; + end if; +end +$migration$; + alter table public.agentic_rectification_cases add column if not exists skill_sha256 text check (skill_sha256 is null or skill_sha256 ~ '^[0-9a-f]{64}$'), diff --git a/frontend/tests/skill-registry-database.test.ts b/frontend/tests/skill-registry-database.test.ts index fb9f0999..96f9538d 100644 --- a/frontend/tests/skill-registry-database.test.ts +++ b/frontend/tests/skill-registry-database.test.ts @@ -492,7 +492,7 @@ test("immutable Skill migration enforces RPC-only identity, legacy adoption, and /agentic_rectification_skill_receipts_are_append_only/, ); expectOwnerError( - `delete from public.agentic_rectification_skill_upgrade_receipts where case_id = ${sqlLiteral(ids.legacyCase)};`, + `delete from public.agentic_rectification_skill_upgrade_receipts where case_id = ${sqlLiteral(openedCaseId)};`, /agentic_rectification_skill_receipts_are_append_only/, ); expectOwnerError( @@ -507,6 +507,14 @@ test("immutable Skill migration enforces RPC-only identity, legacy adoption, and `truncate table public.agentic_rectification_cases cascade;`, /agentic_rectification_skill_receipts_are_append_only/, ); + assert.equal( + fixture.psql(`select concat_ws(':', + (select count(*) from public.agentic_rectification_cases where id = ${sqlLiteral(ids.legacyCase)}), + (select count(*) from public.agentic_rectification_skill_upgrade_receipts where case_id = ${sqlLiteral(ids.legacyCase)}), + (select count(*) from public.agentic_rectification_skill_run_receipts where case_id = ${sqlLiteral(openedCaseId)}) + )`), + '1:1:2', + ); insertSession(ids.caseDeleteSession, ids.user); insertCase({ diff --git a/frontend/tests/skill-registry-migration.test.ts b/frontend/tests/skill-registry-migration.test.ts index e0c64eb0..a17a3bcb 100644 --- a/frontend/tests/skill-registry-migration.test.ts +++ b/frontend/tests/skill-registry-migration.test.ts @@ -68,6 +68,13 @@ test("run receipt schema repair is safe on repeated execution", () => { assert.match(migration, /create unique index agentic_rectification_skill_run_receipts_request_idx/); }); +test("immutable Skill migration is restricted to schema_owner", () => { + assert.match( + migration, + /if current_user <> 'schema_owner' then[\s\S]*immutable_skill_registry_requires_schema_owner/, + ); +}); + test("migration transaction is owned by the migration runner", () => { assert.doesNotMatch(migration, /^\s*begin\s*;/im); assert.doesNotMatch(migration, /^\s*commit\s*;/im);