Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0193vBv6w5MV2cifdTUu9H5P
11 KiB
11 KiB
TASK · 带年月选择题问完后必须交付结果,不得停在「没有拿到下一个问题」(2026-09-11)
- 基线:
origin/staging@1fa994ea(已部署 staging;Skill 10.0.23;含 BUG-646~650) - 分支:
codex/rectification-probe-pool-exhausted-exit-20260911,基于origin/staging - 执行方:coding agent;验收:Claude
- BUG 编号起点:BUG-651(650 已被
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:06–05: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=13,margin 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 源码定位)
- 决策层把无年月性格题当成「下一道区分题」。
rectification-decision.tsdecideRectification在!separation.sufficient分支里先看probe;decision-from-dossier.tsdiscriminatorProbeIfFollowupCanAsk用buildMethodFollowupPlan(sessionOutcome="discriminate_candidates")取next_followup;method-followup.ts区分分支在带年月探针为空时走firstRenderableYearlessFollowup()(BUG-629 允许:带年月题空且 ≥2 候选未分开),把 D9 性格题包成distinguish_candidates + choice_frame返回。于是决策 =discriminate(nextAction=ask_candidate_discriminator,sessionOutcome=discriminate_candidates),而不是重设计单 S3 的「探针池空 → 交付」。 - 持久化层没有把这张卡落下来,也没有任何兜底。
answer-choice.tspersistNextInterviewIfIdle(约 L1362)显式豁免:!followup && nextAction !== "ask_candidate_discriminator" && nextAction !== "ask_holdout_validation"才走persistExhaustionCollect;否则进persistNextInterviewAfterChoice(L774 起)。后者对distinguish_candidates的 followup 调persistFocusAfterChoice → persistServerOwnedFocus(server-focus.tsL470 起),返回skipped / zero_information_gain / probe_already_answered / invalid_choice_schema / duplicate_focus任一时,openQuestionFromPersistedFocus为 null,函数最终return { hostNarration: spokenFollowupForUser(followup) ?? hostNarrationFallback, choiceReady: false }——没有焦点、没有卡;spokenFollowupForUser对区分题返回空串时??不兜底,正文也为空(本案正文确实只有「已记录…」一句)。 - 选择题路径与 GET 路径一致地「有决策、无载体」:GET 用
decideFromDossier重算同样得到discriminate_candidates,current_question因无活动焦点为 null,客户端rectificationQuestionGapState走到unavailable→ 「没有拿到下一个问题」。BUG-627 的ensureNonTerminalTurnExit只在点「接着问」时触发,不在答题事务里。 - 具体是哪一个 persist 状态让性格卡没落下来,本单开工时用 §5 T0 的回放测试打印
persist_status定死,不得猜。候选:expectedAnswerSchemaFor在state && scoring !== false && !schemaProbeId(stamped)时返回 null(candidate_split_hash形如04:45-05:15:04:46,…:varga.d9.…,需与inference_state.probes[].candidate_split_hash逐字相等才能盖probe_id);shouldSkipDiscriminatorFollowup;already_open/duplicate_focus撞旧焦点。 - 时间轴文案
rectification-surface-state.tsL391「选择题已问完,再补带年月的经历才会变」在没有结果卡时等于告诉用户「做不了」,违反 2026-09-10 口径 3。
3. 决策记录(产品负责人 2026-09-11)
- 带年月区分题池空 = 进 S3 交付(重设计单 §2 S3 的「探针池空」以带年月题为准)。训练门开、引擎
acceptance_allowed为真、带年月探针无可问时,决策必须是offer_provisional_range/adopt_representative(按capability.canAdopt),先出区间三列卡 + 一句定性 + 一句「再补什么能收窄」。 - 无年月性格题(D9 / D10
varga_style、月宿边界)不得挡在结果前面。它们仍按 BUG-629 只作 ±1 参考:交付卡之后可作为可选追问(卡下一行「还可以再答两道参考题微调排序」,点了才问),不再作为ask_candidate_discriminator的唯一探针。BUG-629 防复发「不得把 varga_style / nakshatra_boundary 加回带年月区分池」不变,本单是把它们从「必答」降为「可选」。 - 决策有动作就必须有载体:任何
nextAction在持久化后既无可渲染焦点又无正文时,同一事务内必须回落到persistExhaustionCollect(门关 → 精确缺口句;门开 → 交付卡)。撤回persistNextInterviewIfIdle对ask_candidate_discriminator / ask_holdout_validation的豁免;persistNextInterviewAfterChoice末尾不得返回空正文。 - 时间轴文案:有结果卡时写「选择题已问完,下面是当前范围」;只有训练门关时才写「再补一件带年月的经历」。禁词沿用重设计单 §2 末。
4. 硬红线
- 不改
MIN_ACCEPTANCE_EVENTS / MIN_ACCEPTANCE_DOMAINS、SCORE_DELTA、收敛门常量;不开confirmation_allowed。 - 不得把性格题重新提权或让它参与淘汰(BUG-629)。
- 不得靠客户端「接着问」按钮补救;答题事务本身必须留下载体。
- 既有测试总数不降;改断言逐条写原值 / 新值 / 原因。
- 记录与测试只用脱敏的领域 + 年月。
5. 任务分解
- T0 回放定位(不编号):用 §1 序列写
frontend/tests/rectification-probe-pool-exhausted-20260911.test.ts(可复用rectification-replay-20260911.test.ts的装置),在第六题答后断言并打印:decision.nextAction、plan.next_followup.semantic_key / choice_kind、persistServerOwnedFocus返回的status。把结果写进进度记录第一段。 - T1 决策层:带年月池空即交付(BUG-651):
decideRectification的!separation.sufficient分支里,probe只认带年月探针(probe_year > 0或source=event_probe且有 period);discriminatorProbeIfFollowupCanAsk/decideAfterInferenceChange不再把varga_style/nakshatra_boundary当discriminatorProbe传入。池空时按现有顺序落到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的豁免;persistNextInterviewAfterChoice在open不可渲染且spokenFollowupForUser为空时调persistExhaustionCollect;persistServerOwnedFocus非created/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.md(651 关联 BUG-629、BUG-442「训练门已开且无可渲染区分卡时必须给用户出路」、BUG-627);CHANGELOG.md;docs/tasks/PROGRESS-rectification-probe-pool-exhausted-exit-20260911.md;docs/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 --noEmit0 错、npm run lint0 error、相关套件 fail=0、测试总数 ≥ 基线;next build后/仍 Static、首屏 gzip ±2%。 - Python 不动则只跑
run_quality_gate.py --profile quick。 - 部署后真机:按 §1 序列走一遍,第六题答完必须看到三列区间卡与「再补什么」句,不出现「没有拿到下一个问题」。