import assert from "node:assert/strict"; import { readFileSync } from "node:fs"; import test from "node:test"; const migration = readFileSync( new URL("../supabase/migrations/20260814010000_immutable_skill_registry.sql", import.meta.url), "utf8", ); test("skill registry migration is additive and binds full case identity", () => { assert.match(migration, /add column if not exists skill_sha256 text/); assert.match(migration, /add column if not exists skill_source_commit text/); assert.match(migration, /open_agentic_rectification_case_v2/); assert.match(migration, /get_agentic_rectification_skill_identity/); assert.match(migration, /get_agentic_rectification_skill_identity_status/); assert.doesNotMatch(migration, /^\s*(?:drop table|drop column|truncate table)\b/im); }); test("skill upgrades and legacy adoption are explicit, owner-scoped, and receipt-backed", () => { assert.match(migration, /where id = p_case_id and user_id = p_user_id/); assert.match(migration, /agentic_rectification_resumable_statuses\(\)/); assert.match(migration, /upgrade_agentic_rectification_skill_v2/); assert.match(migration, /adopt_agentic_rectification_skill_v1/); assert.match(migration, /previous_skill_sha256 text check/); assert.match(migration, /alter column previous_skill_sha256 drop not null/); assert.match(migration, /previous_identity_status in \('verified', 'legacy_unverifiable'\)/); assert.match(migration, /upgrade_kind in \('version_upgrade', 'legacy_adoption'\)/); assert.match(migration, /where upgrade_kind = 'legacy_adoption'/); assert.match(migration, /'previous_identity_status', 'legacy_unverifiable'/); assert.match(migration, /'upgrade_kind', 'legacy_adoption'/); assert.match(migration, /agentic_rectification_skill_identity_already_verified/); assert.match(migration, /receipt_id/); }); test("incomplete historical identities remain identifiable and fail closed for execution", () => { assert.match(migration, /agentic_rectification_legacy_skill_identity_unverifiable/); assert.match(migration, /'skill_identity_status',[\s\S]*'legacy_unverifiable'/); assert.match(migration, /'requires_skill_adoption'/); assert.match(migration, /v_case\.skill_sha256 is null/); assert.match(migration, /if not v_is_new then/); }); test("open request replays fail closed when the supplied full identity drifts", () => { assert.match(migration, /v_case\.skill_name is distinct from p_skill_name/); assert.match(migration, /v_case\.skill_version is distinct from p_skill_version/); assert.match(migration, /v_case\.skill_sha256 is distinct from p_skill_sha256/); assert.match(migration, /v_case\.skill_source_commit is distinct from p_skill_source_commit/); assert.match(migration, /agentic_rectification_skill_identity_mismatch/); }); test("run receipts are request-scoped and append-only across regeneration", () => { assert.match(migration, /request_id uuid not null/); assert.match(migration, /run_kind text not null/); assert.match(migration, /run_kind in \('turn', 'regeneration'\)/); assert.match(migration, /case_id, turn_id, request_id, run_kind/); assert.match(migration, /on conflict \(case_id, turn_id, request_id, run_kind\) do nothing/); assert.doesNotMatch(migration, /on conflict \(turn_id\) do update/); assert.match(migration, /agentic_rectification_skill_receipt_conflict/); assert.match( migration, /from public\.agentic_rectification_cases\s+where id = p_case_id and user_id = p_user_id\s+for update;/, ); }); test("run receipt schema repair is safe on repeated execution", () => { assert.match(migration, /add column if not exists request_id uuid/); assert.match(migration, /add column if not exists run_kind text/); 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); }); test("case and receipt tables expose no direct service-role DML", () => { assert.match( migration, /revoke all on table public\.agentic_rectification_cases from service_role/, ); assert.match( migration, /revoke all on table public\.agentic_rectification_skill_upgrade_receipts\s+from public, anon, authenticated, service_role/, ); assert.match( migration, /revoke all on table public\.agentic_rectification_skill_run_receipts\s+from public, anon, authenticated, service_role/, ); assert.doesNotMatch( migration, /grant (?:all|select|insert|update|delete) on table public\.agentic_rectification_skill_(?:upgrade|run)_receipts to service_role/, ); }); test("append-only receipt triggers reject direct mutation but allow missing-parent cascades", () => { assert.match(migration, /agentic_rectification_skill_upgrade_receipts_append_only/); assert.match(migration, /agentic_rectification_skill_run_receipts_append_only/); assert.match(migration, /agentic_rectification_skill_upgrade_receipts_truncate_guard/); assert.match(migration, /agentic_rectification_skill_run_receipts_truncate_guard/); assert.match(migration, /before update or delete/); assert.match( migration, /before truncate on public\.agentic_rectification_skill_upgrade_receipts/, ); assert.match( migration, /before truncate on public\.agentic_rectification_skill_run_receipts/, ); assert.match(migration, /if tg_op = 'TRUNCATE' then/); assert.match(migration, /if tg_op = 'UPDATE' then/); assert.match(migration, /select 1 from public\.agentic_rectification_cases where id = old\.case_id/); assert.match(migration, /select 1 from public\.agentic_rectification_turns where id = old\.turn_id/); assert.match(migration, /select 1 from auth\.users where id = old\.user_id/); assert.match(migration, /array\['turn_id'\]::text\[\]/); assert.match(migration, /drop constraint %I/); assert.match(migration, /drop index public\.%I/); assert.match(migration, /agentic_rectification_skill_receipts_are_append_only/); }); test("case identity guard remains defense in depth", () => { assert.match(migration, /guard_agentic_rectification_case_skill_identity/); assert.match( migration, /before update of skill_name, skill_version, skill_sha256, skill_source_commit/, ); assert.match(migration, /current_user <> v_table_owner/); assert.match(migration, /agentic_rectification_skill_identity_immutable/); });