test(rectification): cover v9 migration rollout and regressions

- New suites: entry routing (13), evidence (11), skill/agent (13), stream (9),
  status/security (11) plus shared fake-accounting support.
- Migration static contract tests extended for the v9 agent api migration
  (run_phases, dossier/finalize/persist/accept/confirm RPCs, consent gate,
  needs_rebaseline guard, runtime flag, identity-foundation boundary).
- database-local-business.test.ts ledger + exact public table set updated
  (agentic_rectification_run_phases; BUG-127/BUG-144 boundary).
- rectification-v9-database.test.ts adds the agent-api migration test
  (flag seed, turn finalize, run phase receipt, consent rejection).
- rectification-agentic-entry.test.ts rewritten: the old tests locked in the
  hasRectificationSession + sessions.find guessing, textStream consumption and
  client-side session creation; they now assert the server-owned Case open
  flow, fullStream NDJSON, durable turns and exact-session routing.
- consultation-entrypoint / application-billing-contract rectification sections
  updated to the caseId-bound billing and open API contracts.
- docs/BUG_HISTORY.md: BUG-162 follow-up with regression record explaining why
  the old tests missed the broken entry guessing.
This commit is contained in:
Jesse
2026-08-11 17:18:08 +08:00
parent ca7484666f
commit 724fb64c1a
13 changed files with 2007 additions and 121 deletions
@@ -759,3 +759,108 @@ test("v9 legacy backfill maps statuses, keeps one resumable per user and is idem
fixture.stop();
}
});
test("v9 agent api migration applies, seeds the runtime flag and guards consent", { skip: skipWithoutDocker }, async () => {
const fixture = startPostgresFixture();
const schemaUrl = fixture.connectionUrl("schema_owner", "schema-owner-test-password");
try {
const migration = spawnSync(process.execPath, [runnerPath], {
encoding: "utf8",
env: { ...process.env, SCHEMA_DATABASE_URL: schemaUrl },
});
assert.equal(migration.status, 0, migration.stderr);
assert.match(migration.stdout, /applied 20260813010000_agentic_rectification_v9_agent_api\.sql/);
// The runtime selector flag is published and enabled.
assert.equal(
fixture.psql(`select enabled || ':' || rollout_percentage || ':' || status
from public.feature_flags where flag_key = 'rectification_runtime_version'`),
"true:100:published",
);
// Run phases table exists with RLS.
assert.equal(
fixture.psql(`select count(*) from pg_tables where schemaname='public' and tablename='agentic_rectification_run_phases'`),
"1",
);
// Set up a profile + case + pending turn, then exercise the turn
// finalize and run-phase receipt RPCs.
fixture.psqlAs("identity_runtime", "identity-runtime-test-password", `
insert into public.profiles (id, birth_date, reported_birth_time, active_birth_time, birth_time_source,
uncertainty_before_minutes, uncertainty_after_minutes, latitude, longitude, timezone_offset)
values ('66666666-6666-4666-8666-666666666666', '1997-08-08', '05:00', null, 'family_exact',
10, 10, 36.420487, 114.209936, 8);
`);
fixture.psqlAs("identity_runtime", "identity-runtime-test-password", `
insert into public.chat_sessions (id, user_id, title, theme, session_type, messages)
values ('22222222-2222-4222-8222-222222222222', '66666666-6666-4666-8666-666666666666', '生时校正', 'general', 'birth_time_rectification', '[]'::jsonb);
`);
fixture.psqlAs("identity_runtime", "identity-runtime-test-password", `
insert into public.agentic_rectification_cases (
id, user_id, session_id, status, skill_name, skill_version,
baseline_profile_fingerprint, baseline_birth_snapshot, candidate_range
) values (
'11111111-1111-4111-8111-111111111111', '66666666-6666-4666-8666-666666666666',
'22222222-2222-4222-8222-222222222222', 'draft', 'jyotish-birth-time-rectification', '9.0.0',
'a'.repeat(64),
'{"birth_date":"1997-08-08","latitude":36.420487,"longitude":114.209936,"timezone_offset":8,"birth_time_source":"family_exact"}'::jsonb,
'{"start_time":"04:50","end_time":"05:10"}'::jsonb
);
`);
fixture.psqlAs("identity_runtime", "identity-runtime-test-password", `
insert into public.agentic_rectification_turns (id, case_id, user_message, status, model_name)
values ('33333333-3333-4333-8333-333333333333', '11111111-1111-4111-8111-111111111111',
'2016年9月离开家去北京开始工作', 'pending', 'gpt-4o-mini');
`);
const service = createLocalPostgresDataClient(
fixture.connectionUrl("service_runtime", "service-runtime-test-password"),
null,
"service_role",
);
const finalize = await service.rpc("finalize_agentic_rectification_turn", {
p_user_id: "66666666-6666-4666-8666-666666666666",
p_case_id: "11111111-1111-4111-8111-111111111111",
p_turn_id: "33333333-3333-4333-8333-333333333333",
p_status: "completed",
p_assistant_message: "好的,我会先核对出生时间范围。",
});
assert.equal(finalize.error, null, rpcError(finalize.error));
const phase = await service.rpc("insert_agentic_rectification_run_phase", {
p_user_id: "66666666-6666-4666-8666-666666666666",
p_case_id: "11111111-1111-4111-8111-111111111111",
p_turn_id: "33333333-3333-4333-8333-333333333333",
p_phase: "skill.loaded",
p_tool_name: null,
p_sequence: 1,
});
assert.equal(phase.error, null, rpcError(phase.error));
const receipt = await service.rpc("get_agentic_rectification_turn_receipt", {
p_user_id: "66666666-6666-4666-8666-666666666666",
p_case_id: "11111111-1111-4111-8111-111111111111",
p_turn_id: "33333333-3333-4333-8333-333333333333",
});
assert.equal(receipt.error, null, rpcError(receipt.error));
const receiptRow = receipt.data as Record<string, unknown>;
assert.equal(receiptRow.status, "completed");
assert.equal(receiptRow.skill_version, "9.0.0");
// Consent-less confirmation is rejected even with a confirmable result.
const consent = await service.rpc("confirm_agentic_rectification_birth_time", {
p_user_id: "66666666-6666-4666-8666-666666666666",
p_case_id: "11111111-1111-4111-8111-111111111111",
p_result_id: "55555555-5555-4555-8555-555555555555",
p_time: "05:02",
p_consent_quote: "就用05:02",
p_source_turn_id: "33333333-3333-4333-8333-333333333333",
});
assert.equal(consent.error, null);
const consentRow = consent.data as { error?: unknown };
assert.equal(consentRow.error, "agentic_rectification_candidate_not_found");
} finally {
fixture.stop();
}
});