Files
Jyotisha/frontend/tests/database-segment-opening.test.ts
T
Jesse_ChenandClaude Opus 5.5 73605c3b8e fix(rectification): product-domain open wrapper runs as invoker; V10 turn append in history test and replay harness; rename segment migrations (BUG-1131)
- open_agentic_rectification_case_v2 (12 args) becomes SECURITY INVOKER: the
  immutable-skill ACL reconciliation leaves EXECUTE on the 11-arg open only to
  service_role, so the definer wrapper hit 42501 on every homepage/new open.
- The pending-opening read moves to owner function
  agentic_rectification_opening_pending_v1, granted to service_role only.
- History test and persisted replay harness append turns through the V10
  request-idempotent overload; the V9 overload is revoked from service_role.
- Opening test fixture adds the required birth_time_source.
- Segment migrations renamed to 20261001* so they sort after staging's
  20260930* migrations on both fresh and existing databases.
- Stale Windows replay replaced with the production-path replay (M1/M2 equal
  to accepted research, implementation_identity included).

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

143 lines
12 KiB
TypeScript

import assert from "node:assert/strict";
import { randomUUID } from "node:crypto";
import { spawnSync } from "node:child_process";
import { readFileSync } from "node:fs";
import { fileURLToPath } from "node:url";
import test from "node:test";
import { closeLocalPostgresDataPool, createLocalPostgresDataClient } from "../src/lib/db/local-postgres-client-core.ts";
import { resolveActiveSkillPackage } from "../src/lib/skill-package-registry.ts";
import { buildSegmentSummary, targetChartsForDomain } from "../src/lib/rectification-agentic/core/segment-summary.ts";
import { finalizeSuccessfulTurnExit } from "../src/lib/rectification-agentic/v9/turn-exit.ts";
import { prepareCaseSegmentChecks } from "../src/lib/rectification-agentic/v9/segment-checks.ts";
import { candidateRangeFingerprint, loadV9CaseDossier } from "../src/lib/rectification-agentic/v9/tool-service.ts";
import { dossierResponse } from "../src/lib/rectification-agentic/v9/case-dossier-response.ts";
import { startPostgresFixture } from "./helpers/postgres-fixture.ts";
const docker = spawnSync("docker", ["version"], { stdio: "ignore" }).status === 0;
const runner = fileURLToPath(new URL("../scripts/db-migrate.mjs", import.meta.url));
const golden = JSON.parse(readFileSync(new URL("./fixtures/varga-api-response.golden.json", import.meta.url), "utf8"));
test("segment opening PostgreSQL owns stable domain, full original checks and one shared-exit turn without adopting a minute", { skip: !docker && "docker unavailable" }, async () => {
const fixture = startPostgresFixture();
const url = fixture.connectionUrl("service_runtime", "service-runtime-test-password");
const previousFetch = globalThis.fetch;
try {
const migration = spawnSync(process.execPath, [runner], { encoding: "utf8", env: {
...process.env, SCHEMA_DATABASE_URL: fixture.connectionUrl("schema_owner", "schema-owner-test-password"),
} });
assert.equal(migration.status, 0, migration.stderr);
const service = createLocalPostgresDataClient(url, null, "service_role");
const skill = resolveActiveSkillPackage("jyotish-birth-time-rectification");
const userId = randomUUID();
fixture.psqlAs("identity_runtime", "identity-runtime-test-password", `insert into identity.users(id,name,email,email_verified)
values('${userId}','Synthetic Opening Control','${userId}@example.invalid',true)`);
// Explicitly fictional complete-window permission control, not an engine calibration golden.
const snapshot = { birth_date: "2000-06-14", reported_birth_time: "09:10", active_birth_time: null,
birth_time_source: "family_exact", latitude: 40, longitude: -74, timezone_id: null, timezone_offset: -4 };
const range = { start_time: "09:00", end_time: "09:20", candidate_intervals: [{ start_at: "2000-06-14T09:00", end_at: "2000-06-14T09:20" }] };
const minutes = Array.from({ length: 21 }, (_, offset) => ({ offset, date: "2000-06-14", time: `09:${String(offset).padStart(2, "0")}`,
signs: { D1: 1, D9: 2, D10: 3 } }));
const args = { p_user_id: userId, p_request_id: randomUUID(), p_intent: "new", p_session_id: null,
p_skill_name: skill.name, p_skill_version: skill.version, p_skill_sha256: skill.sha256, p_skill_source_commit: skill.sourceCommit,
p_baseline_profile_fingerprint: "a".repeat(64), p_baseline_birth_snapshot: snapshot, p_candidate_range: range, p_product_domain: "relationship" };
const opened = await service.rpc("open_agentic_rectification_case_v2", args);
assert.equal(opened.error, null, JSON.stringify(opened.error));
const caseId = (opened.data as { case_id: string }).case_id;
assert.equal((await loadV9CaseDossier(service, userId, caseId)).case.rectificationDomain, "relationship");
const retry = await service.rpc("open_agentic_rectification_case_v2", { ...args, p_product_domain: "career" });
assert.equal(retry.error, null);
assert.equal((retry.data as { should_start_opening: boolean }).should_start_opening, true, "unstarted new-case retry still runs its opening");
assert.equal((await loadV9CaseDossier(service, userId, caseId)).case.rectificationDomain, "relationship", "retry cannot drift product domain");
const write = (checks: unknown, candidateRange: unknown = range, owner = userId) => service.rpc("write_agentic_rectification_segment_checks_v1", {
p_user_id: owner, p_case_id: caseId, p_candidate_range: candidateRange, p_checks: checks,
});
const checks = { contract: "segment-opening-v1", domain: "relationship", range_fingerprint: candidateRangeFingerprint(range, "a".repeat(64)), status: "complete",
summary: buildSegmentSummary({ minutes, candidates: [], targets: targetChartsForDomain("relationship"), windowMinutes: 21, scanComplete: true }) };
const before = fixture.psql(`select to_jsonb(c)::text from public.agentic_rectification_cases c where id='${caseId}'`);
assert.ok((await write(checks, range, randomUUID())).error);
assert.ok((await write(checks, { ...range, end_time: "09:10" })).error);
const shortened = buildSegmentSummary({ minutes: minutes.slice(0, 11), candidates: [], targets: ["D1", "D9"], windowMinutes: 11, scanComplete: true });
assert.ok((await write({ ...checks, summary: shortened })).error, "short credible width is not the original full window");
const d1Only = buildSegmentSummary({ minutes, candidates: [], targets: ["D1"], windowMinutes: 21, scanComplete: true });
assert.ok((await write({ ...checks, summary: d1Only })).error, "no D1-only product opt-out");
assert.equal(fixture.psql(`select to_jsonb(c)::text from public.agentic_rectification_cases c where id='${caseId}'`), before, "invalid checks leave no partial write");
const unavailable = await write({ ...checks, status: "unavailable", summary: null, range_fingerprint: "c".repeat(64) });
assert.equal(unavailable.error, null);
assert.ok((await service.rpc("finalize_agentic_rectification_segment_consistency_v1", {
p_user_id: userId, p_case_id: caseId, p_range_fingerprint: "c".repeat(64), p_narration: "Synthetic control",
})).error, "unavailable scan cannot finalize");
// Different fingerprint replaces the unavailable proof; callers cannot invent delivery identity.
assert.equal((await write({ ...checks, delivered_turn_id: randomUUID() })).error, null);
assert.equal(fixture.psql(`select checks ? 'delivered_turn_id' from public.agentic_rectification_cases where id='${caseId}'`), "f");
const profileBefore = fixture.psql(`select to_jsonb(p)::text from public.profiles p where id='${userId}'`);
const exit = await finalizeSuccessfulTurnExit({ accounting: service, userId, caseId, action: "opening", segmentOpening: true });
assert.ok(exit?.segmentConsistency);
const after = fixture.psql(`select to_jsonb(c)::text from public.agentic_rectification_cases c where id='${caseId}'`);
const again = await finalizeSuccessfulTurnExit({ accounting: service, userId, caseId, action: "opening", segmentOpening: true });
assert.equal(again?.turnId, exit.turnId);
assert.equal(fixture.psql(`select to_jsonb(c)::text from public.agentic_rectification_cases c where id='${caseId}'`), after, "retry does not refresh timestamps/checks");
assert.equal(fixture.psql(`select to_jsonb(p)::text from public.profiles p where id='${userId}'`), profileBefore, "consistency is neither adopt nor confirm");
assert.equal(fixture.psql(`select count(*) from public.agentic_rectification_turns where case_id='${caseId}'`), "1");
assert.equal(fixture.psql(`select count(*) from public.agentic_rectification_conversation_focuses where case_id='${caseId}'`), "0");
const dossier = await loadV9CaseDossier(service, userId, caseId);
assert.equal(dossier.case.status, "closed");
const publicView = dossierResponse(dossier, [], { status: "current", requiresSkillAdoption: false } as never);
assert.equal(publicView.current_question, null);
assert.equal(publicView.choice_card, null);
assert.equal(publicView.step_state.headline, "盘型一致,不用校正");
assert.equal(publicView.next_user_action.id, "start_consultation");
assert.ok(publicView.turns.some(turn => "segment_consistency" in turn));
assert.equal(publicView.case.accepted_time, null);
assert.equal(publicView.case.confirmed_time, null);
for (const signature of ["agentic_rectification_opening_pending_v1(uuid,uuid)", "initialize_agentic_rectification_domain_v1(uuid,uuid,text)", "write_agentic_rectification_segment_checks_v1(uuid,uuid,jsonb,jsonb)",
"finalize_agentic_rectification_segment_consistency_v1(uuid,uuid,text,text)", "open_agentic_rectification_case_v2(uuid,uuid,text,uuid,text,text,text,text,text,jsonb,jsonb,text)"]) {
assert.equal(fixture.psql(`select has_function_privilege('authenticated','public.${signature}','execute')::text`), "f");
}
// BUG-1131: the product-domain open wrapper runs as the service caller, never as the owner.
assert.equal(fixture.psql("select prosecdef::text from pg_proc where oid='public.open_agentic_rectification_case_v2(uuid,uuid,text,uuid,text,text,text,text,text,jsonb,jsonb,text)'::regprocedure"), "f");
// Real captured native API scan is a separate nonunique production contract.
const req = golden.request;
const realRange = { start_time: req.start_time, end_time: req.end_time, candidate_intervals: golden.response.decision_receipt.candidate_intervals };
const real = await service.rpc("open_agentic_rectification_case_v2", { ...args, p_request_id: randomUUID(), p_product_domain: "general",
p_candidate_range: realRange, p_baseline_birth_snapshot: { birth_date: req.birth_date, latitude: req.lat, longitude: req.lon, timezone_offset: req.tz, birth_time_source: "family_exact" } });
assert.equal(real.error, null);
const realCaseId = (real.data as { case_id: string }).case_id;
globalThis.fetch = (async () => Response.json(golden.scan)) as typeof fetch;
const realChecks = await prepareCaseSegmentChecks({ accounting: service, userId, caseId: realCaseId });
assert.equal(realChecks?.status, "complete");
assert.equal(realChecks?.summary?.no_rectification_needed, false);
assert.equal(await finalizeSuccessfulTurnExit({ accounting: service, userId, caseId: realCaseId, action: "opening", segmentOpening: true }), undefined);
assert.equal(fixture.psql(`select count(*) from public.agentic_rectification_turns where case_id='${realCaseId}'`), "0");
// Disjoint and 1441-minute controls cannot masquerade as the shorter original window.
for (const invalidRange of [
{ start_time: "09:00", end_time: "09:20", candidate_intervals: [
{ start_at: "2000-06-14T09:00", end_at: "2000-06-14T09:09" },
{ start_at: "2000-06-14T09:11", end_at: "2000-06-14T09:20" }] },
{ start_time: "09:00", end_time: "09:00", candidate_intervals: [{ start_at: "2000-06-14T09:00", end_at: "2000-06-15T09:00" }] },
]) {
const control = await service.rpc("open_agentic_rectification_case_v2", { ...args, p_request_id: randomUUID(), p_candidate_range: invalidRange });
assert.equal(control.error, null);
const controlId = (control.data as { case_id: string }).case_id;
const rejected = await service.rpc("write_agentic_rectification_segment_checks_v1", {
p_user_id: userId, p_case_id: controlId, p_candidate_range: invalidRange, p_checks: checks,
});
assert.ok(rejected.error);
assert.equal(fixture.psql(`select checks is null from public.agentic_rectification_cases where id='${controlId}'`), "t");
}
const missingDomain = await service.rpc("open_agentic_rectification_case_v2", { ...args, p_request_id: randomUUID(), p_product_domain: "D1" });
assert.ok(missingDomain.error);
// Historical caller remains nullable and does not acquire the new opening contract.
const oldArgs: Partial<typeof args> = { ...args };
delete oldArgs.p_product_domain;
const legacy = await service.rpc("open_agentic_rectification_case_v2", { ...oldArgs, p_request_id: randomUUID() });
assert.equal(legacy.error, null);
const legacyId = (legacy.data as { case_id: string }).case_id;
assert.equal((await loadV9CaseDossier(service, userId, legacyId)).case.rectificationDomain, null);
assert.equal(await prepareCaseSegmentChecks({ accounting: service, userId, caseId: legacyId }), null);
} finally {
globalThis.fetch = previousFetch;
await closeLocalPostgresDataPool(url);
fixture.stop();
}
});