muse-agent-example/docs/2026-07-21-升格级联重跑设计.md
zizi b9ff4d0b40 修复: 阻断历史单窗重跑破坏成长链
以正文证据校验实体出场章,补齐失败恢复和参数边界;历史窗口重跑在写入前失败关闭,仅保留末窗安全重试。记录 work8 全书前滚重建的 P0/P1 边界。
2026-07-21 16:08:15 +08:00

245 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.

# 升格级联重跑设计
> 日期:2026-07-21
> 状态:已完成独立评审,P0/P1 边界已冻结
> 适用范围:`parse-book` 作品面升格管线
> 执行状态:P0 历史 redo 安全门已完成并验证;一次性备份、恢复演练、reset 和 `1..116` 重建尚未执行,执行前仍需用户绑定确认
## 一、评审结论
当前 `--redo-window K` 只撤销并重写第 K 窗。在串行生长的升格管线中,后窗已经消费了旧的卡、别名、出场记录、水位和向量;只重写历史单窗会制造前后代际不一致。因此,本次修复分成两个互不阻塞的阶段:
| 阶段 | 目标 | 本阶段必须交付 | 明确不做 |
|---|---|---|---|
| P0 | 立即止损并恢复 work 8 | 封死历史 redo;停止 work 8 评测读取;建立一次性可校验备份;经用户确认后执行 `reset_upgrade_work`;全书 `1..116` 正序重建;完成验收 | 不建设通用持久任务、恢复点、围栏接管和真正级联 |
| P1 | 建立可长期使用的历史重跑能力 | 持久任务、恢复点、写入围栏、强杀恢复、真正的 `K..F` 级联重跑 | 不作为 P0 的前置条件,不借 P0 成功宣称 P1 完成 |
**P0 是本次 work 8 恢复的唯一方案。** 不再根据审计是否充分选择 `84..F` 撤销,也不在本次恢复中实现或调用真正级联。
## 二、事实与边界
### 2.1 已确认事实
- work 8 当前设计基线末窗为 `F=116`。
- 已知失活触发点是 **window 101**;101 不是末窗,也不是 P0 的重建终点。
- 升格按窗口串行生长,后窗依赖前窗形成的卡、别名、出场记录、水位、审计和向量。
- 当前 `--redo-window` 只处理指定单窗,不能重建该窗之后已经形成的派生判断。
- 现有 `reset_upgrade_work.py` 会软删本书活跃升格卡,将全部窗口重置为 `pending`,并清理本书别名、出场留档、卡水位和升格审计;旧向量行保留,但随所属旧卡软删而退出召回。
- 正常 `parse_upgrade.py run` 会按章节顺序处理未完成窗口,可用于全书正序前滚重建。
### 2.2 执行前必须重新核验
`F=116` 是本设计的确认基线,不是允许执行时静默变化的动态参数。任何破坏性动作前必须只读核验:
1. work 8 恰有 116 个有效窗口,窗号和章节范围连续,正文可读。
2. 当前没有 work 8 升格写入进程,也没有评测任务正在读取 work 8。
3. 正文版本、窗口边界、字段合同和必要模型配置已生成不可变摘要。
4. `reset_upgrade_work --work-id 8` 的预览影响范围与备份清单一致。
任一项不满足,立即停止。特别是实际末窗不等于 116 时,不得自动扩大或缩小范围,必须重新生成设计基线并取得新的用户确认。
### 2.3 本文不授予执行权限
全书 reset 会软删和硬删现有派生数据,是破坏性动作。**本文评审通过不等于授权执行。** 只有一次性备份完成并校验通过后,向用户展示本次执行摘要并取得针对该摘要的明确确认,才能执行数据库写入或模型调用。
## 三、P0:立即止损与 work 8 全书重建
### 3.1 P0 总流程
```mermaid
flowchart TD
A[封死历史 redo] --> B[停止 work 8 升格写入与全部评测读取]
B --> C[只读预检并冻结 F=116 与输入摘要]
C --> D[生成一次性备份]
D --> E{读回与恢复校验通过?}
E -- 否 --> X[失败关闭,不改业务数据]
E -- 是 --> F[展示 work 8 / 1..116 / 备份 ID / 破坏影响]
F --> G{用户明确确认本次 reset?}
G -- 否 --> X
G -- 是 --> H[执行 reset_upgrade_work]
H --> I{reset 后空态验收通过?}
I -- 否 --> Y[停止并保持评测关闭,人工决定是否恢复备份]
I -- 是 --> J[按 1 到 116 严格正序前滚重建]
J --> K{P0 全链验收通过?}
K -- 否 --> Y
K -- 是 --> L[用户确认解除 work 8 评测读取禁令]
```
### 3.2 封死历史 redo
P0 第一项是让历史单窗重跑失败关闭,避免 P1 完成前再次制造代际错位:
- `--redo-window K` 指向已完成历史窗时,必须在任何业务写入前拒绝。
- 不提供 `--force`、配置开关或“已知风险继续”等绕过路径。
- 正常运行中对**当前失败窗**的撤销后同窗重试不属于历史 redo,可以保留;它不能越过失败窗,也不能回写更早的已完成窗。
- 只有 P1 真正级联通过全部验收后,历史 redo 才能以新的 `K..F` 语义重新开放。
### 3.3 停止 work 8 评测读取
冻结区间从生成备份前开始,到 `1..116` 重建和 P0 验收全部通过、用户明确同意开放为止。在此期间:
- 停止 work 8 的在线评测、离线回放、批处理评测、评测快照装配、评测导出和缓存预热。
- 停止普通升格写入、历史 redo 和其他会修改 work 8 升格派生状态的任务。
- 只允许本次预检、备份、重建和验收使用受控入口;验收查询不产生评测结果。
- 任一失败或中断都保持关闭,不能因为进程退出、窗口暂停或备份恢复成功而自动开放。
P0 必须留下冻结开始、命中拒绝、验收结束和解除冻结的记录。最终验收要求冻结区间内 work 8 的成功评测读取数为 0。
### 3.4 一次性可校验备份
P0 备份是本次 work 8 reset 前的唯一恢复基线,不是 P1 的通用恢复点系统。备份必须在同一个一致性读视图中覆盖:
| 状态域 | 备份范围 |
|---|---|
| drafts | work 8 全部 `upgrade_book` 卡,包含活跃、软删、版本和归属字段 |
| windows | work 8 全部 116 个窗口及状态、错误、边界和软删字段 |
| aliases | work 8 全部别名行 |
| presence | work 8 全部出场留档 |
| card_state | work 8 全部卡水位 |
| audits | 所有指向 work 8 升格卡的审计行 |
| embeddings | 所有指向 work 8 升格卡的向量行、内容哈希、模型和软删状态 |
备份工件必须满足:
1. 生成唯一 `backup_id`,记录数据库标识、生成时间、代码版本、`F=116` 和输入摘要。
2. 每个状态域记录行数、主键集合摘要和按稳定顺序计算的内容校验值;备份文件另记 SHA-256。
3. 备份存放在不会被 `reset_upgrade_work` 影响的位置,只允许授权操作者读取或恢复,不得被后续尝试覆盖。
4. 生成后重新读回,核对文件校验值、七域行数和内容摘要。
5. 在隔离库或隔离 schema 做一次恢复演练;恢复后的七域摘要必须与源数据一致。
任一校验或恢复演练失败,P0 停止,不得执行 reset。
### 3.5 用户确认硬门
备份校验通过后必须停止,并向用户展示以下固定摘要:
- 作品:`work_id=8`,范围:`1..116`。
- `backup_id`、备份 SHA-256、七域行数与校验结果。
- 将执行的破坏性入口:`reset_upgrade_work --work-id 8 --execute`。
- reset 的影响:软删活跃升格卡、全部窗口置为 `pending`、硬删别名/出场留档/水位/审计;随后会调用模型从窗 1 重建到窗 116。
- 失败边界:P0 没有 P1 的强杀自动恢复;失败后 work 8 保持关闭,由用户决定续跑还是恢复唯一备份。
确认必须绑定本次 `work_id`、范围、输入摘要和 `backup_id`。旧确认、默认值、通用 `--yes` 或范围变化后的确认均无效。**没有本次明确确认,流程只能停在这里。**
### 3.6 reset 与正序前滚
获得确认后,P0 只允许以下路径:
1. 执行现有 `reset_upgrade_work.py --work-id 8 --execute`,不得另写临时 SQL 替代,不得改走 `--redo-window`。
2. reset 后立即验证:活跃升格卡为 0;116 个有效窗口全部为 `pending`;别名、出场留档、卡水位和指向旧升格卡的审计均为空;旧向量只挂在软删旧卡上,不参与召回。
3. 使用正常升格入口从窗 1 开始,严格按 `1,2,...,116` 正序前滚;不得跳窗、并行同书窗口或从 84/101 起跑。
4. 每个窗口只有在完整提交并通过窗后不变量后才能进入下一窗。全局 `$24/6000` 治理或局部上限可以让运行在窗边界暂停,但 work 8 仍保持关闭。
5. 普通失败按现有“当前失败窗撤销后重试/续跑”处理。发生强杀、状态不可解释或输入摘要漂移时立即停止,保持评测关闭;P0 不声称能自动接管恢复。
### 3.7 window 101 回归基线
window 101 是已知失活触发点,必须作为状态转移回归检查,不得只看窗 116 的最终活跃卡数:
- reset 前记录目标逻辑实体在 window 100 结束、window 101 处理后以及 102..116 的卡身份、活跃状态、归并关系、水位和审计来源。
- 重建时记录同一逻辑实体在 window 100/101/116 的对应状态。
- 验收必须证明 window 101 按当前业务证据产生了正确状态转移,且 102..116 消费的是重建后的新前态。
- 不能用“最终存在一张同名活跃卡”代替验证;新建重复卡、错误归并后复活或旧新代际叠加均不通过。
## 四、P0 验收与完成口径
P0 只有同时通过以下门槛才算完成:
| 验收门 | 必须证据 |
|---|---|
| 历史 redo 已封死 | 历史 `--redo-window` 在写入前被机械拒绝;当前失败窗重试仍正常 |
| 冻结有效 | 从备份前到开放前,work 8 成功评测读取数为 0,且无第二升格写入者 |
| 备份可恢复 | 唯一 `backup_id`、SHA-256、七域摘要和隔离恢复演练全部通过 |
| 确认有效 | 确认记录绑定 work 8、`1..116`、输入摘要和 `backup_id`,且发生在 reset 前 |
| reset 正确 | 执行入口和影响行数可追踪;reset 后空态不变量全部通过 |
| 顺序完整 | 窗口完成事件严格为 `1..116`,无跳窗、倒序、并行或旧状态复用 |
| window 101 回归 | 100→101 的状态转移符合业务证据;102..116 持续消费新前态;无重复逻辑实体或代际混合 |
| 七域一致 | 卡、窗口、别名、presence、水位、审计和向量互相一致;活跃向量哈希匹配活跃卡正文 |
| 开放受控 | 116 窗全部完成且全链验收通过后,仍需用户明确同意才解除评测读取禁令 |
以下情况只能报告局部进度,不能说 P0 完成:备份只生成未恢复演练;reset 成功但未跑满 116;window 101 最终态看似正常但过程未核验;任务暂停或回滚;只验证一张卡;评测读取未证明全程为 0。
## 五、P1:持久任务与真正级联
P1 在 P0 独立完成后实施。它把历史重跑从一次性运维动作升级为可恢复、可并发约束、可审计的产品能力。
### 5.1 持久任务与恢复点
每次历史重跑创建持久任务,至少记录:`task_id`、`work_id`、起点 `K`、冻结末窗 `F`、输入版本、当前阶段、逆序/正序游标、最近完成窗口、恢复点、围栏令牌、暂停原因、错误和终态。
- 任务创建时冻结 `K..F` 和输入版本;范围或输入漂移时失败关闭。
- 任何撤销前创建可恢复到任务开始前的全书恢复点,并机械验证完整性。
- 每个窗口边界形成持久检查点。模型计算期间不持有数据库长事务。
- 暂停必须续接同一任务;不得另起普通升格任务越过未完成级联。
### 5.2 写入围栏与读取关闭
同一本书同一时刻只允许一个升格写入任务。数据库可信边界为每次任务发放递增围栏令牌;所有升格写事务都必须验证令牌。
- 租约或心跳过期只表示任务可被接管,不表示普通写入者可以进入。
- 接管者取得新令牌后,旧进程即使复活也无法提交。
- 任务从撤销开始到验收发布前,业务消费和评测读取只能看到“重跑中/待恢复”,不能读取半成品。
### 5.3 真正级联语义
P1 重新开放后的 `--redo-window K` 固定表示:冻结当前末窗 `F`,先按 `F..K` 逆序撤销,再按 `K..F` 正序重放。撤销和重放都不得跳窗;后窗只能读取前窗已经重放完成的新状态。
```mermaid
flowchart LR
A[创建持久级联任务 K..F] --> B[取得新围栏令牌]
B --> C[冻结输入并校验全书恢复点]
C --> D[关闭本书消费与评测读取]
D --> E[逆序撤销 F..K]
E --> F[校验已回到 K-1]
F --> G[正序重放 K..F]
G --> H{全链验收通过?}
H -- 是 --> I[发布新代际并开放读取]
H -- 否 --> J[恢复任务前恢复点并保持关闭]
```
### 5.4 强杀与恢复
强杀后不得猜测进度。下一执行者读取持久任务、恢复点、最近完成检查点和当前围栏令牌:
- 未开始当前窗口写入时,从该窗口继续。
- 当前窗口事务未提交时,从该窗口重新计算。
- 阶段或数据无法由检查点证明时,恢复任务前恢复点并将任务置为失败关闭。
- 恢复、接管或回滚完成前,旧任务令牌、普通写入和所有消费读取均不得重新开放。
```mermaid
stateDiagram-v2
[*] --> preflight
preflight --> snapshotting: 预检通过
snapshotting --> undoing: 恢复点校验通过
undoing --> replaying: 已回到 K-1
replaying --> paused: 达到治理或局部上限
paused --> replaying: 同一任务续接
undoing --> recovery_required: 强杀或状态不明
replaying --> recovery_required: 强杀或状态不明
recovery_required --> restoring: 新围栏持有者接管
restoring --> failed_closed: 已恢复任务前状态
replaying --> validating: 已重放到 F
validating --> restoring: 验收失败
validating --> succeeded: 验收通过并发布
```
### 5.5 P1 验收
P1 完成至少要求:
1. 给定任意 `K..F`,事件顺序严格为撤销 `F..K`、重放 `K..F`。
2. 在恢复点生成、逆序撤销、正序重放、暂停等待和最终验收阶段分别强杀,重启后都能由持久证据决定续接或恢复。
3. 两个同书进程并发时只有新围栏持有者能写;旧进程复活提交被拒绝;不同作品可并行。
4. 任一中间态的业务消费和评测读取均被拒绝,只有完整新代际发布后开放。
5. 恢复点往返后七域逐行一致;窗口检查点不会留下半窗卡、孤儿别名、超前水位或旧向量。
6. P0 的历史 redo 禁令只有在上述自动化、数据库集成和强杀演练全部通过后才能解除。
## 六、实施顺序
1. **P0 代码止损(已完成)**:历史 redo 安全门仅允许末窗重跑;历史窗、不存在窗、不连续窗口、正文为空和负数参数均在业务写入前拒绝。验证证据:parse upgrade 128 项、parse LLM 9 项、outline 11 项全部通过,独立复审 PASS,真实 work 8 的 redo 101 已在写入前拒绝。
2. **P0 执行准备(未执行)**:只读预检、一次性备份与隔离恢复演练尚未执行。
3. **人工确认门(待执行)**:备份与恢复演练通过后,仍须提交绑定 `work_id=8`、`1..116`、输入摘要和 `backup_id` 的破坏性执行摘要,取得用户明确确认。
4. **P0 数据恢复(未执行)**:尚未执行 reset,也未开始 `1..116` 正序重建和验收;只有取得第 3 步绑定确认后才能执行。
5. **P1 长期能力(未执行)**:持久任务、恢复点、围栏、强杀恢复和真正级联仍属后续范围。
当前已完成第 1 步。第 2 至第 5 步均未执行;全书破坏性 reset 仍无执行授权。