Files
Jyotisha/docs/tasks/TASK-rectification-targeted-collect-persist-fix-20260911.md
T

7.2 KiB
Raw Blame History

TASK · 修复单:BUG-655512be9b7)验收后仍未闭合的三处——采集焦点 kind 违反表约束、引擎刷新用整网格、无新题时 GET 永远「等待收窄」(2026-09-12)

  • 基线:origin/staging @ e59dd47c(已部署;代码 512be9b7
  • 分支:codex/rectification-targeted-collect-persist-fix-20260911,基于 origin/staging
  • 执行方:coding agent;验收:Claude
  • BUG 编号起点:BUG-656(开工时核对 docs/BUG_HISTORY.md 最大号,应为 655
  • 本单是 TASK-rectification-targeted-collect-persist-20260911.md 的修复单。执行方当时只拿到该单 T0/T3 的片段(见其 PROGRESS「任务单不在本仓」),原单 §2.1 / §2.2 / §2.3 三条根因一条都没改;原单本次已随本单一并入库。

1. 验收结论(Claude2026-09-12,基于 e59dd47c

结论 依据
512be9b7 T3「空刷新不写库、候选集变更不写库、并入探针 id 与已答同名」 通过 refresh-discriminator-probes.ts refreshWriteBlockedReason / alignedProbeIdtsc --noEmit 0 错;相关测试 fail=0
原单 §2.1 定向 / 锚定 / 通用采集焦点写不进库 未通过(未做) server-focus.ts L346 仍 targetKind: input.followup.kind_hint ?? nulltargeted:<domain> 违反 agentic_rectification_conversation_focuses.target_kind CHECK20260814020000_rectification_v10_runtime.sql L124131),无后续迁移放宽
原单 §2.2 引擎刷新按剩余候选生成 未通过(未做) refinement_packet.py L707 仍 grid_times = built.candidate_timescolumn_times 只喂三列对照;refresh_probes 分支永远拿到全网格
原单 §2.3 「等待收窄」必须有载体 未通过(未做),且 512be9b7 让它变成永久状态 见 §2
真机 未做(无登录态) 需部署后按场景 0c 走

2. 512be9b7 引入的新死角(BUG-658

刷新没有新题时现在什么都不写rectification_refresh_persist_skipped reason=no_new_probes)。于是库里 refresh_count 恒为 0

  • GET decideFromDossiernarrowingExhaustionrefreshed=false 且剩余换升层非空 → refreshExhausted=falsestillNeedNarrowing 恒真 → decideRectificationcollect(..., waitToNarrowCapability):无题、无卡、can_adopt=false。GET 路由本身不调刷新,这个状态永远翻不过来。
  • 答题事务 / 空闲路径每次都会再调一次引擎(runV9CandidateScore,真机单次约 20 s),因为 shouldRefreshDatedPool 只看库里的 refresh_count
  • 由于 §1 的 §2.2 未改,刷新在本案永远是 no_new_probes,所以本案在 e59dd47c 上的表现与 6c9a0896 相同:第六题答完后「没有拿到下一个问题」,且不再出卡。

3. 决策记录(沿用原单 §3,补一条)

  1. 「刷新已尝试且无新题」必须是持久化事实:写进 inference_state.refresh_attempts[]{candidate_set_id, answer_count, result:"no_new_probes"|"new_probes", at}),不新增推理 transition 行也可以(允许走 persistV9InferenceState 之外的轻量字段,例如 latest_result.decisionReceipt 的 refresh_attempts);refreshExhausted 由「同一候选集、同一已答数下已有一次尝试记录」判定,shouldRefreshDatedPool 同源。

4. 硬红线

沿用原单 §4;另加:不得让 GET 的决策依赖只存在于内存的刷新结果;同一候选集、同一已答数下引擎刷新最多调用一次。

5. 任务分解

  • T1 采集焦点 kind 映射(BUG-656 = 原单 §2.1 / T1server-focus.ts persistCollectFocus / persistSpokenChoiceFallbacktargetKind 前经 collectFocusTargetKind(followup)(按领域映射到 CHECK 枚举:family→family_event、finance→finance_gain、relocation→relocation、relationship→relationship_change、career→career_change、education→education_milestone、health→self_health_event,其余 null);kind_hint 原值写入 expected_answer_schema.collect_kind;读取侧(answer-choice.ts / agent-run.ts / collection-question-pool.ts 的 invite / anchor / generic / targeted 判定)改读 collect_kind,找不到时回退旧字段。验收:rectification-server-focus.test.ts 用真实 CHECK 列表断言三类 followup 的 targetKind 合法、status=created;回放测试第六题答完 → 定向题「还有 N 条线能把 04:48 和 05:07 分开:…」成为活动焦点。
  • T2 引擎按剩余候选刷新(BUG-657 = 原单 §2.2 / T2refinement_packet.pyrequest.refresh_probes 为真且 column_times 非空时,把 column_times 作为 _discriminating_event_probe_listscandidate_timesevent_probes.py refresh and remaining 分支据此命中。验收:tests/test_candidate_discriminator_contract.py 加「31 格 + column_times=5」夹具,断言簇按 5 个候选重算且探针 candidate_ids 只含这 5 个;若确无边界则 public=[] 并在回执写 refresh_result="no_new_probes"
  • T3 刷新尝试持久化 + 等待收窄以「已问过」判定(BUG-658 = 原单 §2.3 / T3 + 本单 §2:按 §3.5 写 refresh_attemptsnarrowingExhaustion.refreshExhausted = 当前候选集与已答数下已有尝试记录 或 剩余换升层为空;targetedCollectExhausted = collect:targeted:* 焦点已存在且非 active 或池空;定向题落库 skipped 时同一事务把 collect:targeted:<domain> 记为 skipped 话题并按 BUG-651 出卡。客户端 rectificationQuestionGapStatesessionOutcome=collect_evidence && !stopReason && 无焦点无卡collect_waiting。验收:模拟引擎恒返回旧题 → 第一次刷新后 refresh_attempts 有记录、第二次不再调引擎、GET 决策进入定向补事或出卡;模拟定向题落库失败 → 同一事务出「目前范围」卡,不出现「没有拿到下一个问题」。
  • T4 记录BUG-656658 入历史(656 关联 BUG-648 / 442657 关联 BUG-653658 关联 BUG-655 / 651 / 652);CHANGELOGPROGRESS;场景 0c 补「定向题必须出现、无新题不重复调引擎」。

6. 让步顺序

T1 + T3 不可省(它们决定用户能不能继续);T2 可拆下一单,但拆出去时 T3 必须让「无新题」直接进定向补事。

7. 开工前置命令

git fetch origin --prune
git worktree add -b codex/rectification-targeted-collect-persist-fix-20260911 .worktrees/rectification-targeted-collect-persist-fix-20260911 origin/staging
grep -o "^## BUG-[0-9]*" docs/BUG_HISTORY.md | sort -t- -k2 -n | tail -1   # 应为 655
cd frontend && npm test -- tests/rectification-server-focus.test.ts tests/rectification-probe-pool-exhausted-20260911.test.ts tests/rectification-replay-20260911.test.ts
cd .. && .venv/bin/python -m pytest tests/test_candidate_discriminator_contract.py tests/test_rectification_event_probes.py -q

8. 验收口径

沿用原单 §8;真机:§1 序列第六题答完必须看到定向补事题;说「没有了」后看到「目前范围」卡;全程不出现「没有拿到下一个问题」;同一候选集下引擎刷新只调用一次(容器日志 rectification_refresh_persist_skipped 至多一条)。