Record how later materials can affect earlier stages without hard gates, and split the implementation into planned slices. Co-authored-by: Cursor <cursoragent@cursor.com>
16 KiB
阶段回溯与影响闭环设计
状态: accepted(设计;未改业务代码)
日期: 2026-09-20
分支: feature/stage-backtrack-design
基线: 功能启用与文档影响整改方案 Track T(五表 project_trace_*)
决策方式: 2026-09-20 grill 三轮;本文只记录已定决策与由其直接推出的实现约束
1. 要解决的三件事
- 阶段推进已存在,不重做。 创建者把当前阶段推进到下一阶段。门禁、三权审批、创建者独占推进保持现状。
- 修改之后要能很快看出波及面。 小队编制完成或成员改了材料、需求、对话结论之后,助手给出:可能影响哪些文档与条目、风险是什么、建议下一步做什么。
- 后面阶段的新材料可能反过来碰前面已完成阶段。 系统要标出来,并提供两条处置路径,但不把人锁死在流程里。
松散约束的总原则:信号自动产生,状态改变必须有人拍板。 未处理的影响不阻止推进。
2. 已定决策
| 议题 | 决定 |
|---|---|
| 方案基线 | 沿用五表追踪索引,回溯层共用同一影响内核 |
| 回溯形态 | 双层:L1 文档修订(默认)+ L2 再打开阶段(重路径) |
| 再打开时的文档 | 选择性解锁:勾选的文档退回 drafting,其余保持 approved |
| 下游已完成阶段 | 保持 completed,加「待复核」软标记;再次推进时二次确认 |
| 待复核闭环 | 仅人工:明确「无需变更」,或已经发起修订。禁止自动清除 |
| 分析触发 | 混合:文档修订/批准、阶段推进、对话结论沉淀自动跑;参谋与 API 可随时手动跑 |
| 门禁 | 全域统一松散,含医疗域 rdev。未复核不阻塞推进 |
| L2 发起权 | 受影响文档的编制/审核/批准人可申请;创建者批准后执行。创建者也可直接发起 |
| 候选边裁决 | 不新增 trace_reviewer。由下游文档编制人确认或否决;无编制人时由创建者裁决 |
| 留存 | project_trace_* 与本设计新增的申请/标记表定为合规业务记录(record)。随项目保留,禁止当可丢缓存随意删后重建 |
| 对话 | 纳入桥接:参谋可一键生成受控文档修订提案,人拍板后才改正文 |
| 试点 | main + software_delivery 先行,rdev 后跟 |
| 本轮范围 | 设计与任务拆分。不写业务代码,不改 production_ready |
本决定不签 D7 对话留存 ADR。该 ADR 仍是 pending-compliance-signoff。这里只给追踪索引和回溯记录分类,不授权任何物理删除。
3. 现状里可复用的部分
| 能力 | 位置 | 本设计怎么用 |
|---|---|---|
创建者推进 + 文档须 approved |
internal/projectinstance/stage_service.go AdvanceStage;gate.go evaluateStageReadiness |
保持。软标记只增加二次确认,不改变 Ready 的文档条件 |
| 阶段只前进 | internal/projectinstance/stage_store.go AdvanceStage;状态仅 pending/active/completed/skipped |
L2 增加一条受控回退,不开放任意回跳 |
文档驳回回 drafting |
internal/docgov/state_machine.go |
L1 继续走提案/驳回。L2 解锁用新的审计动作,不伪装成驳回 |
| 变更提案 | internal/docgov ChangeProposal;ImpactAnalysis 现为自由文本 |
提案仍由编制人拍板。影响正文改由报告里的确定性事实供给,提案文本只作说明 |
| 文档级血缘 | document_lineage;CheckAndPropagateStale;目前实质只有 test_manager 一条边 |
保留「消费了哪一版文档」。条目边解释「为什么影响到这一处」 |
| 版本账本 | document_snapshots / document_change_events |
影响分析读已提交 diff,不重扫工作区全文 |
预留实例状态 awaiting_stage_confirmation |
project_workflow_instances.status;代码从未写入 |
本设计不占用。 它会把整条实例停住,和松散约束相反 |
| 阶段就绪待办 | stage_advance_requests |
L2 申请单独成表,形态对齐,不复用推进待办 |
4. 影响分析闭环
4.1 触发
| 来源 | 方式 | 写什么 |
|---|---|---|
| 人工修订提交、Agent 正式提交、导入提交 | 自动,挂现有 change event / outbox | 一份影响报告 |
文档变为 approved |
自动 | 上游批准可能让下游条目变脏;出报告,不自动改下游正文 |
阶段 advance 成功 |
自动 | 对照本阶段新批准内容,标出已完成上游/下游里被碰到的条目 |
| 对话结论被标为可沉淀(见 §7) | 自动 | 只出报告与修订提案草案,不改受控正文 |
| 参谋「分析这次修改的影响」或 API | 手动 | 同一份报告结构 |
失败、索引未完成、调用者看不见目标,三种情况必须分开。禁止把 index_pending / partial / failed 显示成「无影响」。语义沿用整改方案里的七种影响状态:confirmed_impact、possible_impact、dependency_unchanged、no_known_dependencies、index_pending、partial、failed。
4.2 内核
- 事实来自五表:条目身份、不可变修订、关系边、人工决议、索引覆盖状态。模型见整改方案 §4,本文不另起一套表。
- 从变更所触及的条目做有界反向遍历,默认深度 3,必须检测环。
- 服务端计算受影响集合、影响状态和风险下限。模型只能写人能读的解释,不能把
possible_impact改成confirmed_impact,也不能压低风险。 - 向量相似只生产
model_candidate边。未确认前只贡献possible_impact。
4.3 报告给人看的内容
- 这次改了什么(旧值 → 新值,或「无结构化 diff」)。
- 受影响的文档、条目、所在阶段,以及影响状态。
- 风险下限和依据(例如已批准下游仍引用旧修订)。
- 不确定的部分(候选边、索引未覆盖)。
- 建议的下一步,每条都是可点的动作,而不是一段无法执行的文字。
建议动作只有三类:无需变更(清除对应软标记)、发起 L1 修订提案、申请 L2 再打开。点下去只创建草案或申请,随后重新检查权限、职责、租约和版本。
5. 双层回溯
flowchart TD
changeEvent["修改事件"] --> analysis["影响分析"]
analysis --> report["影响报告"]
report --> l1["L1 修订提案"]
report --> mark["下游待复核标记"]
l1 --> mark
report -->|"需要重编或新文档"| request["L2 申请"]
request --> creatorOk["创建者批准"]
creatorOk --> reopen["上游阶段再打开"]
reopen --> unlock["只解锁勾选文档"]
unlock --> squad["可重跑小队"]
squad --> reapprove["重新审批后阶段再次完成"]
mark --> clear["人工闭环"]
5.1 L1:文档修订(默认)
阶段状态不动,completed 保持 completed。
- 受影响且已批准的文档:走现有变更提案或新版本修订,编制人决定是否改。
- 未批准、仍在
drafting的文档:不额外解锁,编制人按原职责改。 - 适用:措辞、单条需求、验收标准、引用关系的局部更新。
- 对接:
internal/docgov提案与internal/docrevision人工修订。不新增阶段状态。
5.2 L2:再打开阶段(重路径)
只在需要重跑小队、补一篇文档,或修订范围大到不适合在锁定正文上打补丁时使用。
确认态不放在实例状态上。 新增 stage_reopen_requests(对齐 stage_advance_requests):
| 字段要点 | 说明 |
|---|---|
| 阶段实例 | 必须当前是 completed |
| 申请人 | 该阶段某份受影响文档的 author / reviewer / approver,或创建者 |
| 勾选文档 | 准备退回 drafting 的 doc task;空列表拒绝 |
| 理由 | 必填,写入审计 |
| 状态 | pending → approved / rejected / cancelled |
创建者直接发起时,同一事务内写入并批准,不给自己再排一个待办。其他人发起时保持 pending,直到创建者批准。
批准后,同一事务:
- 该阶段
completed → active。 - 勾选文档执行新动作
reopen_for_revision:approved → drafting,解锁正文,记原因、操作者和前一个 snapshot。未勾选的文档保持approved且继续锁定。 - 不把阶段内其他文档打回,不把下游阶段改成
pending,不把实例改成awaiting_stage_confirmation。 config_revision递增,沿用现有乐观锁。
允许两个「活」阶段并存。 推进游标仍是 position 最大、状态不是 completed/skipped 的阶段。被再打开的上游是附加可编辑阶段,界面标成「再打开」,不抢下游正在进行的编制。这样后面的阶段不用停下来等前面重走一遍。
再打开阶段再次推进时:
- 门禁只要求本次被解锁的文档重新
approved。本来就保持approved的文档不再卡一次。 - 创建者确认后,该阶段回到
completed。 - 这次推进不自动清除下游「待复核」标记。
被解锁文档可以按原 executor 重跑小队;未解锁任务不得被这次重跑覆盖。
5.3 为什么不用驳回代替 L2 解锁
驳回表示审核/批准没通过。再打开是「前面已经生效,现在因新材料要改一版」。审计、通知和责任人都不该写成驳回。reopen_for_revision 单独留痕,目标状态同样是 drafting。
6. 待复核软标记
新增 stage_recheck_marks,挂在阶段实例上:
- 产生:影响报告判定某已完成阶段的文档或条目受到
confirmed_impact或未决possible_impact。同一阶段、同一来源报告只保留一条未关闭标记。 - 含义:请人看一眼。阶段仍是
completed,文档仍是原状态。 - 推进:
GetStageReadiness仍可Ready=true。若目标阶段或它依赖的已完成上游带未关闭标记,响应增加requires_recheck_ack=true。AdvanceStage在带上acknowledge_recheck=true后才执行,并记下「创建者已知悉」。知悉不等于闭环。 - 清除:只有人写下
no_change决议,或针对该标记发起了 L1 提案 / L2 申请。发起即清除「还没人理」的状态;后续修订失败不会把标记偷偷清掉——提案被拒或 L2 被拒时标记重新打开。 - 禁止:上游出现新 snapshot、文档被批准、索引重建完成,都不得自动清除。
医疗域同样不把未关闭标记变成硬门禁。已知取舍见 §10。
7. 对话桥接
今天对话只能把消息收成自由文档,不能直接改受控文档。本设计补一条提案桥,不补一条直写桥。
| 意图 | 行为 |
|---|---|
project.impact.analyze |
只读。对当前对话里点名的修改、文档或条目出影响报告 |
project.doc.revise_propose |
创建修订提案或 agent candidate。编制人在文档侧确认后才进入正文 |
约束:
- 参谋只能看见调用者有权看的文档和条目。
- 意图注册方式对齐
internal/intent/registry.go里的project.stage.advance:写意图RequiresConfirm=true。 - 执行入口对齐
internal/chat/action_executor.go:确认前零业务写。 - 自由文档沉淀保留,作为不进入受控台账的旁路。
8. 候选边由谁裁决
整改方案 §7 推荐的项目级 trace_reviewer 不实施。
explicit_reference、workflow_consumption、rule边由抽取器直接成为已确认边。model_candidate由下游文档当前编制人确认或否决,写入project_trace_edge_decisions。- 该文档
DutyMode=none或没有编制人时,由项目创建者裁决。 - 未决候选只产生
possible_impact。 - 全局管理员不因管理员身份获得项目内文档或候选的确认权。
- 确认事务仍须校验项目、条目、两端文档属于同一项目,且操作者看得到下游文档。源端文档不可见时,不展示其标题和正文,只保留「存在一条不可见上游」的计数,避免侧信道。
9. 留存
| 数据 | 类别 | 规则 |
|---|---|---|
project_trace_items / item_revisions / edges |
record |
随项目保留。可做审计过的修复重放(从源 snapshot + 决议账本重建派生列),不可先删光再当缓存重建 |
project_trace_edge_decisions |
record,且是决议权威 |
修复重放必须原样带回确认、否决、撤销 |
project_trace_index_state |
record |
覆盖水位属于合规轨迹的一部分,不单独丢弃 |
stage_reopen_requests、stage_recheck_marks |
record |
与阶段实例同寿命 |
| 影响报告 | 沿用现有 change report 留存 | 不因索引重建覆盖历史时间线 |
源文档因依法清除而删除时:条目改为 tombstone,边和决议保留,影响报告保留 digest,不级联物理删除。具体清除仍等 D7 合规签字,本决定不打开删除开关。
这覆盖整改方案审阅的第二项遗留,并取代该方案 §10「条目、修订、关系和索引状态是可重建投影、可丢弃」的表述。人工决议单独权威这一点不变。
10. 已知取舍
全域松散是刻意的。医疗设计控制(rdev)里,上游需求变了而下游验证方案仍显示已完成,合规上应有人复核。本系统选择:
- 不替合规官拦截推进;
- 把待复核做成推进前必须看见并确认的提示,而不是可折叠的小字;
- 保留申请、解锁、知悉、闭环的全部审计,供事后检查。
若以后要为某个工作流打开硬门禁,应做成该工作流 manifest 上的可选开关,默认关闭。首期不做这个开关,避免现在就分叉成两套门禁。
11. 模块对接
| 模块 | 改动面 | 切片 |
|---|---|---|
internal/docrevision |
提交后出报告;风险与影响状态已有雏形(impact.go、risk.go) |
T1 |
internal/projectworkflow |
lineage 继续记文档版本消费;stale 不再是唯一影响类型 | T1 |
| 新追踪存储(整改方案五表) | 迁移、抽取 UR/RS/REQ/TC、项目页 | T2 |
internal/projectinstance |
stage_reopen_requests、阶段 completed→active、选择性解锁、推进知悉 |
B1 |
internal/docgov |
reopen_for_revision;提案桥接 |
B1 / B3 |
web 工作流条与文档栏 |
再打开申请、勾选解锁、待复核二次确认 | B1 / B2 |
internal/intent + internal/coordinator + internal/chat |
两个新意图 | B3 |
| 追踪查询 | 深度 3 反向遍历、候选治理 UI | T3 |
试点验收只覆盖 main 与 software_delivery。抽取器可以识别已出现的 UR/RS/REQ/TC,但 rdev 的阶段再打开不在首期验收里。software_delivery 的 D0 变更说明继续作为人写的变更叙述,不替代影响报告。
12. 实施顺序
- T1 把还没接上的提交来源(人工修订、导入、聊天侧正式提交)收进同一份影响报告。已完成的 lineage 鉴权、proposed/authoritative 报告、PI 叙事裁剪不重做。
- T2 五表、键投影、项目「需求与影响」页。D7 追踪分类已在本文决定,不再等对话 ADR。
- B1 L2 再打开。可与 T2 并行,但报告里的「申请 L2」按钮等 T1 报告存在后再接。
- B2 软标记与二次确认。依赖 B1 的阶段读写,不依赖多跳。
- B3 对话桥。依赖 T2 的条目身份才能「触及条目」;在此之前只允许对文档出提案。
- T3 多跳与候选边。依赖 T2。
切片任务见 .doc/ops/short-term/2026-09-20-stage-backtrack-*.md 与 2026-09-20-impact-loop-t1.md、2026-09-20-project-trace-index-t2.md、2026-09-20-trace-multihop-t3.md。
13. 非目标
- 不改创建者推进权和文档三权状态机的既有转移。
- 不把已完成阶段自动打回,不级联冻结下游。
- 不新增
trace_reviewer角色。 - 不在本轮启用硬门禁,不为
rdev单独做拦截。 - 不把对话直接写入受控正文。
- 不签整份 D7,不执行物理删除,不改
production_ready。 - 不在本设计任务里改
internal/或web/。