Files
Jyotisha/frontend/tests/skill-registry-database.test.ts
T
Jesse_ChenandClaude Opus 5.5 700b903d3e test(rectification): product-domain open wrapper is SECURITY INVOKER in the immutable Skill ACL contract (BUG-1131)
Original value: the 12-arg wrapper expected definer=true. New value: definer
is true for the six historical signatures and false for the 12-arg wrapper.
Reason: a definer wrapper runs as schema_owner, which the ACL reconciliation
leaves without EXECUTE on the 11-arg open, so homepage/new opens hit 42501.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017eEAG8HD3mm8gsKXgk8uU8
2026-10-01 08:04:05 +08:00

553 lines
28 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import assert from "node:assert/strict";
import { spawnSync } from "node:child_process";
import { fileURLToPath } from "node:url";
import { readFileSync } from "node:fs";
import test from "node:test";
import { startPostgresFixture } from "./helpers/postgres-fixture.ts";
const runnerPath = fileURLToPath(
new URL("../scripts/db-migrate.mjs", import.meta.url),
);
const migrationSql = readFileSync(
new URL("../supabase/migrations/20260814010000_immutable_skill_registry.sql", import.meta.url),
"utf8",
);
function dockerAvailable(): boolean {
return spawnSync("docker", ["version", "--format", "{{.Server.Version}}"], {
encoding: "utf8",
stdio: "ignore",
}).status === 0;
}
const skipWithoutDocker = dockerAvailable() ? false : "docker unavailable on this host";
const ids = {
user: "66666666-6666-4666-8666-666666666666",
otherUser: "77777777-7777-4777-8777-777777777777",
cascadeUser: "88888888-8888-4888-8888-888888888888",
legacySession: "22222222-2222-4222-8222-222222222222",
legacyCase: "11111111-1111-4111-8111-111111111111",
legacyTurn: "33333333-3333-4333-8333-333333333333",
legacyEvidence: "55555555-5555-4555-8555-555555555555",
legacyResult: "99999999-9999-4999-8999-999999999999",
terminalSession: "22222222-2222-4222-8222-222222222223",
terminalCase: "11111111-1111-4111-8111-111111111112",
verifiedTurn: "33333333-3333-4333-8333-333333333334",
verifiedRequest: "44444444-4444-4444-8444-444444444444",
cascadeSession: "22222222-2222-4222-8222-222222222224",
cascadeCase: "11111111-1111-4111-8111-111111111113",
cascadeTurn: "33333333-3333-4333-8333-333333333335",
cascadeRequest: "44444444-4444-4444-8444-444444444445",
caseDeleteSession: "22222222-2222-4222-8222-222222222225",
caseDeleteCase: "11111111-1111-4111-8111-111111111114",
caseDeleteTurn: "33333333-3333-4333-8333-333333333336",
caseDeleteRequest: "44444444-4444-4444-8444-444444444446",
};
const oldHash = "a".repeat(64);
const newHash = "b".repeat(64);
const adoptionHash = "c".repeat(64);
const sourceCommit = "0fd111d16b45796086a6c1d0945dbd3de6755d8a";
function sqlLiteral(value: string): string {
return `'${value.replaceAll("'", "''")}'`;
}
test("immutable Skill migration enforces RPC-only identity, legacy adoption, and cascade-safe receipts", { skip: skipWithoutDocker }, () => {
const fixture = startPostgresFixture();
const migrate = () =>
spawnSync(process.execPath, [runnerPath], {
encoding: "utf8",
env: {
...process.env,
SCHEMA_DATABASE_URL: fixture.connectionUrl(
"schema_owner",
"schema-owner-test-password",
),
},
});
const serviceSql = (statement: string) =>
fixture
.psqlAs(
"service_runtime",
"service-runtime-test-password",
`set role service_role; ${statement}`,
)
.replace(/^SET\n/, "");
const expectServiceError = (statement: string, pattern: RegExp) => {
assert.throws(() => serviceSql(statement), pattern);
};
const expectOwnerError = (statement: string, pattern: RegExp) => {
assert.throws(
() => fixture.psqlAs(
"schema_owner",
"schema-owner-test-password",
statement,
),
pattern,
);
};
const insertUser = (id: string, email: string) => fixture.psqlAs(
"identity_runtime",
"identity-runtime-test-password",
`insert into identity.users (id, name, email, email_verified, email_verified_at)
values (${sqlLiteral(id)}, 'Skill Registry User', ${sqlLiteral(email)}, true, now());`,
);
const insertSession = (id: string, userId: string) => fixture.psql(`
insert into public.chat_sessions (id, user_id, title, theme, session_type, messages)
values (${sqlLiteral(id)}, ${sqlLiteral(userId)}, '生时校正', 'general',
'birth_time_rectification', '[]'::jsonb);
`);
const insertCase = (input: {
id: string;
userId: string;
sessionId: string;
status?: "draft" | "closed";
hash?: string | null;
}) => fixture.psql(`
insert into public.agentic_rectification_cases (
id, user_id, session_id, status, skill_name, skill_version,
skill_sha256, skill_source_commit, baseline_profile_fingerprint,
baseline_birth_snapshot, candidate_range, completed_at
) values (
${sqlLiteral(input.id)}, ${sqlLiteral(input.userId)}, ${sqlLiteral(input.sessionId)},
${sqlLiteral(input.status ?? "draft")}, 'jyotish-birth-time-rectification', '9.0.0',
${input.hash ? sqlLiteral(input.hash) : "null"},
${input.hash ? sqlLiteral(sourceCommit) : "null"}, ${sqlLiteral(oldHash)},
'{"birth_date":"1997-08-08"}'::jsonb,
'{"start_time":"04:50","end_time":"05:10"}'::jsonb,
${input.status === "closed" ? "now()" : "null"}
);
`);
try {
fixture.psql(`create role historical_runtime_probe noinherit;`);
fixture.psqlAs(
"schema_owner",
"schema-owner-test-password",
`create or replace function public.open_agentic_rectification_case_v2(
uuid, uuid, text, uuid, text, text, text, text, text, jsonb, jsonb
) returns jsonb language sql as $$ select '{}'::jsonb $$;
grant execute on function public.open_agentic_rectification_case_v2(
uuid, uuid, text, uuid, text, text, text, text, text, jsonb, jsonb
) to historical_runtime_probe, service_runtime;`,
);
const first = migrate();
assert.equal(first.status, 0, `${first.stdout}${first.stderr}`);
assert.match(first.stdout, /applied 20260814010000_immutable_skill_registry\.sql/);
const oldSignatures = [
"public.open_agentic_rectification_case_v2(uuid,uuid,text,uuid,text,text,text,text,text,jsonb,jsonb)",
"public.get_agentic_rectification_skill_identity(uuid,uuid)",
"public.get_agentic_rectification_skill_identity_status(uuid,uuid)",
"public.upgrade_agentic_rectification_skill_v2(uuid,uuid,text,text,text,text)",
"public.adopt_agentic_rectification_skill_v1(uuid,uuid,text,text,text,text)",
"public.insert_agentic_rectification_skill_run_receipt(uuid,uuid,uuid,uuid,text,text,text,text,text)",
];
const segmentOpenSignature = "public.open_agentic_rectification_case_v2(uuid,uuid,text,uuid,text,text,text,text,text,jsonb,jsonb,text)";
const assertExactAcls = () => {
for (const signature of [...oldSignatures, segmentOpenSignature]) {
const row = JSON.parse(fixture.psql(`
select jsonb_build_object(
'owner', pg_catalog.pg_get_userbyid(p.proowner),
'definer', p.prosecdef,
'config', p.proconfig,
'owner_execute', has_function_privilege(p.proowner,p.oid,'EXECUTE'),
'legacy', has_function_privilege('historical_runtime_probe',p.oid,'EXECUTE'),
'service', has_function_privilege('service_role',p.oid,'EXECUTE'),
'anon', has_function_privilege('anon',p.oid,'EXECUTE'),
'authenticated', has_function_privilege('authenticated',p.oid,'EXECUTE'),
'public', exists(select 1 from pg_catalog.aclexplode(p.proacl) a where a.grantee=0 and a.privilege_type='EXECUTE'),
'runtime_direct', exists(select 1 from pg_catalog.aclexplode(p.proacl) a join pg_catalog.pg_roles r on r.oid=a.grantee
where r.rolname='service_runtime' and a.privilege_type='EXECUTE')
) from pg_catalog.pg_proc p where p.oid='${signature}'::regprocedure;
`));
// 原值:12 参数包装 definer: true;新值:12 参数包装 definer: false,旧 6 签名仍为 true;
// 原因:BUG-1131——definer 包装以属主身份调用只授权 service_role 的 11 参数开案,首页/新建开案 42501。
assert.deepEqual(row, { owner: "schema_owner", definer: signature !== segmentOpenSignature, config: ['search_path=""'], owner_execute: signature === segmentOpenSignature,
legacy: false, service: true, anon: false, authenticated: false, public: false, runtime_direct: false }, signature);
}
};
// 原值:按proname串联6项f:t:f:f:f;新值:旧6精确签名逐角色不变+新12参数签名独立检查。
// 原因:overload增加同名函数,不能以任意count替代或遗漏owner/PUBLIC/anon/auth/历史角色权限。
assertExactAcls();
fixture.psql(`
grant execute on function public.open_agentic_rectification_case_v2(
uuid, uuid, text, uuid, text, text, text, text, text, jsonb, jsonb
) to historical_runtime_probe, service_runtime;
grant execute on function public.get_agentic_rectification_skill_identity(uuid, uuid)
to historical_runtime_probe, service_runtime;
grant execute on function public.get_agentic_rectification_skill_identity_status(uuid, uuid)
to historical_runtime_probe, service_runtime;
grant execute on function public.upgrade_agentic_rectification_skill_v2(
uuid, uuid, text, text, text, text
) to historical_runtime_probe, service_runtime;
grant execute on function public.adopt_agentic_rectification_skill_v1(
uuid, uuid, text, text, text, text
) to historical_runtime_probe, service_runtime;
grant execute on function public.insert_agentic_rectification_skill_run_receipt(
uuid, uuid, uuid, uuid, text, text, text, text, text
) to historical_runtime_probe, service_runtime;
`);
fixture.psqlAs(
"schema_owner",
"schema-owner-test-password",
migrationSql,
);
// 原值:reconciliation后同名6项串;新值:复用精确签名与逐角色断言;原因:不把新overload混入旧身份。
assertExactAcls();
assert.equal(
fixture.psql("select pg_has_role('service_runtime', 'service_role', 'MEMBER');"),
"t",
);
// 原值:同名6项t串;新值:旧6+新overload逐签名runtime检查;原因:保留全部身份与SET ROLE生产边界。
for (const signature of [...oldSignatures, segmentOpenSignature]) {
assert.equal(serviceSql(`select has_function_privilege(current_user,'${signature}'::regprocedure,'EXECUTE');`), "t", signature);
}
const second = migrate();
assert.equal(second.status, 0, `${second.stdout}${second.stderr}`);
assert.match(second.stdout, /already applied 20260814010000_immutable_skill_registry\.sql/);
assert.equal(
fixture.psql(`
select concat_ws(':',
has_table_privilege('service_role', 'public.agentic_rectification_cases', 'SELECT'),
has_table_privilege('service_role', 'public.agentic_rectification_cases', 'INSERT'),
has_table_privilege('service_role', 'public.agentic_rectification_cases', 'UPDATE'),
has_table_privilege('service_role', 'public.agentic_rectification_cases', 'DELETE'),
has_table_privilege('service_role', 'public.agentic_rectification_cases', 'TRUNCATE'),
has_table_privilege('service_role', 'public.agentic_rectification_skill_upgrade_receipts', 'SELECT,INSERT,UPDATE,DELETE,TRUNCATE'),
has_table_privilege('service_role', 'public.agentic_rectification_skill_run_receipts', 'SELECT,INSERT,UPDATE,DELETE,TRUNCATE')
)
`),
"f:f:f:f:f:f:f",
);
const catalog = fixture.psql(`
select p.proname || ':' || pg_catalog.pg_get_userbyid(p.proowner) || ':' ||
p.prosecdef || ':' || coalesce(array_to_string(p.proconfig, ','), '') || ':' ||
has_function_privilege('service_role', p.oid, 'EXECUTE') || ':' ||
has_function_privilege('anon', p.oid, 'EXECUTE') || ':' ||
has_function_privilege('authenticated', p.oid, 'EXECUTE')
from pg_catalog.pg_proc p
join pg_catalog.pg_namespace n on n.oid = p.pronamespace
-- 原值:按6个proname含所有overload;新值:仅旧6个精确签名。
-- 原因:旧catalog数量/配置保持6不变,新12参数由assertExactAcls单独验证。
where n.nspname = 'public' and p.oid in (
${oldSignatures.map(signature => `'${signature}'::regprocedure`).join(",\n ")}
)
order by p.proname;
`);
for (const line of catalog.split("\n")) {
assert.match(line, /^[^:]+:schema_owner:true:search_path="":true:f:f$/);
}
assert.equal(catalog.split("\n").length, 6);
assert.equal(
fixture.psql(`
select string_agg(c.relname || ':' || pg_catalog.pg_get_userbyid(c.relowner), ',' order by c.relname)
from pg_catalog.pg_class c
join pg_catalog.pg_namespace n on n.oid = c.relnamespace
where n.nspname = 'public' and c.relname in (
'agentic_rectification_cases',
'agentic_rectification_skill_upgrade_receipts',
'agentic_rectification_skill_run_receipts'
);
`),
"agentic_rectification_cases:schema_owner,agentic_rectification_skill_run_receipts:schema_owner,agentic_rectification_skill_upgrade_receipts:schema_owner",
);
insertUser(ids.user, "skill-registry@example.com");
insertUser(ids.otherUser, "skill-registry-other@example.com");
insertUser(ids.cascadeUser, "skill-registry-cascade@example.com");
const openedCaseId = serviceSql(`
select public.open_agentic_rectification_case_v2(
${sqlLiteral(ids.user)}::uuid, gen_random_uuid(), 'homepage', null,
'jyotish-birth-time-rectification', '9.0.0', ${sqlLiteral(oldHash)},
${sqlLiteral(sourceCommit)}, ${sqlLiteral(oldHash)},
'{"birth_date":"1997-08-08","latitude":25.04,"longitude":121.56,"timezone_offset":8,"birth_time_source":"family_exact"}'::jsonb,
'{"start_time":"04:50","end_time":"05:10"}'::jsonb
)->>'case_id';
`);
assert.match(openedCaseId, /^[0-9a-f-]{36}$/);
assert.equal(
fixture.psql(`select skill_version || ':' || skill_sha256 from public.agentic_rectification_cases where id = ${sqlLiteral(openedCaseId)}`),
`9.0.0:${oldHash}`,
);
expectServiceError(
`insert into public.agentic_rectification_cases (
user_id, session_id, status, skill_name, skill_version,
baseline_profile_fingerprint, baseline_birth_snapshot, candidate_range
) values (${sqlLiteral(ids.user)}, gen_random_uuid(), 'draft',
'jyotish-birth-time-rectification', '9.0.0', ${sqlLiteral(oldHash)}, '{}'::jsonb, '{}'::jsonb);`,
/permission denied for table agentic_rectification_cases/,
);
expectServiceError(
`update public.agentic_rectification_cases set skill_sha256 = ${sqlLiteral(newHash)} where id = ${sqlLiteral(openedCaseId)};`,
/permission denied for table agentic_rectification_cases/,
);
fixture.psql(`
insert into public.agentic_rectification_turns (id, case_id, user_message, status, model_name)
values (${sqlLiteral(ids.verifiedTurn)}, ${sqlLiteral(openedCaseId)}, 'verified run', 'pending', 'fixture-model');
`);
assert.equal(
serviceSql(`select public.insert_agentic_rectification_skill_run_receipt(
${sqlLiteral(ids.user)}::uuid, ${sqlLiteral(openedCaseId)}::uuid,
${sqlLiteral(ids.verifiedTurn)}::uuid, ${sqlLiteral(ids.verifiedRequest)}::uuid,
'turn', 'jyotish-birth-time-rectification', '9.0.0',
${sqlLiteral(oldHash)}, ${sqlLiteral(sourceCommit)}
)->>'skill_sha256';`),
oldHash,
);
assert.equal(
serviceSql(`select public.upgrade_agentic_rectification_skill_v2(
${sqlLiteral(ids.user)}::uuid, ${sqlLiteral(openedCaseId)}::uuid,
'jyotish-birth-time-rectification', '9.1.0', ${sqlLiteral(newHash)}, null
)->>'skill_sha256';`),
newHash,
);
assert.equal(
fixture.psql(`select previous_identity_status || ':' || upgrade_kind || ':' || previous_skill_sha256 || ':' || skill_sha256
from public.agentic_rectification_skill_upgrade_receipts where case_id = ${sqlLiteral(openedCaseId)}`),
`verified:version_upgrade:${oldHash}:${newHash}`,
);
assert.equal(
serviceSql(`select public.insert_agentic_rectification_skill_run_receipt(
${sqlLiteral(ids.user)}::uuid, ${sqlLiteral(openedCaseId)}::uuid,
${sqlLiteral(ids.verifiedTurn)}::uuid, gen_random_uuid()::uuid,
'regeneration', 'jyotish-birth-time-rectification', '9.1.0',
${sqlLiteral(newHash)}, null
)->>'run_kind';`),
'regeneration',
);
assert.equal(
fixture.psql(`select count(*) from public.agentic_rectification_skill_run_receipts where turn_id = ${sqlLiteral(ids.verifiedTurn)}`),
'2',
);
expectServiceError(
`select public.adopt_agentic_rectification_skill_v1(
${sqlLiteral(ids.user)}::uuid, ${sqlLiteral(openedCaseId)}::uuid,
'jyotish-birth-time-rectification', '9.0.0', ${sqlLiteral(adoptionHash)}, ${sqlLiteral(sourceCommit)}
);`,
/agentic_rectification_skill_identity_already_verified/,
);
insertSession(ids.legacySession, ids.user);
insertCase({ id: ids.legacyCase, userId: ids.user, sessionId: ids.legacySession, hash: null });
fixture.psql(`
insert into public.agentic_rectification_turns (id, case_id, user_message, status, model_name)
values (${sqlLiteral(ids.legacyTurn)}, ${sqlLiteral(ids.legacyCase)}, 'legacy evidence', 'pending', 'fixture-model');
insert into public.agentic_rectification_evidence (
id, case_id, source_turn_id, user_quote, subject, event_kind, domain, date_precision, summary
) values (
${sqlLiteral(ids.legacyEvidence)}, ${sqlLiteral(ids.legacyCase)}, ${sqlLiteral(ids.legacyTurn)},
'legacy evidence', 'self', 'career_entry', 'career', 'year', 'legacy evidence summary'
);
insert into public.agentic_rectification_results (
id, user_id, session_id, engine_result_id, canonical_input_hash, algorithm_version,
candidate_range, candidates, overall_confidence, selection_allowed, confirmation_allowed,
baseline_birth_date, baseline_latitude, baseline_longitude, baseline_timezone_offset,
case_id, skill_version
) values (
${sqlLiteral(ids.legacyResult)}, ${sqlLiteral(ids.user)}, ${sqlLiteral(ids.legacySession)},
'legacy-engine-result', ${sqlLiteral(oldHash)}, 'legacy-algorithm',
'{"start_time":"04:50","end_time":"05:10"}'::jsonb, '[]'::jsonb,
'low', false, false, '1997-08-08', 25.04, 121.56, 8,
${sqlLiteral(ids.legacyCase)}, '9.0.0'
);
`);
assert.equal(
serviceSql(`select
(public.get_agentic_rectification_skill_identity_status(
${sqlLiteral(ids.user)}::uuid, ${sqlLiteral(ids.legacyCase)}::uuid
)->>'skill_identity_status') || ':' ||
(public.get_agentic_rectification_skill_identity_status(
${sqlLiteral(ids.user)}::uuid, ${sqlLiteral(ids.legacyCase)}::uuid
)->>'requires_skill_adoption');`),
"legacy_unverifiable:true",
);
expectServiceError(
`select public.get_agentic_rectification_skill_identity(${sqlLiteral(ids.user)}::uuid, ${sqlLiteral(ids.legacyCase)}::uuid);`,
/agentic_rectification_legacy_skill_identity_unverifiable/,
);
expectServiceError(
`select public.insert_agentic_rectification_skill_run_receipt(
${sqlLiteral(ids.user)}::uuid, ${sqlLiteral(ids.legacyCase)}::uuid,
${sqlLiteral(ids.legacyTurn)}::uuid, gen_random_uuid(), 'turn',
'jyotish-birth-time-rectification', '9.0.0', ${sqlLiteral(adoptionHash)}, ${sqlLiteral(sourceCommit)}
);`,
/agentic_rectification_skill_identity_mismatch/,
);
expectServiceError(
`select public.adopt_agentic_rectification_skill_v1(
${sqlLiteral(ids.otherUser)}::uuid, ${sqlLiteral(ids.legacyCase)}::uuid,
'jyotish-birth-time-rectification', '9.0.0', ${sqlLiteral(adoptionHash)}, ${sqlLiteral(sourceCommit)}
);`,
/agentic_rectification_case_not_found/,
);
insertSession(ids.terminalSession, ids.user);
insertCase({ id: ids.terminalCase, userId: ids.user, sessionId: ids.terminalSession, status: "closed", hash: null });
assert.equal(
serviceSql(`select
(public.get_agentic_rectification_skill_identity_status(
${sqlLiteral(ids.user)}::uuid, ${sqlLiteral(ids.terminalCase)}::uuid
)->>'skill_identity_status') || ':' ||
(public.get_agentic_rectification_skill_identity_status(
${sqlLiteral(ids.user)}::uuid, ${sqlLiteral(ids.terminalCase)}::uuid
)->>'requires_skill_adoption');`),
"legacy_unverifiable:false",
);
expectServiceError(
`select public.adopt_agentic_rectification_skill_v1(
${sqlLiteral(ids.user)}::uuid, ${sqlLiteral(ids.terminalCase)}::uuid,
'jyotish-birth-time-rectification', '9.0.0', ${sqlLiteral(adoptionHash)}, ${sqlLiteral(sourceCommit)}
);`,
/agentic_rectification_case_terminal/,
);
const adopted = serviceSql(`select public.adopt_agentic_rectification_skill_v1(
${sqlLiteral(ids.user)}::uuid, ${sqlLiteral(ids.legacyCase)}::uuid,
'jyotish-birth-time-rectification', '9.0.0', ${sqlLiteral(adoptionHash)}, ${sqlLiteral(sourceCommit)}
);`);
assert.equal(JSON.parse(adopted).idempotent, false);
const replayed = serviceSql(`select public.adopt_agentic_rectification_skill_v1(
${sqlLiteral(ids.user)}::uuid, ${sqlLiteral(ids.legacyCase)}::uuid,
'jyotish-birth-time-rectification', '9.0.0', ${sqlLiteral(adoptionHash)}, ${sqlLiteral(sourceCommit)}
);`);
assert.equal(JSON.parse(replayed).idempotent, true);
assert.equal(
fixture.psql(`select coalesce(previous_skill_sha256, 'NULL') || ':' || previous_identity_status || ':' || upgrade_kind || ':' || skill_sha256
from public.agentic_rectification_skill_upgrade_receipts where case_id = ${sqlLiteral(ids.legacyCase)}`),
`NULL:legacy_unverifiable:legacy_adoption:${adoptionHash}`,
);
assert.equal(
fixture.psql(`select count(*) from public.agentic_rectification_skill_upgrade_receipts where case_id = ${sqlLiteral(ids.legacyCase)}`),
"1",
);
assert.equal(
fixture.psql(`select concat_ws(':',
(select count(*) from public.agentic_rectification_turns where case_id = ${sqlLiteral(ids.legacyCase)}),
(select count(*) from public.agentic_rectification_evidence where case_id = ${sqlLiteral(ids.legacyCase)}),
(select count(*) from public.agentic_rectification_results where case_id = ${sqlLiteral(ids.legacyCase)})
)`),
"1:1:1",
);
expectServiceError(
`insert into public.agentic_rectification_skill_upgrade_receipts (
case_id, user_id, previous_skill_name, previous_skill_version,
previous_identity_status, upgrade_kind, skill_name, skill_version, skill_sha256
) values (${sqlLiteral(ids.legacyCase)}, ${sqlLiteral(ids.user)},
'jyotish-birth-time-rectification', '9.0.0', 'legacy_unverifiable', 'legacy_adoption',
'jyotish-birth-time-rectification', '9.0.0', ${sqlLiteral(adoptionHash)});`,
/permission denied for table agentic_rectification_skill_upgrade_receipts/,
);
expectOwnerError(
`update public.agentic_rectification_skill_upgrade_receipts set skill_sha256 = ${sqlLiteral(oldHash)} where case_id = ${sqlLiteral(ids.legacyCase)};`,
/agentic_rectification_skill_receipts_are_append_only/,
);
expectOwnerError(
`delete from public.agentic_rectification_skill_run_receipts where case_id = ${sqlLiteral(openedCaseId)};`,
/agentic_rectification_skill_receipts_are_append_only/,
);
expectOwnerError(
`delete from public.agentic_rectification_skill_upgrade_receipts where case_id = ${sqlLiteral(openedCaseId)};`,
/agentic_rectification_skill_receipts_are_append_only/,
);
expectOwnerError(
`truncate table public.agentic_rectification_skill_upgrade_receipts;`,
/agentic_rectification_skill_receipts_are_append_only/,
);
expectOwnerError(
`truncate table public.agentic_rectification_skill_run_receipts;`,
/agentic_rectification_skill_receipts_are_append_only/,
);
expectOwnerError(
`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({
id: ids.caseDeleteCase,
userId: ids.user,
sessionId: ids.caseDeleteSession,
hash: oldHash,
});
fixture.psql(`insert into public.agentic_rectification_turns (id, case_id, user_message, status, model_name)
values (${sqlLiteral(ids.caseDeleteTurn)}, ${sqlLiteral(ids.caseDeleteCase)}, 'case cascade user', 'pending', 'fixture-model');`);
serviceSql(`select public.insert_agentic_rectification_skill_run_receipt(
${sqlLiteral(ids.user)}::uuid, ${sqlLiteral(ids.caseDeleteCase)}::uuid,
${sqlLiteral(ids.caseDeleteTurn)}::uuid, ${sqlLiteral(ids.caseDeleteRequest)}::uuid,
'turn', 'jyotish-birth-time-rectification', '9.0.0', ${sqlLiteral(oldHash)}, ${sqlLiteral(sourceCommit)}
);`);
fixture.psql(`delete from public.agentic_rectification_cases where id = ${sqlLiteral(ids.caseDeleteCase)};`);
assert.equal(
fixture.psql(`select concat_ws(':',
(select count(*) from public.agentic_rectification_cases where id = ${sqlLiteral(ids.caseDeleteCase)}),
(select count(*) from public.agentic_rectification_turns where id = ${sqlLiteral(ids.caseDeleteTurn)}),
(select count(*) from public.agentic_rectification_skill_run_receipts where case_id = ${sqlLiteral(ids.caseDeleteCase)})
)`),
'0:0:0',
);
fixture.psql(`delete from public.agentic_rectification_turns where id = ${sqlLiteral(ids.verifiedTurn)};`);
assert.equal(
fixture.psql(`select count(*) from public.agentic_rectification_skill_run_receipts where turn_id = ${sqlLiteral(ids.verifiedTurn)}`),
"0",
);
assert.equal(
fixture.psql(`select count(*) from public.agentic_rectification_skill_upgrade_receipts where case_id = ${sqlLiteral(openedCaseId)}`),
"1",
);
fixture.psql(`delete from public.agentic_rectification_cases where id = ${sqlLiteral(openedCaseId)};`);
assert.equal(
fixture.psql(`select count(*) from public.agentic_rectification_skill_upgrade_receipts where case_id = ${sqlLiteral(openedCaseId)}`),
"0",
);
insertSession(ids.cascadeSession, ids.cascadeUser);
insertCase({ id: ids.cascadeCase, userId: ids.cascadeUser, sessionId: ids.cascadeSession, hash: oldHash });
fixture.psql(`insert into public.agentic_rectification_turns (id, case_id, user_message, status, model_name)
values (${sqlLiteral(ids.cascadeTurn)}, ${sqlLiteral(ids.cascadeCase)}, 'cascade user', 'pending', 'fixture-model');`);
serviceSql(`select public.insert_agentic_rectification_skill_run_receipt(
${sqlLiteral(ids.cascadeUser)}::uuid, ${sqlLiteral(ids.cascadeCase)}::uuid,
${sqlLiteral(ids.cascadeTurn)}::uuid, ${sqlLiteral(ids.cascadeRequest)}::uuid,
'turn', 'jyotish-birth-time-rectification', '9.0.0', ${sqlLiteral(oldHash)}, ${sqlLiteral(sourceCommit)}
);`);
serviceSql(`select public.upgrade_agentic_rectification_skill_v2(
${sqlLiteral(ids.cascadeUser)}::uuid, ${sqlLiteral(ids.cascadeCase)}::uuid,
'jyotish-birth-time-rectification', '9.1.0', ${sqlLiteral(newHash)}, null
);`);
fixture.psqlAs(
"identity_runtime",
"identity-runtime-test-password",
`delete from identity.users where id = ${sqlLiteral(ids.cascadeUser)};`,
);
assert.equal(
fixture.psql(`select concat_ws(':',
(select count(*) from public.agentic_rectification_cases where id = ${sqlLiteral(ids.cascadeCase)}),
(select count(*) from public.agentic_rectification_skill_run_receipts where case_id = ${sqlLiteral(ids.cascadeCase)}),
(select count(*) from public.agentic_rectification_skill_upgrade_receipts where case_id = ${sqlLiteral(ids.cascadeCase)})
)`),
"0:0:0",
);
} finally {
fixture.stop();
}
});