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
+17
View File
@@ -2766,6 +2766,23 @@
- 复发自:无(新 Agentic Domain 主链;旧防线缺失的直接原因见下)
- 修复版本:本地 staging 候选(未 push / deploy
### 后续推进(2026-08-13 · stream agent execution + entry routing lane
- 状态:resolvedlocal candidate;真实 PostgreSQL fixture 待远端 runner 执行)
- 最近更新:2026-08-13
- 现象:旧 `/api/rectification/agent` 仍消费 `textStream`,客户端 history 可覆盖服务端历史,`maxSteps=8` 全局固定,billing identity 是 sessionId;旧工具让模型传 `candidate_range/events[]``confirmedGate` 在进程内;UI 的“思考状态”不能证明 Skill/工具真的执行过,候选卡从 Agent 文本或隐藏 sentinel 解析。
- 根因:第一轮 V9 runtime 落地了 Case/Evidence/Turn/Receipt 表与 open API,但 agent 执行层仍是旧 Direct Agentic 传话筒:没有 durable turnpending→completed)、没有 fullStream→allowlist NDJSON 映射、没有 Case-ref 工具、没有 Skill 真实加载证据、没有 DB 驱动的 runtime selector。
- 修复:
1. 重构 `agentic-rectification.ts`:系统提示词压缩为约 25 行高优先边界(不再复制 gate→scan→score→diagnostics);固定加载 `skills/jyotish-birth-time-rectification``maxSteps` 按 action 有界(opening/read-only 6、evidence 8、rescore 12、accept/confirm 6+ 硬上限 16 + 重复工具调用检测(>3 次相同调用中止)。
2. 新增十个 Case-ref 工具(`rectification-v9-tools.ts`):read-case / propose-evidence / confirm-evidence / revise-evidence / compare-candidates / read-diagnostics / offer-candidates / accept-candidate / confirm-birth-time / close-caseinput 只含 caseId/sourceTurnId/evidenceId/resultId/candidateId/quote/proposedKind 等最小引用,绝不接 userId/出生资料/range/events[]/分数/权限开关;每个工具走 RLS 仅 service_role 的 RPCevidence ledger、指纹缓存、receipt、幂等);`accepted``confirmed` 由 RPC 分列,confirm 需要 confirmation gate + 用户原话 consent quote 原文匹配;candidate 相同 evidence/range/engine 指纹复用缓存。
3. `/api/rectification/agent` 重写为 Case-ref API:请求仅 caseId/sessionId/requestId/action/message;服务端验证 Case↔Session exact binding 与 `chat_sessions.agentic_rectification_case_id`;客户端 history 不再进入上下文,每轮先从持久化 dossier 读取 turns/evidence;先原子 pending Turn,成功才 completed(半截文本永不成为 settled history);消费 `result.fullStream` 并按 allowlist 输出 NDJSONrun.started/skill.started/skill.loaded/case.loaded/evidence.*/candidates.*/diagnostics.completed/candidate.accepted/birth_time.confirmed/answer.delta/run.completed/run.failed),reasoning/raw/provider metadata/tool args/results/出生资料/评分/DB 错误永不透传;skill 加载证据来自真实框架(`agent.getSkill` 预校验 + fullStream skill tool-call/tool-result),首轮无 skill.started/loaded 允许一次受控重试,仍失败则 Turn failed/retryable 并释放用量;billing identity 绑定 `rectification:case:{caseId}`opening/read-only 不扣,首次实质运行预留一次,恢复/重试不重复;断线 abort 传播释放。
4. 新增向前业务迁移 `20260813010000_agentic_rectification_v9_agent_api.sql`case dossier / case compute(含 baseline 但不向模型暴露)/ turn finalize / candidate persist(指纹缓存) / case-scoped accept / consent-gated confirm / guarded transition / needs_rebaseline profile guard trigger / `agentic_rectification_run_phases`(持久化 skill.started/loaded 等 phases/ turn receipt 读取;`rectification_runtime_version` feature flagpublished、100%config version=v9 legacy_mode=readonly),DB 驱动 selector,两个 runtime 不可能同时写 profile/扣费/confirm。
5. 前端:`page.tsx` 删除 `hasRectificationSession``sessions.find(sessionType==='birth_time_rectification')`;新增 `openRectificationFromHomepage/openRectificationSession(sessionId)/startNewRectification`,全部请求服务端 Case open API 并使用返回的 exact sessionId/caseId;首页 CTA 由 entry-summary 驱动(开始/继续上次/再次校正);侧边栏校正 Session 点击走 intent=session + 精确 sessionIdterminal 只读 + “再次校正”);`shouldStartOpening` 只来自服务端;聊天组件改为 caseId/sessionId/readonly/shouldStartOpening 初始化并从持久化 Turns 恢复,候选卡来自 Candidate Snapshot API,活动展示来自真实 NDJSON + 持久化 receipt(可折叠“本轮做了什么”,不显示 reasoning),删除本地 timer 模拟与隐藏 sentinel。
6. 新增 `GET cases/[caseId]?sessionId=` 返回持久化 turns/evidence/latest_result/逐轮 receipt 支持刷新恢复;新增 `POST cases/[caseId]/candidates/accept`(UI 候选卡直连,不扣费、幂等、case-scoped)。
- 验证:新增 `rectification-v9-entry-routing.test.ts`13)、`rectification-v9-evidence.test.ts`11)、`rectification-v9-agent.test.ts`12)、`rectification-v9-stream.test.ts`9)、`rectification-v9-status-security.test.ts`11);`rectification-v9-migration.test.ts` 扩 9 项静态合同;`rectification-v9-database.test.ts` 扩 agent API 迁移/flag/consent 测试(本机无 Docker 按环境 skip);重写 `rectification-agentic-entry.test.ts`(旧测试锁死了 `hasRectificationSession + sessions.find` 与 maxSteps=8、textStream、sessions.find 复用等错误行为,全部改为断言 V9 服务端路由);`consultation-entrypoint.test.ts` / `application-billing-contract.test.ts` 的 rectification 段同步为 caseId 绑定契约。本机运行:v9 聚焦 107 通过 / 0 失败 / 6 Docker skip;完整 frontend 套件 1231 通过 / 18 失败(全部为无 Docker/PostgreSQL 的环境类既有失败,基线 20 失败,本轮未新增环境失败);`tsc --noEmit` 仅剩 6 个既有测试文件错误(均未触碰);ESLint 0 error`git diff --check` 通过。
- 防复发(补充):任何新 rectification 前端逻辑不得再根据消息数/候选存在/session 排序推断 Case 状态;`/api/rectification/agent` 只能消费 fullStream 并输出 allowlist NDJSON;工具输入 schema 必须 strict 且只含最小引用;首轮必须保留真实 skill 加载证据;billing request identity 只能绑定 caseIdDB 驱动 feature flag 是 runtime selector 的唯一来源。
- 修复版本:本地 staging 候选(未 push / deploy
### 旧防线为何没拦住(hasRectificationSession + sessions.find()
- `hasRectificationSession` 只断言“存在任意校正 Session”,不区分 draft / collecting / candidate_ready / candidate_accepted / confirmed / closed,因此已完成会话始终被当作可继续,且没有服务端 Case 状态可被测试断言。