243 lines
13 KiB
Markdown
243 lines
13 KiB
Markdown
# 生时校正决策权威重构进度(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`;病灶 4,holdout 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`。单候选不覆盖独立核对门。
|
||
|
||
### 组 5:sticky 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-2:Skill `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 验证。
|