Files
Jyotisha/docs/tasks/TASK-rectification-uncertainty-stop-20260903.md
T
Jesse_Chen 8db71aaf81 docs: product-level README, AGENTS.md split into code/reading parts, add CLAUDE.md, move task briefs to docs/tasks
- README.md is now the product/repo front door (architecture, repo map,
  local dev, test tiers, delivery flow, doc map). Engine positioning,
  VedAstro/Codex setup and the oracle/benchmark command reference move
  verbatim to docs/engine/README.md, docs/engine/vedastro-gateway.md and
  docs/benchmark/README.md. Capability badges realigned with the registry
  (91/78/8/0); tests/test_readme_badges.py was red on staging.
- AGENTS.md: Part A (environment truth, delivery, worktrees, record
  placement, bug workflow, growth freeze, frontend red lines, privacy,
  pre-work check, test tiers) and Part B (reading-rigor constraints).
  GitHub issue-tracker/triage boilerplate removed: GitHub is a read-only
  mirror. All strings locked by tests/ are preserved.
- CLAUDE.md added: roles, three working modes, task-brief sections,
  acceptance criteria, session discipline; imports AGENTS.md.
- 50 tracked TASK-*/PROGRESS-* files and 3 never-committed briefs move to
  docs/tasks/ with an index; REPO_LAYOUT.md merged into README.

Docs-only change (no gated path touched).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0193vBv6w5MV2cifdTUu9H5P
2026-09-03 06:56:06 +00:00

93 lines
9.2 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.
# 任务书 · 一道题选「一时说不好」就交付:不确定度停止规则缺样本下限(2026-09-03)
基线:`origin/staging` `1d77b5b1`。本单独立于 `TASK-rectification-adopt-flow-fix-20260903.md`BUG-501/502),但建议由同一实现者**串行**做:若 `codex/rectification-adopt-flow-fix-20260903` 尚未合入 staging,就在该分支之上继续,避免 `docs/BUG_HISTORY.md` 与测试文件冲突。
## 0. 真实环境复现(用户 09-03 反馈,事实)
用户走完开场、给了几件带日期的事之后,**整场只收到一道选择题**:
> 说到这几段短暂的恋情,回想一下你自己在亲密关系里更接近哪一种相处方式?
> A. 把日子过稳当,也在意关系里的安全感 / B. 爱聊、爱换话题,新鲜感一过就容易飘 / C. 把对方当家人养,情绪来了也藏不住 / D. 一时说不好
用户选 **D**。系统回复"已记录你的选择,并更新了候选比较。"随后直接交付:
> 已经从最初的 60 分钟收到 14:02–14:43 这 41 分钟。代表分钟是 14:43。当前最优结果是候选时间段,而不是已经确认的唯一出生分钟。临时代表时间仅用于下一轮验证与比较。
用户原话:"41 分钟跟没做有什么区别"、"全程就反馈给我一个问题,我选择了'一时说不好'就直接给我定了时间"。
## 1. 根因(静态核对,不是猜)
`frontend/src/lib/rectification-agentic/v9/decision-from-dossier.ts` `uncertaintyHighFromAnswers`~529):
```ts
const userAnswers = answers?.filter((item) => item.classified_from === "choice" || item.classified_from === "declined") ?? [];
if (userAnswers.length === 0) return false;
const uncertain = userAnswers.filter((item) => item.answer_class === "unsure" || item.classified_from === "declined").length;
return uncertain >= Math.ceil(userAnswers.length / 2);
```
**没有样本下限**1 道题选 D → `1 >= ceil(1/2) = 1``userUncertaintyHigh = true``core/rectification-decision.ts` `classifyStop` 返回 `{ kind: "exhausted", reason: "user_uncertainty_too_high" }`~214)→ `decideRectification``!separation.sufficient` 之前就命中 `completeWithRange(…, "exhausted")`~297)→ `canAdopt` 为真时 `sessionOutcome = adopt_representative``answer-choice.ts` `persistApplied``deliveryAdoptNarration` 出交付话术,`shouldContinueAfterStructuredChoice` 因 nextAction 不是 `ask_*` 而不续跑。**第一道题选"说不好"就等于按了"先这样"。**
同一方向还有一条会在第二道题触发:`apply-probe-outcome.ts``unsure` 记成 `low_information` 轮(~55),`decisionBudgetFromInference` 连续 `low_information` 计入 `plateauRounds``maxPlateauRounds = 2``references/rectification_policy.v1.json`)→ `budgetExhausted` → 同样 `completeWithRange`。即**连着两道选 D 也立刻交付**。
这条规则来自 `TASK-rectification-convergence-20260830.md` 任务 6 引入的上游 Stop Rule "user uncertainty is too high to map support/conflict reliably"`3a4396a4`)。上游语义是"样本够多且大半答不上来",本仓实现成了"任意一道答不上来"。
两个旁证:
- D 答案 `deltas` 全 0,却仍走 `appliedInference: true` 的旁白"已记录你的选择,并更新了候选比较。"——没更新任何比较。
- 交付话术不说明**为什么停**`user-copy.ts` `nonConvergingRangeNarration` / `deliveryAdoptNarration` 不接 `stopReason`),用户只看到"收到 41 分钟",不知道是自己的"说不好"把访谈停掉的。
## 2. 修法
### 2.1 P1 · 不确定度停止加样本下限(BUG-503)
1. `references/rectification_policy.v1.json` 增加 `"minUncertaintyAnswers": 3``scripts/rectification_policy.py` 读同一文件,加键不影响它;Python 侧不必读这个键)。
2. `uncertaintyHighFromAnswers``userAnswers.length < RECTIFICATION_POLICY.minUncertaintyAnswers` → 返回 `false`;达到下限后沿用现有"≥ 一半"判定。`classified_from === "declined"` 继续计入不确定,但同样受下限约束。
3. 平台期:`decisionBudgetFromInference``plateauRounds` 只在 `userAnswers.length >= minUncertaintyAnswers` 后才对 `unsure` 轮计数——实现方式二选一,实现者定并写进 BUG 条目:(a) 在 `decisionBudgetFromInference` 里按 `answered_probes` 的用户答题数做同样的下限门;(b) `apply-probe-outcome.ts``unsure` 返回新 kind `"unsure"``plateauRounds` 只数 `low_information`(即真正"答了但分不开"的轮),并把 `ROUND_KINDS` / `compose-receipt.ts` / `core/types.ts` 同步。**不得**改 `maxPlateauRounds = 2` 本身。
4. `answer-choice.ts` `composeChoiceNarration``answer_class === "unsure"` 时旁白改为"已记录。这题先不计分,换一件事问。"——`choice-action.ts``unsure` 分支,`appliedInference` 语义不变(仍持久化 inference 行,只是话术不说"更新了比较")。
### 2.2 P2 · 交付时说出停止原因
`persistApplied``adoptionNarration` / `completedRangeNarration``stopReason` 非空时,在 `progressClause` 之前加一句原因(放 `user-copy.ts`,进 `listUserVisibleCopy`):
- `user_uncertainty_too_high``前面几道题你多半选了"说不好",再问下去也分不开,先停在这里。`
- `tied_first``几个候选打成平手,问题已经分不开它们。`
- `insufficient_dated_events` / `insufficient_domains` 走的是 `collect`,不在此列。
`decisionReceipt` 已有 `stopReason`,前端不需要新字段;`nextAction` 公开 `stop_reason` 若尚未暴露则加上(只读字段,`publicNextAction`)。
## 3. 硬红线
1. 采用门 / 确认门语义不动:`publicCanAdopt``sessionOutcomeAllowsAdopt`、确认门 fail-closed 一律不改;本单只改"什么时候算 exhausted"。
2. `maxPlateauRounds``DEFAULT_MAX_DISCRIMINATION_ROUNDS = 8``EFFECTIVE_ANSWER_SAFETY_CAP = 10` 三个外层熔断不动。
3. 用户主动「先这样」(`userStopped`)仍立即交付,不受本单影响。
4. 选项、answer_class、计分服务端所有;不改 Python 引擎;Skill 10.0.14 不 bump。
5. `./node_modules/.bin/tsc --noEmit` 通过(不用 `npx tsc`);`npm run lint --prefix frontend` 0 错误;`rectification-*` / `consultation-*` / `consult-*` / `chat-*` 测试 fail=0;改动的既有断言逐条三栏说明(旧 → 新 → 保留语义)。
6. 无凭据不得声称已真实环境验证。
## 4. 测试
- `rectification-convergence-budget.test.ts` "dossier and post-inference decisions share the half-uncertain stop rule"~338):现在的 `halfUncertain`2 答 1 unsure)与 `halfDeclined`2 答 1 declined)断言 `user_uncertainty_too_high` → 改为 `stopReason ?? null === null`(旧:2 答 1 不确定即停 → 新:不足 3 答不停 → 保留语义:**达到下限后**一半不确定仍停)。补三条:1 答 1 unsure → 不停且 nextAction 仍是 `ask_candidate_discriminator`(给一个可用 probe);3 答 2 unsure → `user_uncertainty_too_high`4 答 2 unsure → 停(一半规则不变)。`evidenceDoesNotDilute` 用例改成 3 答 2 unsure + 3 条 evidence 行,语义保留(evidence 行不稀释)。
- 平台期:`rectification-inference-machine.test.ts` 或新用例——连续 2 道 unsure、用户答题数 < 3 → `budgetExhausted` 为假、决策仍出 probe;答题数 ≥ 3 后连续 2 道 unsure → 停。
- `rectification-answer-choice.test.ts`unsure 的旁白为"已记录。这题先不计分,换一件事问。",`appliedInference` 仍为 true。
- 用户可见文案:新增两句进 `listUserVisibleCopy``rectification-user-copy` 相关测试(含"不得/请写成"指令腔过滤)通过。
- 用 §0 场景写一条端到端形状测试(fake accounting):3 条带日期证据、1 个可用 probe、用户答 D → 返回体 `nextAction.type === "ask_candidate_discriminator"` 或至少不是 `adopt_representative`,且持久化了下一道题。
## 5. 开工前置
```bash
git fetch origin --prune
# 若 codex/rectification-adopt-flow-fix-20260903 未合入,则以它为起点:
git worktree add -b codex/rectification-uncertainty-stop-20260903 \
../.worktrees/rectification-uncertainty-stop-20260903 origin/codex/rectification-adopt-flow-fix-20260903
# 否则以 origin/staging 为起点。
```
`TASK-rectification-convergence-20260830.md` 任务 6(停止规则来源)、`references/upstream/evidence_thresholds.md``docs/BUG_HISTORY.md`BUG-463 起的收敛链)。BUG 编号 **BUG-503**(复发自:无;引入于 `3a4396a4`);§2.2 可并入 503 或独立成条。**行号是线索,按符号名定位。**
## 6. 验收标准
1. §0 形状:1 道题选 D → 不交付,继续出下一道题;连续 2 道 D 且答题数 < 3 → 仍不交付。
2. 3 道题 2 道 D → 交付,且交付话术第一句说明"多半选了说不好"。
3. D 答案旁白不再说"更新了候选比较"。
4. `minUncertaintyAnswers` 只在 `references/rectification_policy.v1.json` 定义一处。
5. tsc + lint + 四组测试 fail=0BUG_HISTORY 追加 BUG-503;三栏说明齐。
6. 真实环境人工清单(部署后):开场给 3 件带日期的事 → 第一道选择题选 D → 应继续出题而不是交付;连选 3 道 D → 交付并写明原因。