Files
Jyotisha/PROGRESS-rectification-decision-authority-20260831.md
T
Jesse_Chen 4b1331776d
Independent Staging Quality Gate / validate (push) Successful in 12m32s
Independent Staging Quality Gate / publish (push) Has been cancelled
fix(rectification): fail closed UI and question ownership
2026-09-01 02:06:53 +08:00

243 lines
13 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-08-31)
## 基线与交付边界
- 隔离 worktree`/Users/jesse/Downloads/Copse/astrology/.worktrees/rectification-decision-authority-20260831`
- 分支:`codex/rectification-decision-authority-20260831`
- 基线:`origin/staging` @ `9aebd0474a089ed5bec1fc8e8432d0d85e93ffb6`
- A+B 已以 `9f011194f2befc23693a5b2872e8e3da5a4394c0` 推送到 `origin/staging` 并完成独立验收。
- 本次继续交付任务 C、D;最终提交前执行 `git fetch origin staging --prune`,远端仍为 `9f011194f2befc23693a5b2872e8e3da5a4394c0`,无需 rebase。不部署,不提升 `main`,不声称真实 authenticated staging flow 已验证。
## 任务 0:不变量门控
实现前先加入五条不变量并运行,五条均红,且失败原因分别落在任务书列出的病灶:
1. TypeScript 能力超过引擎 ceiling。
2. 证据不足仍允许采用/选择。
3. 候选并列或未分离仍允许采用。
4. holdout unavailable 仍允许采用。
5. public overlay 用本地字段覆盖引擎否决。
A+B 完成后五条全部转绿:`tests 5 / pass 5 / fail 0`
## 任务 A:停止原因分类化
- 新增三类 `StopClass``keep_collecting``exhausted``user_stopped`
- `insufficient_dated_events``insufficient_domains` 只进入收集流程,不再进入 `completeWithRange()`
- 真正耗尽(并列第一、用户不确定度过高)仍可结束为 review-only range。
- 用户主动停止改变的是“是否继续追问”,不是证据是否达标:允许交付区间,但不放开采用能力。
- 空候选不再返回无停止原因的 fail-open 状态,而是归入证据不足、继续收集。
- `decideNextAction()``decideRectification()` 同步演进,保留既有一致性断言。
## 任务 B:能力标志单一来源与引擎上限
- 新增严格四字段 `EngineCapabilityCeiling`
- `acceptanceAllowed`
- `selectionAllowed`
- `proposeAllowed`
- `confirmationAllowed`
- `decisionReceipt` 四字段缺失、非 boolean 或字段关系非法时 fail-closed。raw dossier receipt 还会严格校验 `accept_allowed` / `confirm_allowed` alias 与 canonical 字段一致,并校验 `display_allowed` 不得与已允许 selection 的结果冲突。
- `deliveryCapability()` 成为以下四字段的唯一计算点:
- `canAdopt`
- `selectionAllowed`
- `proposeAllowed`
- `canConfirmExactMinute`
- 本地证据、coverage、holdout、separation 门与引擎 ceiling 取交集;TypeScript 只能收紧,不能放宽。
- `overlayPublicDecision()` 对四个门禁字段取本地结果与 receipt ceiling 的逻辑与,保留候选投影不一致时的 fail-closed。
- `v9/engine-client.ts` 扩展返回四个引擎能力字段。
- `v9/decision-from-dossier.ts` 三个调用点、`core/decide-next-action.ts``v9/method-followup.ts` 已逐一处理。
- `method-followup.ts` 的 helper 不参与生产交付,只用于问题流程,因此显式传关闭 ceiling;流程走向仍可 collect/discriminate,不会因四个交付能力全 false 而静默。
- `v9/answer-choice.ts` 的采用 narration 改为绑定最终能力 `can_offer_range && can_adopt`,避免 `ready_to_adopt` 丢失同回合采用载体。
- `v9/turn-decision.ts` 继续写入 `decision.selectionAllowed`;该字段现在已是 `deliveryCapability()` 的最终结果,无另行判门。
- `frontend/src/lib/rectification-agentic/rectification-agentic/session.ts``frontend/src` 下无生产 import,属于非活跃 legacy 路径,本轮不改。
- `decideConversationalSession()` 仍未接入生产路径;既有“生产工具不得调用它”的测试约束未解除。
## B-1 grep 自证
以下六个分支函数内,四个能力字段均无 boolean 字面量赋值:
```text
collect: OK
discriminate: OK
offerRangeWithoutAdopt: OK
holdoutValidation: OK
completeWithRange: OK
finish: OK
```
## 事故输入回归
输入:
- 1 条日期证据
- 1 个 domain
- 9 个候选分数:`12,12,12,12,11,11,10,10,10`
- `trainingGateOpen=false`
- `methodCoverageAll=false`
- `snapshotCurrent=true`
- 引擎 ceiling 四字段全 false
重构前任务书记录:
```json
{
"canAdopt": true,
"selectionAllowed": true,
"nextAction": "complete_with_range",
"stopReason": "insufficient_dated_events"
}
```
当前输出:
```json
{
"canAdopt": false,
"selectionAllowed": false,
"proposeAllowed": false,
"canConfirmExactMinute": false,
"nextAction": "ask_fact_collection",
"sessionOutcome": "collect_evidence",
"stopReason": "insufficient_dated_events",
"canOfferRange": false
}
```
## 既有断言修改配额
按用户批准的新口径:
- 错误行为组:`6 / 6`
- 修改的既有断言:`10 / 12`
前三条先前未列明的断言为:
1. `frontend/tests/rectification-convergence-budget.test.ts:172``canAdopt true → false`;病灶 3,候选未分开且预算耗尽只能交付 review-only range。
2. `frontend/tests/rectification-convergence-budget.test.ts:412`label `blocked → user_history_verification_required`;病灶 1,空候选必须分类为证据不足而不是无原因 fail-open。
3. `frontend/tests/rectification-decide-next-action.test.ts:511``sessionOutcome adopt_representative → provisional_range`;病灶 4holdout unavailable 不能进入采用态。
完整清单(按错误行为组):
### 组 1:候选未分离却进入采用态(病灶 3)
1. `frontend/tests/rectification-answer-choice.test.ts:608``nextAction offer_provisional_range → ready_to_adopt`。原 fixture 为 `16/14`,回答后仍未达到 separation 门却依赖错误采用行为;fixture 调整为 `18/14`,但 prior lead 仍只有 4、单独并未达到 `MIN_SEPARATION_LEAD=8`;真正让该用例进入采用态的是 `applyHoldoutAnswer(..., "yes")` 更新 posterior 后满足 separation,并通过 holdout。正确采用态是 `ready_to_adopt`
2. `frontend/tests/rectification-convergence-budget.test.ts:172``canAdopt true → false`。预算耗尽不等于候选已分开。
### 组 2:空候选缺少停止分类(病灶 1)
3. `frontend/tests/rectification-convergence-budget.test.ts:412`label `blocked → user_history_verification_required`。空候选应继续收集日期证据。
### 组 3:无 holdout 的可信区间被当作可采用(病灶 4)
4. `frontend/tests/rectification-credible-range-projection.test.ts:146``nextAction ready_to_adopt → offer_provisional_range`。无 holdout 只能 review-only。
### 组 4:单候选但 holdout unavailable 被当作可采用(病灶 4)
5. `frontend/tests/rectification-decide-next-action.test.ts:511``sessionOutcome adopt_representative → provisional_range`。单候选不覆盖独立核对门。
### 组 5sticky holdout 失去日期精度后仍可采用(病灶 4)
6. `frontend/tests/rectification-holdout-renderable.test.ts:151``nextAction ready_to_adopt → offer_provisional_range`
7. `frontend/tests/rectification-holdout-renderable.test.ts:153``sessionOutcome adopt_representative → provisional_range`
8. `frontend/tests/rectification-holdout-renderable.test.ts:157``canAdopt true → false`
三条均由同一错误行为产生:sticky 只保留 holdout 身份,unknown precision 使其不可验证,必须 fail-closed。
### 组 6:用户停止绕过证据与 coverage 门(病灶 2
9. `frontend/tests/rectification-range-offer-deadend.test.ts:601``canAdopt true → false`。用户停止只允许展示区间。
10. `frontend/tests/rectification-range-offer-deadend.test.ts:608``canAdopt true → false`。两条同域日期事件仍不满足独立交付采用门。
`frontend/tests/rectification-holdout-renderable.test.ts``passed holdout``canAdopt === true` 保持不变;只补全 receipt fixture。
## 未放宽的门槛
- `MIN_STANDALONE_DATED_EVENTS = 3`
- `MIN_STANDALONE_DATED_DOMAINS = 2`
- `MIN_SEPARATION_LEAD = 8`
- 4 事件 / 3 domain 的 exact-minute confirmation gate
## 本地验收
- `./node_modules/.bin/tsx --test --test-name-pattern='^invariant [1-5]:' tests/rectification-decision-authority.test.ts``5/5` 通过。
- `./node_modules/.bin/tsx --test tests/rectification-answer-choice.test.ts``27/27` 通过。
- `./node_modules/.bin/tsx --test tests/rectification-*.test.ts``712 tests / 703 pass / 9 skipped / 0 fail`
- `./node_modules/.bin/tsc --noEmit`exit 0。
- `git diff --check`exit 0。
- `.gitea/workflows/**`:无改动。
## 任务 C:前端 fail-closed
- `parseRectificationCandidateResult()` 不再用 camelCase / snake_case 的 `||` 放行。字段缺失、非 boolean 或两个 alias 冲突时均为 `false`
- candidate snapshot 的 `selectionAllowed` / `canAdopt` 还必须与严格解析后的 engine receipt ceiling 取交集。receipt 缺失、字段非法、alias 冲突或明确否决时均不可采用。
- `rectification-agentic-chat.tsx` 在渲染 selection cards 前再次调用独立 receipt 防线;该防线是纵深防御,不替代后端 A+B。
## 任务 D-1:服务端问题槽是唯一问题源
- 零证据 opening 由 `buildMethodFollowupPlan()` 产生服务端 collect focus,并持久化到 `ConversationFocus.expectedAnswerSchema.prompt`
- opening 实际问题槽文案:
```text
请先说一件你记得大概时间的人生经历,比如升学、入职、搬家、结婚或生病;只记得年份也可以。
```
- 三处旧兜底已处理:通用 collect 与 choice 降级路径复用上述第一问;无法形成 collect focus 的最后兜底改为非问句“我会继续根据现有资料整理下一步。”
- 未增加模型正文正则、问号检测或字符串匹配;模型正文不参与问题槽判定。
## 任务 D-2Skill `10.0.14`
-`versions/10.0.13` 创建完整快照 `versions/10.0.14`
- 根目录 `SKILL.md``versions/10.0.14/SKILL.md` 完全一致,frontmatter 均为 `10.0.14`
- 新约束保留“至多一个主问题”,并明确问题只能由服务端 `ConversationFocus` / 界面问题槽产生;Agent 回复正文不得自行提出、复述、改写或预告问题。
- registry 中 `10.0.13``deprecated``10.0.14` 为唯一 `active``RECTIFICATION_SKILL_VERSION = "10.0.14"`
- sha256 只对运行时包目录 `skills/jyotish-birth-time-rectification/versions/10.0.14/` 计算,最终值:
```text
60234b65badd0d69206d4a9c500d674005d1cb9aa2add58c133e538de4ad0546
```
## C+D 行为断言配额
- 错误行为组:`3 / 3`
- 行为断言:`3 / 6`
- 未修改既有行为断言。
1. C`rectification-candidate-result.test.ts` 新增 1 个行为组、1 条 `assert.deepEqual`,汇总覆盖 snapshot alias 冲突、receipt deny、receipt alias 冲突与 receipt 缺失。
2. D-1`rectification-server-focus.test.ts` 新增 1 个行为组、1 条 `assert.deepEqual`,同时锁定 opening narration、持久化 focus prompt 与问题槽投影。
3. D-2`rectification-v9-contracts.test.ts` 在既有 Skill 身份测试中新增 1 条 `assert.match`,锁定“服务端唯一问题源 + 正文不得自行提问”。
## 机械版本身份同步(不计行为配额)
以下文件只同步 `10.0.13 → 10.0.14`、对应 package path、fixture、测试标题或 sha256,未改变行为语义:
- `frontend/tests/rectification-ingest-p0.test.ts`
- `frontend/tests/rectification-v9-entry-routing.test.ts`
- `frontend/tests/rectification-collect-stall.test.ts`
- `frontend/tests/skill-registry.test.ts`
- `frontend/tests/rectification-occupation-coverage-exit.test.ts`
- `frontend/tests/rectification-confirmation-gate.test.ts`
- `frontend/tests/rectification-range-offer-deadend.test.ts`
- `frontend/tests/rectification-spoken-collect.test.ts`
- `frontend/tests/rectification-v9-contracts.test.ts`(版本身份部分)
- `frontend/tests/rectification-eight-method.test.ts`
- `frontend/tests/rectification-v9-agent.test.ts`
`rectification-v9-entry-routing.test.ts` 的新建 case fixture 同步为 `10.0.14`。本轮未顺手收敛六处“skill version stays...”反模式。
## C+D 本地验收
- 五条 decision-authority 不变量:`5/5` 通过。
- C/D 定向测试:`83/83` 通过。
- `tests/skill-registry.test.ts``16/16` 通过。
- `tests/rectification-v9-case-service.test.ts``23/23` 通过;新建 case 路径未触发 `skill_registry_version_mismatch`
- `tests/rectification-*.test.ts``714 tests / 714 pass / 0 skipped / 0 fail`
- `./node_modules/.bin/tsc --noEmit`exit 0。
- root Skill 与 `versions/10.0.14`:字节一致。
- registry / frontmatter / `RECTIFICATION_SKILL_VERSION` / 测试内版本与 sha256:一致。
- `git diff --check`exit 0。
- `.gitea/workflows/**`:无改动。
## 未执行
- staging 部署与真实 authenticated flow 验证。