588 lines
28 KiB
TypeScript
588 lines
28 KiB
TypeScript
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 aclCatalog = fixture.psql(`
|
|
select string_agg(
|
|
case when has_function_privilege('historical_runtime_probe', p.oid, 'EXECUTE') then 't' else 'f' end || ':' ||
|
|
case when has_function_privilege('service_role', p.oid, 'EXECUTE') then 't' else 'f' end || ':' ||
|
|
case when has_function_privilege('anon', p.oid, 'EXECUTE') then 't' else 'f' end || ':' ||
|
|
case when has_function_privilege('authenticated', p.oid, 'EXECUTE') then 't' else 'f' end || ':' ||
|
|
case when 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'
|
|
) then 't' else 'f' end,
|
|
',' order by p.proname
|
|
)
|
|
from pg_catalog.pg_proc p
|
|
join pg_catalog.pg_namespace n on n.oid = p.pronamespace
|
|
where n.nspname = 'public' and p.proname in (
|
|
'open_agentic_rectification_case_v2',
|
|
'get_agentic_rectification_skill_identity',
|
|
'get_agentic_rectification_skill_identity_status',
|
|
'upgrade_agentic_rectification_skill_v2',
|
|
'adopt_agentic_rectification_skill_v1',
|
|
'insert_agentic_rectification_skill_run_receipt'
|
|
);
|
|
`);
|
|
assert.equal(aclCatalog, "f:t:f:f:f,f:t:f:f:f,f:t:f:f:f,f:t:f:f:f,f:t:f:f:f,f:t:f:f:f");
|
|
|
|
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,
|
|
);
|
|
assert.equal(
|
|
fixture.psql(`
|
|
select string_agg(
|
|
case when has_function_privilege('historical_runtime_probe', p.oid, 'EXECUTE') then 't' else 'f' end || ':' ||
|
|
case when has_function_privilege('service_role', p.oid, 'EXECUTE') then 't' else 'f' end || ':' ||
|
|
case when 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'
|
|
) then 't' else 'f' end || ':' ||
|
|
case when has_function_privilege('anon', p.oid, 'EXECUTE') then 't' else 'f' end || ':' ||
|
|
case when has_function_privilege('authenticated', p.oid, 'EXECUTE') then 't' else 'f' end,
|
|
',' order by p.proname
|
|
)
|
|
from pg_catalog.pg_proc p
|
|
join pg_catalog.pg_namespace n on n.oid = p.pronamespace
|
|
where n.nspname = 'public' and p.proname in (
|
|
'open_agentic_rectification_case_v2',
|
|
'get_agentic_rectification_skill_identity',
|
|
'get_agentic_rectification_skill_identity_status',
|
|
'upgrade_agentic_rectification_skill_v2',
|
|
'adopt_agentic_rectification_skill_v1',
|
|
'insert_agentic_rectification_skill_run_receipt'
|
|
);
|
|
`),
|
|
"f:t:f:f:f,f:t:f:f:f,f:t:f:f:f,f:t:f:f:f,f:t:f:f:f,f:t:f:f:f",
|
|
);
|
|
assert.equal(
|
|
fixture.psql("select pg_has_role('service_runtime', 'service_role', 'MEMBER');"),
|
|
"t",
|
|
);
|
|
assert.equal(
|
|
serviceSql(`
|
|
select string_agg(
|
|
case when has_function_privilege(current_user, p.oid, 'EXECUTE') then 't' else 'f' end,
|
|
',' order by p.proname
|
|
)
|
|
from pg_catalog.pg_proc p
|
|
join pg_catalog.pg_namespace n on n.oid = p.pronamespace
|
|
where n.nspname = 'public' and p.proname in (
|
|
'open_agentic_rectification_case_v2',
|
|
'get_agentic_rectification_skill_identity',
|
|
'get_agentic_rectification_skill_identity_status',
|
|
'upgrade_agentic_rectification_skill_v2',
|
|
'adopt_agentic_rectification_skill_v1',
|
|
'insert_agentic_rectification_skill_run_receipt'
|
|
);
|
|
`),
|
|
"t,t,t,t,t,t",
|
|
);
|
|
|
|
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
|
|
where n.nspname = 'public' and p.proname in (
|
|
'open_agentic_rectification_case_v2',
|
|
'get_agentic_rectification_skill_identity',
|
|
'get_agentic_rectification_skill_identity_status',
|
|
'upgrade_agentic_rectification_skill_v2',
|
|
'adopt_agentic_rectification_skill_v1',
|
|
'insert_agentic_rectification_skill_run_receipt'
|
|
)
|
|
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();
|
|
}
|
|
});
|