From 0219f63b4482fef055d45a10ca7979d2f07b1776 Mon Sep 17 00:00:00 2001 From: Jesse_Chen Date: Mon, 7 Sep 2026 08:57:12 +0000 Subject: [PATCH] docs(tasks): P0 stop-rescore fix brief (BUG-581~582) Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_0193vBv6w5MV2cifdTUu9H5P --- docs/tasks/README.md | 1 + ...rectification-stop-rescore-fix-20260907.md | 72 +++++++++++++++++++ 2 files changed, 73 insertions(+) create mode 100644 docs/tasks/TASK-rectification-stop-rescore-fix-20260907.md diff --git a/docs/tasks/README.md b/docs/tasks/README.md index 14430d57..82e5d13d 100644 --- a/docs/tasks/README.md +++ b/docs/tasks/README.md @@ -69,6 +69,7 @@ | `TASK-rectification-capability-fix-20260907.md` | `PROGRESS-rectification-capability-fix-20260907.md` | 能力补齐修复单:答后旁白把 `range_start/range_end`(搜索窗口)当范围比较,每题都说「范围没变」;`block_scan` 五段支持度按段内原始分求和,长时段先天占优(24/24/36/30/30 个候选);`TRACK_LABEL` 音译与产品 Vimshottari/Narayana 口径不一;BLK-001 写到 `docs/BLOCKED.md` 应回根目录 | 已验收通过(P2:block_scan 15 s 壁钟断言在门禁里可能间歇红) | `517df002`(BUG-569~570);staging 未部署,需先 Migrate Staging Database | | `TASK-rectification-declared-uncertainty-20260907.md` | `PROGRESS-rectification-declared-uncertainty-20260907.md` | 出生时间「有多确定」只在 intake 问一次:三档(医院记录 / 家人大概 ±15·30·60·120 / 时段或未知),校正窗口读档案(现在有钟点一律 ±15,声明值被忽略,BUG-571);吻合率 <60% 且代表分钟贴窗口边缘时出服务端一键放宽卡,放宽后重算并写回档案(BUG-572);窗口 >120 分钟先切三子段迭代到 ≤120 再进分钟(BUG-573);日级事件问一次可靠度;四个脚本化手测场景进 docs/testing | 已验收通过(2 P3 建议:可靠度正则去掉「记得」、档案写回改走 account-profile-patch) | `8e31680b`(BUG-571~573);迁移 `20260907020000` 待应用,部署前先 Migrate Staging Database | | `TASK-rectification-stale-compare-fix-20260907.md` | `PROGRESS-rectification-stale-compare-fix-20260907.md` | **P0**:BUG-559 把过长 `candidate_split_hash` 塞进引擎比较键,答完性格/边界卡后比较静默失败;快照过期压过用户停止;holdout 用过期领域被改写成「再说一件事」;采集轮正文双写 | 已验收通过(P2 正文双写根因未拆、P3 重算节流 Map 只增不减) | `4e0db55f`(落地编号 BUG-577~580);staging 未部署 | +| `TASK-rectification-stop-rescore-fix-20260907.md` | `PROGRESS-rectification-stop-rescore-fix-20260907.md` | **P0**:BUG-579 的停止路径用「裸重算」(`rescoreMinuteAfterWindowChange` 不重建 `inference_state`、不重放已答探针、不写 transition),停止后范围变宽、卡片变成引擎裸支持度、采用 RPC 报 `candidate_state_inconsistent`;无框区分题经 `spokenCollectFallbackFollowup` 变成 domain=other 的采集题,再次冒出开场句 `GENERIC_COLLECT_QUESTION`;holdout 采集排在带年月采集之前 | 待领取(当天须合入部署) | `codex/rectification-stop-rescore-fix-20260907`(BUG-581~582) | | `TASK-api-not-configured-mislabel-20260904.md` | `PROGRESS-api-not-configured-mislabel-20260904.md` | 16 处路由把数据库瞬断(部署切换窗口)兜底翻译成 503「服务尚未配置」;改为仅配置错误用该文案,其余 `service_unavailable`,收敛为共享 helper | 已验收 | `5483649b`(BUG-542);2 条子进程测试留 CI Node 22 复核 | | `TASK-rectification-ux-20260902.md` | `PROGRESS-rectification-ux-20260903.md` | 会话面空白假死与交互摩擦 | 已验收 | `d159f08e`(09-03 在新基线重做后合入,BUG-505~509) | diff --git a/docs/tasks/TASK-rectification-stop-rescore-fix-20260907.md b/docs/tasks/TASK-rectification-stop-rescore-fix-20260907.md new file mode 100644 index 00000000..d70b34a6 --- /dev/null +++ b/docs/tasks/TASK-rectification-stop-rescore-fix-20260907.md @@ -0,0 +1,72 @@ +# TASK · 生时校正修复单:停止后重算丢掉全部答题、采用报 `candidate_state_inconsistent`;无探针的区分题退化成开场句(2026-09-07) + +- 基线:`origin/staging` @ `63eba420`(代码头 `4e0db55f`,staging 已部署 `84a29754`,事故就是这版跑出来的) +- 分支:`codex/rectification-stop-rescore-fix-20260907` +- 执行方:coding agent;验收:Claude +- 涉及文件:`frontend/src/lib/rectification-agentic/v9/block-scan-answer.ts`(`rescoreMinuteAfterWindowChange`)、`frontend/src/mastra/rectification-v9-tools.ts`(`scoreAndPersistCurrentEvidence` 抽出)、`v9/tool-service.ts`、`v9/server-focus.ts`(`persistSpokenChoiceFallback`)、`v9/method-followup.ts`(`spokenCollectFallbackFollowup`、`precisionStageFollowup`、holdout 排序) +- BUG 编号起点:**BUG-581**(开工时 `grep -o "^## BUG-5[0-9][0-9]" docs/BUG_HISTORY.md | tail -1` 复核) +- 优先级:**P0**(采用按钮点了就报错;BUG-579 的重算把用户答过的题全部作废) + +## 1. 事故实证(staging `84a29754`,2026-09-07 第三次实测;只写结构) + +| # | 现象 | 代码定位 | +| --- | --- | --- | +| 1 | 五张卡答完,旁白说范围已收到 X–Y(11 分钟内的更窄一段);健康拒答后助手问回开场那句"从你最容易想起来的一件事开始就好——比如哪年上的大学…"(`GENERIC_COLLECT_QUESTION` 原文) | 见 §2.2 | +| 2 | 用户点「先这样,先看当前范围」→ 旁白"再问下去也分不开 A 和 B…已从 30 分钟收到 X'–Y' 这 11 分钟"——**范围比答完最后一张卡时反而变宽了**,还多出一个原本已被淘汰、在可信区间之外的分钟 | 见 §2.1 | +| 3 | 候选卡出现 4 张,"相对支持度 16 / 13 / 12 / 8"——这是引擎裸结果的 `relative_support`,不是答题后的 posterior(答题后应是 21 / 18 / 9 一类) | 同上 | +| 4 | 点任一"采用此时间" → `{"error":"候选状态已更新,请刷新后重试","code":"candidate_state_inconsistent"}` | 采用 RPC `accept_agentic_rectification_candidate_for_case_v2`(`20260906020000_adopted_credible_range.sql` L162–183)要求 `decision_receipt.inference_state` 存在且 `candidates` 非空、`revision`/`candidate_set_id` 与最新 transition 一致;否则抛 `candidate_state_inconsistent` | + +## 2. 根因 + +### 2.1 BUG-581(P0):停止路径用的是"裸重算",把推断态整个丢了 + +BUG-579 让 STOP / 无焦点停止 / 闲置持久化在快照过期时先调 `rescoreStaleMinuteSnapshotIfNeeded` → `rescoreMinuteAfterWindowChange`(`block-scan-answer.ts` L160–200)。这个函数是为"时段选定 / 放宽窗口"写的:只 `runV9CandidateScore` 然后 `persistV9Candidate`,**不调 `buildCaseInferenceState`,不带 `previous`,不写 `inference_state`,不写 transition**。而正规比较路径 `rectification-v9-tools.ts::scoreAndPersistCurrentEvidence`(L879–1190)会用上一份 `inference_state` 作 `previous`,按 `semantic_key` 重放已答探针,再把 `inference_state` 放进 receipt。 + +后果链:停止 → 裸重算 → 最新结果没有 `inference_state` → (a) 投影退回引擎裸 `relative_support`,范围变宽、被淘汰分钟复活、卡片数字变成 16/13/12/8;(b) 采用 RPC 校验 `inference_state` 为空 → `candidate_state_inconsistent`。同样的裸重算也用于时段选定和放宽窗口之后,那两条路的采用同样会报错(尚未实测,按代码必然)。 + +### 2.2 BUG-582(P1):没有探针支撑的"区分题"退化成开场句 + +`buildMethodFollowupPlan` 在几个分支会给出 **frameless** 的 `distinguish_candidates` followup(`precisionStageFollowup` 的 `lagna_frame` 分支 `domain: null`;`varga_observation` 分支;`else if (precisionCard) next = precisionCard`),它们没有 `choice_frame`。`persistServerOwnedFocus` 对无框区分题走 `persistSpokenChoiceFallback` → `spokenCollectFallbackFollowup`:`intent` 改成采集、`domain = collectQuestionDomain(null) = "other"`、`source = "method_coverage"`。`spokenFollowupForUser` 看到 `domain=other && source=method_coverage && !collect_retry` 判成 **开场**(`openingOther`),题干就是 `GENERIC_COLLECT_QUESTION`;`rectification-v9-tools.ts` L1447 的 `USER_COLLECT_QUESTION[""] ?? GENERIC_COLLECT_QUESTION` 是同一句的第二条来路。这就是"死问题"再次出现的机制,与 BUG-558 的 `other` 兜底无关,是另一条路。 + +### 2.3 P3:holdout 改成采集题后排在带年月采集之前 + +BUG-580 把 holdout 改为 `collect_method_evidence`,但它在计划里仍排在 `nextDatedCollectFollowup` 之前,所以这轮先问了健康(oos 领域)再轮到搬家;健康拒答后搬家没被问到就进了 §2.2 的分支。顺序应当是:带年月采集(`DATED_COLLECT_ORDER`)→ 职业 → holdout。 + +## 3. 决策记录 + +1. **只有一条重算路径。** 把 `scoreAndPersistCurrentEvidence` 从工具闭包抽到 `tool-service.ts`(或新文件 `v9/rescore.ts`),`rescoreMinuteAfterWindowChange` 与 `rescoreStaleMinuteSnapshotIfNeeded` 都调它。同窗口重算带 `previous`(重放已答探针);窗口变了(时段选定 / 放宽)不带 `previous`,但仍要写出新的 `inference_state`(空答案)与 transition。 +2. 采用 RPC 的校验不放宽。 +3. **无框区分题不得持久化为口述题。** `persistSpokenChoiceFallback` 只在 `followup.domain` 落在 `USER_COLLECT_QUESTION` 且 `intent` 原本就是采集时才落地;无框的 `distinguish_candidates` 一律 `skipped`。计划层:`precisionStageFollowup` / `varga_observation` 分支只在 `choice_frame` 存在时才当 `next`,否则记进 `dropped_probes(reason="frameless_distinguish")` 并继续往下走(带年月采集 → 职业 → holdout → 交付)。`spokenFollowupForUser` 的 `openingOther` 判定加 `followup.source === "method_coverage" && followup.method_id === "dasha_events" && !evidence.some(isConfirmedDated)`,即只有真正的开场才出 GENERIC;`rectification-v9-tools.ts` L1447 的 GENERIC 兜底删除,域不在表里就返回 `invalid_spoken_prompt` 不落地。 +4. holdout 采集排到 `nextDatedCollectFollowup` 与职业之后。 +5. 不动采用门、确认门、`MIN_SEPARATION_LEAD`、`_relative_support`、`minute_step=1` 指纹。 + +## 4. 任务分解 + +### 4.1 BUG-581 +- 抽出并复用 `scoreAndPersistCurrentEvidence`;`rescoreMinuteAfterWindowChange(…, { keepAnswers: boolean })`:停止/闲置 → `keepAnswers=true`(`previous` = 上一份 inference);时段/放宽 → `false`。 +- 验收:`rectification-stale-compare-fix-20260907.test.ts` 扩——(a) 5 条已答探针 + 快照过期 → STOP 后 `latest_result.decisionReceipt.inference_state.answered_probes.length === 5`,`credible_range` 与停止前一致或更窄,候选 `posterior_score` 不是引擎裸支持度;(b) 采用 RPC 夹具(`database-rectification-*.test.ts`,Docker)在 STOP 重算后能通过 `inference_state` 校验;(c) 时段选定 / 放宽后 `inference_state` 存在且 `answered_probes` 为空,采用不报 inconsistent。 + +### 4.2 BUG-582 +- 决策 3 三处改动;删除 GENERIC 兜底。 +- 验收:形状用例——7 件事、家人/健康拒答、职业未问、`precision_stage=lagna_frame` 或 d4 观察分歧、无可渲染探针 → 下一问是搬家采集或职业口述,**不是** `GENERIC_COLLECT_QUESTION`;`persistSpokenChoiceFallback` 对 `domain=null` 的区分题返回 `skipped`;`agent-voice-copy-contract` 增加"GENERIC 只允许出现在 `collect:other:*` 开场焦点"。 + +### 4.3 P3 顺序 +- holdout 后移;验收:健康 oos 领域存在且搬家未问时,下一问是搬家。 + +### 4.4 记录 +- `docs/BUG_HISTORY.md` BUG-581(复发自 BUG-579)、BUG-582(复发自 BUG-558:同一"死问题"现象、不同来路);`CHANGELOG.md`;`PROGRESS-rectification-stop-rescore-fix-20260907.md`;`docs/testing/rectification-scenarios-20260907.md` 第 0 条加"停止后卡片数字必须等于答题后的支持度,且点采用成功"。 + +## 5. 让步顺序 + +4.1 先做、当天部署;4.2 必做;4.3 可后置;4.4 不可省。 + +## 6. 开工前置命令 + +```bash +git fetch origin --prune +git worktree add -b codex/rectification-stop-rescore-fix-20260907 .worktrees/rectification-stop-rescore-fix-20260907 origin/staging +cd .worktrees/rectification-stop-rescore-fix-20260907 +ln -s /workspace/Jyotisha/frontend/node_modules frontend/node_modules +ln -s /workspace/Jyotisha/.venv .venv +cd frontend && ls tests/rectification-*.test.ts tests/consultation-*.test.ts tests/agent-voice-copy-contract.test.ts | grep -v database | xargs npx tsx --test 2>&1 | grep -E "^# (tests|pass|fail)" +```