Files
Jyotisha/docs/tasks/TASK-rectification-probe-pool-exhausted-exit-20260911.md
T

11 KiB
Raw Blame History

TASK · 带年月选择题问完后必须交付结果,不得停在「没有拿到下一个问题」(2026-09-11)

  • 基线:origin/staging @ 1fa994ea(已部署 stagingSkill 10.0.23;含 BUG-646650
  • 分支:codex/rectification-probe-pool-exhausted-exit-20260911,基于 origin/staging
  • 执行方:coding agent;验收:Claude
  • BUG 编号起点:BUG-651650 已被 1fa994ea 占用;开工时 grep -o "^## BUG-[0-9]*" docs/BUG_HISTORY.md | sort -t- -k2 -n | tail -1 核对)
  • 不改引擎计分、不改确认门、不动 SCORE_DELTA;Skill 只在改用户可见口径时升 10.0.24(本单预计不用升)

1. 事故实证(2026-09-11 staging 真机,代码 1fa994ea

流程(脱敏):开场 → 两件学业(2016-09 / 2020-06)→ 邀请「还有吗」→ 事业两件(2020-04 入职实习、2020-10 离职)→ 训练门开(3 件训练 + 1 件 holdout,2 类)→ 连续六道带年月选择题:事业 2023-05 C、感情 2023-05 C、迁居 2015-05 C、学业 2016 质量 B、事业 2024-04 A、感情 2024-04 C → 助手只回「已记录,范围没变;04:54–04:59 领先,05:0605:07 落后。」→ 时间轴写「目前范围 04:48–05:07,选择题已问完,再补带年月的经历才会变」→ 界面「没有拿到下一个问题。」。没有结果卡、没有采用入口、没有缺口句。

回执关键字段(第六题答前快照,case_revision=6):

字段 含义
gates.event_quality / domain_diversity 3/3 通过、career+education 通过 训练门开
decisionReceipt.acceptance_allowed / selection_allowed / propose_allowed 全 true 引擎已允许交付区间
inference_state.rounds 5 轮 informative,无 low_information 平台期计数为 0,收敛门未开(04:53=14 vs 05:06=13margin 2.8%
inference_state.probes 剩余 career.2023.dasha_activation(与已答 2023-05 同域同年,BUG-559 去重)、5 道无年月存在题(yearless_ungrounded_contrast 已 drop)、D9/D10 两道 varga_style 性格题、月宿边界题(IG 0.01 第六题答完后带年月的区分题池已空
interview.session_outcome(答后) discriminate_candidates(时间轴据此写「选择题已问完」) 决策层仍判「继续出区分题」
current_question / choice_card(答后) null 没有落下任何载体

2. 根因(按 1fa994ea 源码定位)

  1. 决策层把无年月性格题当成「下一道区分题」rectification-decision.ts decideRectification!separation.sufficient 分支里先看 probedecision-from-dossier.ts discriminatorProbeIfFollowupCanAskbuildMethodFollowupPlan(sessionOutcome="discriminate_candidates")next_followupmethod-followup.ts 区分分支在带年月探针为空时走 firstRenderableYearlessFollowup()(BUG-629 允许:带年月题空且 ≥2 候选未分开),把 D9 性格题包成 distinguish_candidates + choice_frame 返回。于是决策 = discriminatenextAction=ask_candidate_discriminatorsessionOutcome=discriminate_candidates),而不是重设计单 S3 的「探针池空 → 交付」。
  2. 持久化层没有把这张卡落下来,也没有任何兜底answer-choice.ts persistNextInterviewIfIdle(约 L1362)显式豁免:!followup && nextAction !== "ask_candidate_discriminator" && nextAction !== "ask_holdout_validation" 才走 persistExhaustionCollect;否则进 persistNextInterviewAfterChoiceL774 起)。后者对 distinguish_candidates 的 followup 调 persistFocusAfterChoice → persistServerOwnedFocusserver-focus.ts L470 起),返回 skipped / zero_information_gain / probe_already_answered / invalid_choice_schema / duplicate_focus 任一时,openQuestionFromPersistedFocus 为 null,函数最终 return { hostNarration: spokenFollowupForUser(followup) ?? hostNarrationFallback, choiceReady: false }——没有焦点、没有卡;spokenFollowupForUser 对区分题返回空串时 ?? 不兜底,正文也为空(本案正文确实只有「已记录…」一句)。
  3. 选择题路径与 GET 路径一致地「有决策、无载体」GET 用 decideFromDossier 重算同样得到 discriminate_candidatescurrent_question 因无活动焦点为 null,客户端 rectificationQuestionGapState 走到 unavailable → 「没有拿到下一个问题」。BUG-627 的 ensureNonTerminalTurnExit 只在点「接着问」时触发,不在答题事务里。
  4. 具体是哪一个 persist 状态让性格卡没落下来,本单开工时用 §5 T0 的回放测试打印 persist_status 定死,不得猜。候选:expectedAnswerSchemaForstate && scoring !== false && !schemaProbeId(stamped) 时返回 nullcandidate_split_hash 形如 04:45-05:15:04:46,…:varga.d9.…,需与 inference_state.probes[].candidate_split_hash 逐字相等才能盖 probe_id);shouldSkipDiscriminatorFollowupalready_open/duplicate_focus 撞旧焦点。
  5. 时间轴文案 rectification-surface-state.ts L391「选择题已问完,再补带年月的经历才会变」在没有结果卡时等于告诉用户「做不了」,违反 2026-09-10 口径 3。

3. 决策记录(产品负责人 2026-09-11)

  1. 带年月区分题池空 = 进 S3 交付(重设计单 §2 S3 的「探针池空」以带年月题为准)。训练门开、引擎 acceptance_allowed 为真、带年月探针无可问时,决策必须是 offer_provisional_range / adopt_representative(按 capability.canAdopt),先出区间三列卡 + 一句定性 + 一句「再补什么能收窄」。
  2. 无年月性格题(D9 / D10 varga_style、月宿边界)不得挡在结果前面。它们仍按 BUG-629 只作 ±1 参考:交付卡之后可作为可选追问(卡下一行「还可以再答两道参考题微调排序」,点了才问),不再作为 ask_candidate_discriminator 的唯一探针。BUG-629 防复发「不得把 varga_style / nakshatra_boundary 加回带年月区分池」不变,本单是把它们从「必答」降为「可选」。
  3. 决策有动作就必须有载体:任何 nextAction 在持久化后既无可渲染焦点又无正文时,同一事务内必须回落到 persistExhaustionCollect(门关 → 精确缺口句;门开 → 交付卡)。撤回 persistNextInterviewIfIdleask_candidate_discriminator / ask_holdout_validation 的豁免;persistNextInterviewAfterChoice 末尾不得返回空正文。
  4. 时间轴文案:有结果卡时写「选择题已问完,下面是当前范围」;只有训练门关时才写「再补一件带年月的经历」。禁词沿用重设计单 §2 末。

4. 硬红线

  • 不改 MIN_ACCEPTANCE_EVENTS / MIN_ACCEPTANCE_DOMAINSSCORE_DELTA、收敛门常量;不开 confirmation_allowed
  • 不得把性格题重新提权或让它参与淘汰(BUG-629)。
  • 不得靠客户端「接着问」按钮补救;答题事务本身必须留下载体。
  • 既有测试总数不降;改断言逐条写原值 / 新值 / 原因。
  • 记录与测试只用脱敏的领域 + 年月。

5. 任务分解

  • T0 回放定位(不编号):用 §1 序列写 frontend/tests/rectification-probe-pool-exhausted-20260911.test.ts(可复用 rectification-replay-20260911.test.ts 的装置),在第六题答后断言并打印:decision.nextActionplan.next_followup.semantic_key / choice_kindpersistServerOwnedFocus 返回的 status。把结果写进进度记录第一段。
  • T1 决策层:带年月池空即交付BUG-651):decideRectification!separation.sufficient 分支里,probe 只认带年月探针(probe_year > 0source=event_probe 且有 period);discriminatorProbeIfFollowupCanAsk / decideAfterInferenceChange 不再把 varga_style / nakshatra_boundarydiscriminatorProbe 传入。池空时按现有顺序落到 completeWithRange(..., "offer")finish("adopt_representative", stopReason="probe_pool_exhausted")。验收:§1 序列第六题答后 next_user_action.id ∈ {offer_provisional_range, adopt_representative}can_offer_range=true,快照带 range_delivery 三列,正文含一句「再补什么」且无禁词。
  • T2 性格题改可选追问BUG-651):交付卡渲染后,若 yearlessPersonalityCanAsk 为真,卡下给一个「再答两道参考题」入口;点击后才 set-focus 性格卡,答完按 BUG-629 ±1 重排并刷新卡,不改 can_adopt。验收:不点入口时不出性格卡;点了出 D9 卡,答后 rounds.kind=tie_break
  • T3 载体不变量BUG-652):persistNextInterviewIfIdle 删除对 ask_candidate_discriminator / ask_holdout_validation 的豁免;persistNextInterviewAfterChoiceopen 不可渲染且 spokenFollowupForUser 为空时调 persistExhaustionCollectpersistServerOwnedFocuscreated/already_open 的状态一律 console.warn 一条 rectification_discriminator_persist_skipped {case_id, status, semantic_key}。验收:合同测试构造 persistServerOwnedFocus 返回 skipped 的区分 followup,断言答题事务结束后 host 正文非空且(门开时)can_offer_range=true
  • T4 时间轴文案BUG-652):rectificationReadonlyRangeCopy 按决策分支写:有卡「选择题已问完,下面是当前范围」;门关「再说一件带年月的事就能继续」;其余「还在核对」。agent-voice-copy-contract 加禁词「才会变」。
  • T5 记录BUG-651/652 入 docs/BUG_HISTORY.md651 关联 BUG-629、BUG-442「训练门已开且无可渲染区分卡时必须给用户出路」、BUG-627);CHANGELOG.mddocs/tasks/PROGRESS-rectification-probe-pool-exhausted-exit-20260911.mddocs/testing/rectification-scenarios-20260907.md 加「六道选择题答完必须出卡」一条。

6. 让步顺序

T2 的入口可先只做「卡后不再问性格题」(即性格题完全不问),入口留下一单;T0 + T1 + T3 不可省。

7. 开工前置命令

git fetch origin --prune
git worktree add -b codex/rectification-probe-pool-exhausted-exit-20260911 .worktrees/rectification-probe-pool-exhausted-exit-20260911 origin/staging
grep -o "^## BUG-[0-9]*" docs/BUG_HISTORY.md | sort -t- -k2 -n | tail -1   # 应为 650
cd frontend && npm test -- tests/rectification-replay-20260911.test.ts tests/rectification-yearless-probe-downgrade-20260909.test.ts tests/rectification-exhaustion-exit-20260906.test.ts tests/rectification-decide-next-action.test.ts tests/rectification-server-focus.test.ts

8. 验收口径

  • 前端 tsc --noEmit 0 错、npm run lint 0 error、相关套件 fail=0、测试总数 ≥ 基线;next build/ 仍 Static、首屏 gzip ±2%。
  • Python 不动则只跑 run_quality_gate.py --profile quick
  • 部署后真机:按 §1 序列走一遍,第六题答完必须看到三列区间卡与「再补什么」句,不出现「没有拿到下一个问题」。