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

9.2 KiB
Raw Blame History

任务书 · 一道题选「一时说不好」就交付:不确定度停止规则缺样本下限(2026-09-03)

基线:origin/staging 1d77b5b1。本单独立于 TASK-rectification-adopt-flow-fix-20260903.mdBUG-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):

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) = 1userUncertaintyHigh = truecore/rectification-decision.ts classifyStop 返回 { kind: "exhausted", reason: "user_uncertainty_too_high" }~214)→ decideRectification!separation.sufficient 之前就命中 completeWithRange(…, "exhausted")~297)→ canAdopt 为真时 sessionOutcome = adopt_representativeanswer-choice.ts persistApplieddeliveryAdoptNarration 出交付话术,shouldContinueAfterStructuredChoice 因 nextAction 不是 ask_* 而不续跑。第一道题选"说不好"就等于按了"先这样"。

同一方向还有一条会在第二道题触发:apply-probe-outcome.tsunsure 记成 low_information 轮(~55),decisionBudgetFromInference 连续 low_information 计入 plateauRoundsmaxPlateauRounds = 2references/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": 3scripts/rectification_policy.py 读同一文件,加键不影响它;Python 侧不必读这个键)。
  2. uncertaintyHighFromAnswersuserAnswers.length < RECTIFICATION_POLICY.minUncertaintyAnswers → 返回 false;达到下限后沿用现有"≥ 一半"判定。classified_from === "declined" 继续计入不确定,但同样受下限约束。
  3. 平台期:decisionBudgetFromInferenceplateauRounds 只在 userAnswers.length >= minUncertaintyAnswers 后才对 unsure 轮计数——实现方式二选一,实现者定并写进 BUG 条目:(a) 在 decisionBudgetFromInference 里按 answered_probes 的用户答题数做同样的下限门;(b) apply-probe-outcome.tsunsure 返回新 kind "unsure"plateauRounds 只数 low_information(即真正"答了但分不开"的轮),并把 ROUND_KINDS / compose-receipt.ts / core/types.ts 同步。不得maxPlateauRounds = 2 本身。
  4. answer-choice.ts composeChoiceNarrationanswer_class === "unsure" 时旁白改为"已记录。这题先不计分,换一件事问。"——choice-action.tsunsure 分支,appliedInference 语义不变(仍持久化 inference 行,只是话术不说"更新了比较")。

2.2 P2 · 交付时说出停止原因

persistAppliedadoptionNarration / completedRangeNarrationstopReason 非空时,在 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. 采用门 / 确认门语义不动:publicCanAdoptsessionOutcomeAllowsAdopt、确认门 fail-closed 一律不改;本单只改"什么时候算 exhausted"。
  2. maxPlateauRoundsDEFAULT_MAX_DISCRIMINATION_ROUNDS = 8EFFECTIVE_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):现在的 halfUncertain2 答 1 unsure)与 halfDeclined2 答 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_high4 答 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.tsunsure 的旁白为"已记录。这题先不计分,换一件事问。",appliedInference 仍为 true。
  • 用户可见文案:新增两句进 listUserVisibleCopyrectification-user-copy 相关测试(含"不得/请写成"指令腔过滤)通过。
  • 用 §0 场景写一条端到端形状测试(fake accounting):3 条带日期证据、1 个可用 probe、用户答 D → 返回体 nextAction.type === "ask_candidate_discriminator" 或至少不是 adopt_representative,且持久化了下一道题。

5. 开工前置

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.mddocs/BUG_HISTORY.mdBUG-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 → 交付并写明原因。