Files
Jyotisha/TASK-rectification-provisional-adopt-20260901.md
T
Jesse_Chen 422fc65b22
Independent Staging Quality Gate / validate (push) Successful in 15m33s
Independent Staging Quality Gate / publish (push) Has been cancelled
docs(rectification): add provisional-adopt task brief
采用门对齐引擎语义:provisional 采用成为一等成功出口。
记录三权威冲突根因、经授权推翻的旧红线、以及本地 1993
分钟级收敛系答案泄漏(不构成 web 端目标)的核查结论。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0155nFCgCHtoA7jhSDGmZmMu
2026-09-01 08:01:25 +00:00

157 lines
15 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 任务书 · 采用门对齐引擎语义:provisional 采用成为一等成功出口(2026-09-01)
基线:`origin/staging` @ `75fc456d`
## 0. 决策记录(必读,本轮推翻旧红线是经产品拍板的授权变更)
上一轮任务书(`TASK-rectification-nonterminal-exit-20260901.md`)写有红线:"不得放宽 `deliveryCapability`"、"任何让事故 case 变成 `canAdopt: true` 的改动都是错的"。**该红线已由产品负责人于 2026-09-01 显式推翻**,理由见下方根因:现行 `deliveryCapability` 把"可采用"绑在了数学上不可达的条件上,导致**没有任何真实用户能走完一次成功流程**。本轮的目标:
> **当引擎按方法论判定可以出牌时,用户必须真的能采用"代表分钟 + 可信区间"provisional)。分钟级分离与 holdout 只保留给"唯一分钟确认"这道门。**
依据(三个权威本来就一致,只有 TS 层跑偏):
- 引擎 receipt`rectification-candidate-policy-v2`):`acceptance_allowed` 四门(候选存在 / ≥3 可评分事件 / ≥2 领域 / 日期质量)+ `propose_allowed`4 事件/3 领域/诊断稳定 **或** 事件吻合率 ≥80%)。它老实承认相邻分钟不可分(本 case `indistinguishable_width_minutes: 29`),所以它的交付物定义就是"代表时间 + 区间"。
- V10 Skill 文本(10.0.14`references/candidate-comparison.md`):"唯一领先和宽度≤5 **只挡确认门**,不挡出示代表性时间卡。"——**skill 文本无需改动,本轮不 bump skill 版本。**
- 上游方法论(yinduzhanxing `interview_playbook.md`):最终交付措辞就是"当前最优结果是候选时间段…临时代表时间仅用于下一轮验证与比较"。
**不放宽的东西**(新红线,见 §3):唯一分钟确认门、表达边界、证据下限、引擎 ceiling 交集。
---
## 1. 事故实证
真实 case `f83d9b42-0ac7-411b-9e43-95059b2ede43`Skill `10.0.14`,快照已存档(向任务发起人索取原始 JSON)。关键形状:
- 5 条 confirmed 证据(education×1 被引擎划为 holdout、relationship×2、career×2),3 个领域;5 道区分题已答完;候选从 04:45–05:15(30 分钟)收敛到 **05:0005:077 分钟)**,熵 2.00→0.86。流程本身工作正常且收敛良好。
- 引擎 receipt`acceptance_allowed: true``selection_allowed: true``propose_allowed: true``event_fit_rate.band: "high"`80%)、`margin_percent: 19.94`
- TS overlay(用户实际看到的):`can_adopt: false``selection_allowed: false``propose_allowed: false``choice_card: null``interview.type: "offer_provisional_range"`
- 结果:用户答完约 10 道题、花掉点数,得到一句"当前可信区间是 05:0005:07"的文字,没有候选卡、什么都存不下来,然后被继续追问"有没有记得住时间的收入变化"exhaustion 轮换:finance→occupation→health_pressure→relocation→career→relationship→通用兜底),无限循环。
## 2. 根因(已静态溯源逐条核实)
### 根因 1 · `deliveryCapability` 的两个条件数学上不可达
`frontend/src/lib/rectification-agentic/core/rectification-decision.ts``deliveryCapability`(约 :196,按符号名定位):
```ts
const locallySelectable = separation.ranked.length > 0
&& !coverageBlocks
&& stopClass?.kind !== "keep_collecting"
&& stopClass?.kind !== "exhausted"
&& separation.sufficient // ← 不可达之一
&& holdout === "passed"; // ← 不可达之二
```
- **`separation.sufficient` 不可达**:需要 top-2 领先 ≥ `MIN_SEPARATION_LEAD=8`,计分制每题 ±2。逐条核对事故 case `inference_state.probes``expected_outcomes`:**所有剩余可问的题(dasha 边界题、nakshatra 边界题)都把 05:00 与 05:07 分在同一组**,答什么都同涨同跌。唯一能分开这两个分钟的是 d24/d12 等 varga 对比题,但全部被 `yearless_ungrounded_contrast` 政策丢进 `dropped_probes`。lead 永远停在 5。
- **`holdout === "passed"` 事实上不可达**`ask_holdout_validation` 分支在 `decideRectification` 里位于 coverage 检查之后、且要求 probe 耗尽;即使问到并通过,第一条仍然封死 `canAdopt`
- 这不是单 case 现象:任何"约 5 条真实事件 + 31 分钟候选网格"的普通用户都落在同一形状上。**成功率结构性为零。**
### 根因 2 · 引擎与 TS 权威冲突时,TS 永远向"否"裁决
引擎与 skill 文本都认为本轮可出牌;TS `deliveryCapability`(来自 9f011194/4b133177 两轮收紧)单方面追加了分离+holdout 要求。overlay`decision-from-dossier.ts``overlayPublicDecision`)取交集,于是引擎的"可以"永远被 TS 的"不行"覆盖。
### 根因 3 · "本地 Agent 能收敛到 1-2 分钟"不构成 web 端目标(答案泄漏,勿对标)
有一组本地对话记录(yinduzhanxing 根目录 `1993生时校正对话-*.txt`)显示本地 Agent 调用同一 skill 能把 1993 案例收敛到 14:49。**那不是校时能力,是答案泄漏**:本地仓里存有该用户的既有结论(`qizheng_1993_native_current_report_2026_08_26.md` 记录 14:49:00、候选段表钉死 14:48-14:50、`classical_zr_known_user_case_calibration_1993` 回归包),Agent 在对话里两次自认"14:49 主要被仓里的已知校准档锁住,不是单靠回答算出来的",随后更把 `target_minute=14:49` 作为回归提示直接写进了脚本(该污染只在上游 `codex/add-birth-time-rectification-skill` 分支 commit 3bb90620**已核实未进入本产品仓与 vendored skill 快照**`grep pl9_1993|target_minute|regression_only` 为零命中)。web 端面对无答案库的陌生用户,7 分钟不可分区间 + 80% 拟合才是 5 条事件的真实信息量上限。任何人(包括测试)不得以"收敛到唯一分钟"作为本轮验收口径,也不得把该上游分支的回归提示代码引入产品。
### 附带确认(本轮不修)
- coverage 死锁**不存在**`exhaustionSpokenCollectFollowup` 的轮换在 family/education/finance 之后会问 occupation,且拒答(`occupationCollectFocusClosed`)也算覆盖。coverage 门保留。
- 引擎 receipt 的 `acceptance_allowed``diagnostic_quality.passed` 不联动,是已记录的引擎侧特性(见 BUG_HISTORY / 上轮任务书),本轮不动引擎。
- 上一轮的非终态出口闸(`finalizeSuccessfulTurnExit`)工作正常,本 case `current_question` 非空。保留。
---
## 3. 硬红线(本轮新版)
1. **唯一分钟确认门一个字不放宽。** `canConfirmExactMinute` 必须继续要求:分离充分 + holdout passed + `confirmationAllowed` + 引擎 `confirmation_allowed`(引擎恒 falsefail-closed)。改完后任何路径下 `can_confirm_exact_minute` 仍不得为 true。
2. **表达边界不放宽。** "不可分区间 / 代表性候选 / 不是已确认的唯一出生分钟"等措辞、`REPRESENTATIVE_MINUTE_DISCLAIMER``RECTIFICATION_TERMINATION_COPY` 保持;采用后写入 `accepted``active_birth_time`),**不得**写 `confirmed`
3. **证据下限不放宽。** `keep_collecting`< `MIN_STANDALONE_DATED_EVENTS=3` 或 < `MIN_STANDALONE_DATED_DOMAINS=2`)时仍不得采用。四个阈值常量(`MIN_SEPARATION_LEAD=8`、3/2、3/2)数值不动——本轮只改它们**参与哪道门**,不改它们的值。
4. **引擎 ceiling 仍是硬上限。** overlay 对引擎结果只能收紧,不能放宽:receipt 不自洽或 `acceptance_allowed=false` ⇒ 采用仍关死(`engineCapabilityCeilingFromReceipt` 的 fail-closed 语义不动)。
5. **coverage 门保留。** `coverageBlocks`(含 occupation)仍挡采用——它经 exhaustion 轮换可满足,且与 skill 文本一致。
6. **不 bump skill 版本,不改 `skills/**`。** skill 文本已与本轮目标语义一致。
7. **不修引擎(Python 侧)。** 本轮只动 TS 决策层与必要的投影/UI。
8. 推 staging 前 `cd frontend && ./node_modules/.bin/tsc --noEmit` 必须通过。**不要用 `npx tsc`**(空 worktree 会装到假包 `tsc@2.0.4`)。
9. 不改 `.gitea/workflows/**`;不在有未提交改动的工作树上切分支;不自行把 staging 提升到 main。
10. 作者无 staging 环境凭据,根因来自静态溯源 + 事故快照。你若同样无凭据,**不得声称已在真实环境验证**。
让步顺序:**用户能拿到诚实且可采用的结果 > 确认门与表达边界不放宽 > 功能与测试不回归 > 代码整洁**。
## 4. 开工前置
```bash
git fetch origin --prune
git worktree add -b codex/rectification-provisional-adopt-20260901 \
../.worktrees/rectification-provisional-adopt-20260901 origin/staging
```
`docs/research/pre_work_error_ledger.md`,跑 `scripts/pre_work_check.py`,读 `frontend/AGENTS.md`。在 `docs/BUG_HISTORY.md` 检索 **BUG-456 / BUG-459** 及上两轮记录。**行号只是线索,按符号名定位。**
---
## 任务 0(门控)· 先写不变量,先让它红
`frontend/tests/rectification-decision-authority.test.ts`(或新文件 `rectification-provisional-adopt.test.ts`)加入:
1. **事故形状回归(核心)**:按 `f83d9b42` 的真实形状构造 dossier——5 条 confirmed 证据(education/relationship×2/career×2education 为 holdout usage)、inference 5 题已答、活跃候选 05:00=24 / 05:07=19 / 04:53=8lead=5 < 8)、引擎 receipt `acceptance_allowed/selection_allowed/propose_allowed` 全 true、`confirmation_allowed=false``diagnostic_quality.passed=false`、oos_blind_prompts 剩 family/finance/health_pressure、occupation 未覆盖、family 已拒答。断言:
- a) occupation 覆盖(confirmed 或拒答关闭)后:`can_adopt === true``selection_allowed === true``credible_range``["05:00","05:07"]``representative_time === "05:00"`
- b) 任何情况下 `can_confirm_exact_minute === false`
- c) occupation 仍未覆盖时:`can_adopt === false``current_question` 语义上存在下一步(沿用上一轮不变量)。
2. **证据下限保留**:同形状但只有 2 条事件或 1 个领域 ⇒ `can_adopt === false` 且 nextAction 为收集类。
3. **引擎上限保留**:同形状但 receipt `acceptance_allowed=false`(或字段不自洽触发 fail-closed ceiling)⇒ `can_adopt === false`
4. **确认门冻结**:构造分离充分 + holdout passed + 引擎 `confirmation_allowed=false``can_confirm_exact_minute === false`;引擎四字段自洽且 confirmation true 的合成形状下才允许 true(现网引擎恒 false)。
跑一遍,确认 1a 是红的、其余按当前行为核对。**1a 全绿说明测试没写对,重写。**
## 任务 AP0)· `deliveryCapability` 重构
`core/rectification-decision.ts`,按符号名定位 `deliveryCapability`
- `locallySelectable`(供 `canAdopt` / `selectionAllowed` / `proposeAllowed`)改为:
```
ranked.length > 0
&& !coverageBlocks
&& stopClass?.kind !== "keep_collecting"
```
即**移除** `separation.sufficient`、`holdout === "passed"`、`exhausted` 三个条件。理由:分离与 holdout 移交确认门;`exhausted`tied_first / user_uncertainty_too_high)按引擎与 skill 语义只影响措辞与确认门——tie 时引擎照常给出代表候选,unsure 答案本来就不计分。
- `canConfirmExactMinute` 显式补回严格条件:
```
locallySelectable
&& separation.sufficient
&& holdout === "passed"
&& confirmationAllowed
&& engineCeiling.confirmationAllowed
```
(红线 1:净效果必须与现状等价或更严。)
- `capability` 已通过 `...capability` 展开进入 `collect/discriminate/holdoutValidation/offerRangeWithoutAdopt/completeWithRange/finish` 所有出口,无需逐出口改;确认无出口自行覆写这四个字段。
- `isNonConvergingRangeOffer``canOfferRange && !canAdopt && ...`)语义不变:capability 放行后它自然只在 coverage 未满足或证据不足时为真,exhaustion 收集兜底照旧工作。确认 `answer-choice.ts` 的 `persistNextInterviewIfIdle` / `persistExhaustionCollect` 在新语义下行为正确(occupation 覆盖前仍会追问;覆盖后进入可采用交付,不再轮询)。
## 任务 B(P0)· 交付点必须真的出牌
capability 放行只是数据层。逐条确认交付链路,缺哪补哪:
1. **投影**`decideFromDossier` → `overlayPublicDecision` → GET case 快照中 `can_adopt/selection_allowed` 为 true 时,`latest_result.candidates` 与 `interview` 投影能驱动前端候选卡(`rectification-agentic-chat.tsx` 的 `RectificationCandidateCards` / `birth-time-candidate-result`)。**用户不应需要再发一条消息才看到卡**——GET 刷新即可见。
2. **next_user_action**`decideConversationalSession` / `buildNextUserAction``method-followup.ts`)在 offer/complete 且 `canAdopt=true` 时应产出 `adopt_representative`(或等效采用引导),且该轮零追问(skill:不得同一回复既要求补证据又提供采用)。
3. **采用 RPC**`cases/[caseId]/candidates/accept` 链路上若存在 `selection_blocked` 类服务端校验,确认其读取的是新语义下的投影(采用不被旧字段挡住);采用后进入既有 `verify_adopted_time` 核前事流程,不改其逻辑。
4. **叙述**offer 轮正文按 skill §9 出验证报告(候选窗、代表分钟、相对支持、八法理由),并保留"不可分区间/代表性候选"边界句。`nonConvergingRangeNarration` 在可采用时不应再是唯一出口。
## 任务 C(P1)· 既有断言清点与治理
- 上一轮的下列不变量**按本任务书 §0 授权修改**,逐条在 PR 里写"原断言 → 新断言 → 为什么":分离不足不采用、holdout unavailable/not passed 不放行采用、`offer_provisional_range` 恒 `can_adopt=false`、(如存在)tied 不采用。
- 下列不变量**必须保持全绿**:证据不足不放行、引擎 ceiling 交集 fail-closed、确认门 fail-closed、非终态出口闸(BUG-456 组)、`read_only` 无副作用。
- 修改断言配额:仅限上述授权组;授权组之外不得改既有断言,遇到冲突停下汇报。
- `docs/BUG_HISTORY.md` 追加一条:定性为"策略死结"(不是回归),记录三权威冲突、被推翻的红线出处、授权来源与日期。
## 验收标准
1. `cd frontend && ./node_modules/.bin/tsc --noEmit` exit 0。
2. `npx tsx --test tests/rectification-*.test.ts` fail=0`skill-registry.test.ts` 16/16。测试总数允许变化(授权断言组改动 + 新增不变量),在 PR 里给出新基线数并解释增减。
3. 任务 0 的四组不变量全绿。
4. **事故形状前后对照**贴进 PR:改动前 `can_adopt=false` + finance 追问循环;改动后 occupation 覆盖 ⇒ `can_adopt=true` + 候选卡可见 + `can_confirm_exact_minute=false`。
5. 说明"新增决策分支不会绕过 capability 单点"的机制(capability 只在 `deliveryCapability` 一处计算、经展开进入全部出口,不得在出口处覆写)。
6. 未在真实环境验证的部分如实注明。