plan-governance-cli 1.0.3 → 1.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -6
- package/package.json +1 -1
- package/resources/skill/SKILL.md +12 -29
- package/resources/skill/agents/openai.yaml +1 -1
- package/resources/skill/assets/plan.template.md +6 -16
- package/resources/skill/references/cli.md +2 -2
- package/resources/skill/references/planning.md +32 -105
- package/resources/skill/references/verification.md +39 -59
- package/scripts/check_plan_governance.py +262 -51
- package/scripts/init_plan_governance.py +3 -3
package/README.md
CHANGED
|
@@ -23,9 +23,9 @@
|
|
|
23
23
|
|
|
24
24
|
## 需求探索与 grilling
|
|
25
25
|
|
|
26
|
-
|
|
26
|
+
先利用上下文和工具查证需求,在已有授权内推进。只有必须由用户决定、且会实质影响结果或授权边界的问题才询问,已有决定不重复确认。
|
|
27
27
|
|
|
28
|
-
|
|
28
|
+
`grill-me` 是深入探索时的可选方法,缺失不阻塞任务。需要治理时把重要决定和未决问题记录到当前计划;普通可逆细节自主处理,等待必要答案时继续无依赖的授权工作。
|
|
29
29
|
|
|
30
30
|
## 文档结构
|
|
31
31
|
|
|
@@ -205,9 +205,9 @@ plan-governance-cli graph code impact --repo modelpad --file Sources/ModelPadCor
|
|
|
205
205
|
|
|
206
206
|
## 持续推进
|
|
207
207
|
|
|
208
|
-
在已授权的跨轮任务中可使用 `goal`;skill
|
|
208
|
+
在已授权的跨轮任务中可使用 `goal`;skill 本身不定义整计划自主执行运行时。普通改动自验,高风险或高影响同范围只独立复核一次,修复后自验;旧策略名和旧记录格式采用相同行为并保留历史,无需逐计划迁移。
|
|
209
209
|
|
|
210
|
-
[验证规范](resources/skill/references/verification.md)
|
|
210
|
+
[验证规范](resources/skill/references/verification.md)统一维护单次复核、任务复用、证据复用、修复自验与用户验收;其他会话从同一 skill 读取。需要独立复核且授权明确时自动安排只读 subagent,复用已有同范围任务/结果,等待异常先核准任务 ID 和状态,且复核者不得再委派复核;不为普通小改逐次派发。技术完成待用户验收的适用功能计划仍保持实施中。
|
|
211
211
|
|
|
212
212
|
1.0.1 起支持可选的最近记录窗口:
|
|
213
213
|
|
|
@@ -228,7 +228,7 @@ plan-governance-cli guide cli # CLI 操作
|
|
|
228
228
|
|
|
229
229
|
读取当前包内正文,与 cwd 无关,不需要 Python,不下载或修改项目。未知主题、多余参数、缺失或空资源非零退出。项目受管入口依赖已安装 skill 或具备上述能力的 CLI;两者均不可用时明确说明前提,不自动安装。
|
|
230
230
|
|
|
231
|
-
SKILL
|
|
231
|
+
SKILL 保留任务边界、自主执行原则和按需入口,三份 references 按需读取并随 manifest/setup/npm 分发。已有项目只刷新受管入口,不迁移旧计划、不改用户块外内容。选择同一版本的规范来源,安装副本与仓库同版本号也可能有不同内容。
|
|
232
232
|
|
|
233
233
|
也可以不全局安装,直接使用锁定版本:
|
|
234
234
|
|
|
@@ -375,7 +375,7 @@ python3 -m pytest
|
|
|
375
375
|
|
|
376
376
|
状态与当前阶段由 `docs/PLAN_MAP.md` 维护。每阶段必须具备自身 Step 0、验证/完成和失败边界、无有效阻塞及适用复核,不能凭上一阶段完成自动待实施。
|
|
377
377
|
|
|
378
|
-
|
|
378
|
+
新旧计划统一采用单次复核行为:普通改动自验,高风险或高影响独立检查一次后由实施者修复自验,原发现保留。旧 `风险分流` 标签和缺省六列格式只作为兼容记录形式,不恢复多次独立门禁;固定字段、历史保护和严格检查语义以[阶段门规范](resources/skill/references/verification.md#step-0-与阶段门)为准。
|
|
379
379
|
|
|
380
380
|
使用 `check --strict-readiness` 做准入/CI/发布机械检查;默认检查兼容告警,机械通过不代替真实风险判断、业务验收或用户接受。
|
|
381
381
|
|
package/package.json
CHANGED
package/resources/skill/SKILL.md
CHANGED
|
@@ -1,43 +1,26 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: plan-governance
|
|
3
|
-
description:
|
|
3
|
+
description: 维护已有项目计划,或为需要跨阶段、跨会话协调依赖与决策的工作建立轻量治理。用户要求 PLAN_MAP、专项计划或治理时使用;普通一次性修复、解释代码和零散待办不启用完整流程。
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Plan Governance
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
帮助用户完成需要持续协调的工作,维护足够的计划、决策与验证证据。已有相关计划时沿用;需要持久记录阶段和依赖时才新建。任务持续多轮或涉及 API,并不自动要求治理。
|
|
9
9
|
|
|
10
|
-
##
|
|
10
|
+
## 执行原则
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|---|---:|
|
|
14
|
-
| 跨阶段、里程碑或多轮会话 | 1 |
|
|
15
|
-
| 架构、公共 API、Schema、兼容性或迁移 | 1 |
|
|
16
|
-
| 依赖真实运行报告、样本或反馈 | 1 |
|
|
17
|
-
| 多计划依赖、替代、重叠或冲突 | 1 |
|
|
18
|
-
| 同一模块后续反复修改 | 1 |
|
|
12
|
+
根据当前请求、上下文和已有授权持续推进。用户指令优先于本 skill 的流程建议;保留用户明确的实现、外部操作和验收边界。能自行查证或作可逆决定的事项自主处理;只有缺失信息会实质改变结果或超出授权时才询问,并继续不依赖该答案的工作。
|
|
19
13
|
|
|
20
|
-
|
|
14
|
+
首次接手读取地图、当前相关计划及必要契约;恢复只核对当前阶段、实际差异和最近证据。只实施已授权且准入的范围;实质边界变化时调整受影响计划,日常进展随工作同步。事实源与格式见 planning。
|
|
15
|
+
|
|
16
|
+
按 verification 验证:普通改动自验,高风险或高影响的同一交付范围独立检查一次,修复由实施者自验;旧策略名和旧记录格式不改变此行为。适用检查通过后完成交付;未解决问题如实保留。交付说明完成结果、必要证据和剩余事项,明确约定的用户体验验收继续有效。
|
|
21
17
|
|
|
22
18
|
## 按需读取
|
|
23
19
|
|
|
24
|
-
|
|
|
20
|
+
| 当前动作 | 参考 |
|
|
25
21
|
|---|---|
|
|
26
|
-
|
|
|
27
|
-
|
|
|
28
|
-
|
|
|
29
|
-
|
|
30
|
-
参考相对本 skill 实际安装位置读取,不相对项目 cwd。仅安装 CLI 时,`plan-governance-cli guide` 读取本入口,`guide planning|verification|cli` 读取相应正文;缺失资源明确报错,不猜路径或自动安装。选择同一版本来源,不能混读不同副本;旧 CLI 不支持 guide 或风险策略时,先说明能力缺失。
|
|
31
|
-
|
|
32
|
-
## 执行底线
|
|
33
|
-
|
|
34
|
-
- 首次接手读取 PLAN_MAP、当前计划和确实相关的契约/ADR/migration;恢复先看当前工作集、当前阶段、最近证据和实际 diff,缺失或冲突再扩大读取,不每轮重读全部历史。
|
|
35
|
-
- 复用已确认需求和授权。未明确且满足触发条件的高影响取舍才做 grilling;可验证的事实自己查。必需回答不能以沉默推定,清楚的普通细节自主决定。
|
|
36
|
-
- 低风险且范围、实际后果、可逆性和验证充分的改动由当前 AI 自验,失败自主修复/复验;高影响、有效独立失败或实质疑点保留必要独立检查。风险规则及新/旧机器记录在 verification,不以行数或阶段事件一概判断。
|
|
37
|
-
- 只实施当前获准且有授权的阶段。每阶段有自身 Step 0、样本、验证/完成与失败边界、无有效阻塞和适用复核;旧计划未显式选择风险策略时保留独立门禁,新策略不批准自身变更。
|
|
38
|
-
- 独立复核失败、不可用、超时或证据冲突不自批或改分类放行。需要复核且授权清楚时自动安排独立只读 subagent;它不能代替新的外部授权。
|
|
39
|
-
- 验证真实可观察结果,按变更范围补验;只有当前树、依赖/配置、环境和验收契约均被可信证据覆盖时才复用全量结果。纯落档做适用文档检查,不无理由全跑。
|
|
40
|
-
- 适用功能计划技术完成待用户体验时保持实施中,明确待验收项;用户接受后关闭。原范围小问题自主闭环,沉默、提交和技术通过不代替用户接受;普通小改不增加逐次签收。
|
|
41
|
-
- 状态、阶段、关系、阻塞和证据入口以 PLAN_MAP 为准;专项计划写本次差异及证据,现行契约优先复用已有 Schema/OpenAPI,按需才建 spec。新事实不写回旧草案。
|
|
22
|
+
| 明确需求、维护计划、同步决策 | [planning](references/planning.md) |
|
|
23
|
+
| 阶段准入、风险判断、验证或完成 | [verification](references/verification.md) |
|
|
24
|
+
| 查询工作集、运行检查、初始化或分发 | [cli](references/cli.md) |
|
|
42
25
|
|
|
43
|
-
|
|
26
|
+
开发或审查本 skill 时使用仓库源及对应 CLI;其他项目使用实际发现的安装版,参考按所选 skill 目录解析。仅 CLI 可用时用 `guide` 和对应主题读取。来源一次选定后复用,只有内容变化或能力不符时重新核对;不要混读副本。缺少所需能力时说明具体缺口,继续不依赖它的工作,安装与升级按已有授权处理。
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
interface:
|
|
2
2
|
display_name: "Plan Governance"
|
|
3
3
|
short_description: "用计划、证据和检查管理分阶段工作"
|
|
4
|
-
default_prompt: "使用 $plan-governance 管理这个跨阶段变更,维护计划文档、Step 0
|
|
4
|
+
default_prompt: "使用 $plan-governance 管理这个跨阶段变更,维护计划文档、Step 0 证据和完成检查;按当前有效策略验证,在已有授权内完成交付,保留适用用户验收。"
|
|
@@ -12,26 +12,16 @@
|
|
|
12
12
|
|
|
13
13
|
- 待补充。
|
|
14
14
|
|
|
15
|
-
<!--
|
|
15
|
+
<!-- 仅在存在需要保留的需求取舍时填写,否则删除本节。 -->
|
|
16
16
|
## 需求探索
|
|
17
17
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
### 暂定假设与验证方式
|
|
21
|
-
|
|
22
|
-
### 范围与非目标
|
|
23
|
-
|
|
24
|
-
### 候选方案与取舍
|
|
25
|
-
|
|
26
|
-
### 未决问题
|
|
27
|
-
|
|
28
|
-
### 用户确认的探索结论
|
|
18
|
+
记录重要决定、假设及未决问题;已确认内容不重复签收。
|
|
29
19
|
|
|
30
20
|
## 不变量
|
|
31
21
|
|
|
32
22
|
- 当前阶段写细,后续阶段写粗。
|
|
33
23
|
- 完成时必须记录可验证证据。
|
|
34
|
-
- 按共享 skill 的 verification
|
|
24
|
+
- 按共享 skill 的 verification 规范判断风险:普通改动自验,高风险或高影响同范围独立复核一次,修复后自验;不按改动行数或阶段事件一概派发。
|
|
35
25
|
|
|
36
26
|
## 影响模块或文件
|
|
37
27
|
|
|
@@ -60,7 +50,7 @@
|
|
|
60
50
|
| 字段 | 内容 |
|
|
61
51
|
|---|---|
|
|
62
52
|
| 准入状态 | {{status}} |
|
|
63
|
-
| 复核策略 |
|
|
53
|
+
| 复核策略 | 单次独立复核 |
|
|
64
54
|
| Step 0 | 当前阶段 Step 0 证据的链接或锚点 |
|
|
65
55
|
| 样本矩阵 | 样本/fixture 或替代基线的链接或锚点 |
|
|
66
56
|
| 验证方式 | 可执行命令和输出位置的链接或锚点 |
|
|
@@ -93,7 +83,7 @@
|
|
|
93
83
|
|
|
94
84
|
### Attestation 说明
|
|
95
85
|
|
|
96
|
-
|
|
86
|
+
仅采用快照时填写;格式和检查方式见所选 skill 的 CLI 参考。
|
|
97
87
|
|
|
98
88
|
### 验证方式
|
|
99
89
|
|
|
@@ -109,7 +99,7 @@
|
|
|
109
99
|
|
|
110
100
|
### 测试覆盖率
|
|
111
101
|
|
|
112
|
-
|
|
102
|
+
按项目契约和实际改动提供覆盖率或适用验证证据;纯文档可注明“代码覆盖率不适用”,不为填表运行无关测试。
|
|
113
103
|
|
|
114
104
|
### 完成条件
|
|
115
105
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
只读规则入口:`plan-governance-cli guide [overview|planning|verification|cli]`。缺省 overview;`guide --help` 列用法。四主题从同一包根读取本入口及三参考,任意 cwd 可用且无需 Python。正文中的相对文档链接在 CLI 环境通过对应 guide 主题读取,不相对当前项目查找。
|
|
4
4
|
|
|
5
|
-
成功退出 0;未知主题、多余参数、缺失/不可读/空资源退出 1 并报错。不下载、安装、修改项目或回退别的版本。skill
|
|
5
|
+
成功退出 0;未知主题、多余参数、缺失/不可读/空资源退出 1 并报错。不下载、安装、修改项目或回退别的版本。skill/CLI 来源按入口一次选定:开发本 skill 用仓库源与对应 CLI,其他项目用安装资源。出现内容变化或能力缺口时再核对,版本号相同不证明资源一致。
|
|
6
6
|
|
|
7
7
|
已安装 skill 不等于 CLI 存在;既无 skill 也无 CLI 时说明治理前提缺失,仅阻塞依赖治理规则的工作,不自动安装。CLI 不支持新策略或 guide 时明确能力缺失,按授权更新。仅需阅读规则不调用 init/setup。
|
|
8
8
|
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
|
|
21
21
|
SKILL、三个 references、agents 元数据与 assets 由同一 npm 包的 manifest 分发。`plan-governance-cli setup --target codex|claude|all --dry-run` 查看同步差异;实际覆盖需既有用户授权并按 setup 冲突提示处理,不自动清理用户目录残留。项目升级不迁移旧计划,受管块外内容必须保持。
|
|
22
22
|
|
|
23
|
-
|
|
23
|
+
仅更新计划格式不等于更新项目入口。已有项目在任务交接、没有进行中的实施或复核时更新受管入口;升级不批量回写历史,旧策略名和六列格式按 verification 解释为统一单次行为。安装副本与仓库开发规则有差异时说明本次选用来源,不能因版本号相同就混读正文。
|
|
24
24
|
|
|
25
25
|
## 初始化、模板和检查脚本
|
|
26
26
|
|
|
@@ -1,134 +1,61 @@
|
|
|
1
1
|
# 计划与契约
|
|
2
2
|
|
|
3
|
-
从 [skill 入口](../SKILL.md)
|
|
3
|
+
从 [skill 入口](../SKILL.md) 判断是否需要治理。本页维护计划与决策,验证和阶段完成见 [verification](verification.md);CLI 入口为 `guide planning`。
|
|
4
4
|
|
|
5
5
|
## 需求探索与 grilling
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
先利用上下文和可查证事实补齐需求。普通可逆细节自主决定;只有必须由用户决定、且会实质改变结果或授权边界的缺口才提问。问题聚焦当前取舍,不因某个字段未填完整就重做访谈或重复确认已有决定。
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
2. 下列不确定性信号中至少两项同时存在:目标/验收不清、范围/非目标不清、存在多个有实质取舍的方案、关键事实尚待查证、后续返工成本显著。
|
|
11
|
-
|
|
12
|
-
目标、影响范围和验收方式均明确的小范围任务,以及当前阶段已通过准入且已确认假设没有变化的工作,跳过自动 grilling;用户仍可显式调用它。已确认的范围、验收与授权直接复用,不为相同结论重新 grilling。仅影响可选细节的问题可在合理等待后声明假设继续;必需答案和授权不可由沉默推定。
|
|
13
|
-
|
|
14
|
-
grilling 每次只问一个问题。可从仓库、环境或工具确认的事实必须先查证;产品取舍、外部承诺和不可逆选择由用户确认。结束前必须汇总并得到用户确认:目标、范围和非目标、已确认事实、暂定假设及验证方式、候选方案与取舍、未决问题及阻塞级别,以及验收口径或下一步动作。
|
|
15
|
-
|
|
16
|
-
用户确认后,只有在任务需要治理时,才把结构化结论写入专项计划的 `需求探索` 章节;不保存完整访谈逐字稿,不自动创建 ADR。探索结论不替代当前阶段的 Step 0、样本矩阵、验证方式、失败/回滚边界或适用阶段准入复核。`grill-me` 不可用时,说明依赖不可用并保持在澄清/设计边界,不得伪造已完成 grilling 或静默实施。
|
|
17
|
-
|
|
18
|
-
收敛时固定少量可观察场景:输入/前置、操作、可见结果;已有场景直接链接。涉及运行资源影响时明确环境、负载、采样窗口和相关 CPU/内存/磁盘等指标,不发明通用阈值。交互或方案明显不确定时安排关键流程原型/早期体验;目标明确的小修改不增加中途签收。反馈未到可继续不依赖它的已授权工作。
|
|
9
|
+
需要深入探索或用户点名时可使用 `grill-me`;它是可选方法,缺失不阻塞任务,可直接澄清关键问题。需要治理时将已确认的范围、重要假设与未决取舍记入计划;只有新的必要决定等待回答,其余已授权工作继续。
|
|
19
10
|
|
|
20
11
|
## 仓库模型
|
|
21
12
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
```text
|
|
25
|
-
docs/
|
|
26
|
-
PLAN_MAP.md
|
|
27
|
-
plans/
|
|
28
|
-
<plan-name>.md
|
|
29
|
-
specs/
|
|
30
|
-
<capability>.md # 按需,优先复用已有 Schema/OpenAPI
|
|
31
|
-
adr/
|
|
32
|
-
0001-<decision>.md
|
|
33
|
-
migrations/
|
|
34
|
-
<migration-name>.md
|
|
35
|
-
```
|
|
36
|
-
|
|
37
|
-
只创建有真实内容的文件,不预建空目录或强制 PRD/design/tasks 组合。`docs/plans/*.md` 保持平铺。`docs/reviews/` 存独立复核,`docs/fixtures/` 存人工样本/回放说明,`tests/fixtures/` 存可执行输入,`docs/attestations/` 仅在使用快照时需要。
|
|
38
|
-
|
|
39
|
-
文档权责:
|
|
40
|
-
|
|
41
|
-
- `PLAN_MAP.md`:计划索引、状态、当前阶段、最后更新、依赖、替代/合并/废弃关系、推荐顺序、当前阻塞项、证据链接。
|
|
42
|
-
- `docs/plans/*.md`:本次变更的目标、范围、行为差异、阶段、Step 0、验证与完成条件;链接现行契约。没有独立契约来源时明确由计划哪个位置承载,不要求追溯提取旧内容。
|
|
43
|
-
- `docs/adr/*.md`:持久架构决策及其后果。
|
|
44
|
-
- `docs/migrations/*.md`:兼容策略、迁移步骤、回滚方式、旧行为保留窗口。
|
|
45
|
-
- 现行契约优先复用已有 Schema/OpenAPI。需要跨迭代维护文字行为且无合适来源时,才按需建立 `docs/specs/<capability>.md`;可使用 [spec 模板](../assets/spec.template.md),补充输入/输出、不变量、失败语义与可观察场景,不复制已有字段定义或计划进度。
|
|
13
|
+
只创建当前任务需要的文档,不预建完整目录或 PRD/spec/ADR 套装。
|
|
46
14
|
|
|
47
|
-
|
|
15
|
+
| 文档 | 职责 |
|
|
16
|
+
|---|---|
|
|
17
|
+
| `docs/PLAN_MAP.md` | 计划状态、阶段、关系、阻塞及证据入口 |
|
|
18
|
+
| `docs/plans/*.md` | 本次目标、范围、决策、差异与验证证据,计划文件平铺 |
|
|
19
|
+
| Schema/OpenAPI 或 `docs/specs/` | 现行公共契约,优先复用已有来源 |
|
|
20
|
+
| `docs/adr/`、`docs/migrations/` | 确有需要时记录长期架构决策或迁移步骤 |
|
|
21
|
+
| `docs/reviews/`、`docs/fixtures/` | 必要复核及可复验样本,代码 fixture 放 `tests/fixtures/` |
|
|
48
22
|
|
|
49
23
|
## 多文档同步规则
|
|
50
24
|
|
|
51
|
-
|
|
52
|
-
- `PLAN_MAP.md` 是状态、当前阶段、最后更新、依赖、替代/合并/废弃关系、推荐顺序、阻塞项和证据链接的事实源。
|
|
53
|
-
- 总路线图、优先级计划和索引只记录顺序、状态摘要和专项计划链接,不复制字段级方案、枚举、Step 0 细节或完成定义。
|
|
54
|
-
- 当专项计划的状态、当前阶段、最后更新、字段方案、完成条件或验证结果变化时,必须同步 `PLAN_MAP.md` 和所有引用该计划的路线图、优先级计划或索引。
|
|
55
|
-
- 验收治理文档时,必须用 `rg` 搜索同名计划、P 编号、状态名和关键字段,检查是否存在重复定义或漂移。
|
|
56
|
-
- 如果同一事实在多个文档中重复,保留一个事实源,其他文档改为链接引用。
|
|
57
|
-
- 实施者填写的计划状态、完成证据文字和文档格式只代表实施声明,不等于验收结论;按适用复核方式基于当前仓库内容、可复现验证命令和反向引用确认。
|
|
25
|
+
同一事实保留一个权威来源,其他文档引用。决策或结果改变后,同步地图及实际受影响的索引/引用;同一组已收敛变化可合并同步,不为每次保存增加流程。用 `rg` 定向检查相关计划名、关键字段和链接,不重扫无关历史。
|
|
58
26
|
|
|
59
27
|
## 草案和历史文档处理
|
|
60
28
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
- 除非用户明确点名要求更新这些文档,否则不要继续修改它们来承载新规范。
|
|
64
|
-
- 新发生的目标、范围、非目标、公共契约、字段、Schema、状态语义、阶段、验证方式、完成条件、风险和回滚,应写入 `docs/plans/*.md`、ADR、migration 或正式 spec。
|
|
65
|
-
- 计划状态、当前阶段、最后更新、依赖、替代/合并/废弃关系、推荐顺序、阻塞项和证据链接,应写入或同步 `docs/PLAN_MAP.md`。
|
|
66
|
-
- 历史草案可以在治理文档中作为背景材料链接引用,但不应承载新的规范事实。
|
|
67
|
-
- 如果治理文档出现“以草案为事实源”“详见草案中的字段定义”“草案为准”等表述,应改为由治理文档自身承载事实源,或链接到正式 spec/Schema。
|
|
68
|
-
- 当用户说“补充一下”“记录一下”“把刚才的结论写进去”时,如果治理已启用,默认写入治理文档;不要写入草案、README 或历史设计文档,除非用户明确点名该文件。
|
|
69
|
-
|
|
70
|
-
## 推荐状态
|
|
71
|
-
|
|
72
|
-
统一使用中文状态:
|
|
73
|
-
|
|
74
|
-
| 状态 | 含义 |
|
|
75
|
-
|---|---|
|
|
76
|
-
| `候选` | 记录了想法,但尚未承诺实施 |
|
|
77
|
-
| `设计中` | 正在明确范围、契约和门禁 |
|
|
78
|
-
| `待实施` | 当前阶段门禁已通过,但尚未开始 |
|
|
79
|
-
| `实施中` | 当前阶段推进中;适用时包含技术完成待用户验收 |
|
|
80
|
-
| `已完成` | 实现、验证、证据和文档齐全,并满足适用的用户验收 |
|
|
81
|
-
| `已替代` | 被另一个计划取代 |
|
|
82
|
-
| `已合并` | 并入另一个计划 |
|
|
83
|
-
| `已废弃` | 明确不再推进 |
|
|
84
|
-
|
|
29
|
+
历史草案、旧计划与临时分析保留为背景,不承载新规范。新事实写入当前计划或适用契约;用户明确指定的文档位置优先。已替代内容通过链接说明关系,避免重复改写历史。
|
|
85
30
|
|
|
86
31
|
## 漂移检查
|
|
87
32
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
- 既有 ADR 或核心原则
|
|
91
|
-
- 计划顺序、依赖、替代关系或有效性
|
|
92
|
-
- 公共 API、Schema、状态语义、兼容承诺或失败模式
|
|
93
|
-
- 多个计划共享的重复修改点
|
|
94
|
-
- 用来证明完成的测试或完成条件
|
|
95
|
-
- 专项计划与路线图、优先级计划或索引之间的状态、字段方案、完成条件或验证结果
|
|
96
|
-
|
|
97
|
-
## 停滞检查
|
|
98
|
-
|
|
99
|
-
`PLAN_MAP.md` 的计划索引应包含 `最后更新` 列,日期格式为 `YYYY-MM-DD`。
|
|
100
|
-
|
|
101
|
-
计划索引必须在同一个 `## 计划索引` 章节下,按固定顺序拆成三张六列表格:`### 未完成`、`### 已完成`、`### 已废弃`。状态归类规则如下:
|
|
102
|
-
|
|
103
|
-
- `未完成`:`候选`、`设计中`、`待实施`、`实施中`。
|
|
104
|
-
- `已完成`:`已完成`。
|
|
105
|
-
- `已废弃`:`已废弃`、`已替代`、`已合并`等不再推进的终态;表格标题不改变行内真实状态。
|
|
106
|
-
|
|
107
|
-
每个计划只能出现一次;没有对应计划的分类保留表头但不添加占位行。必须使用三级标题,以便治理 CLI 将三张表作为同一个计划索引读取。
|
|
33
|
+
目标、授权、关键契约或高影响后果变化时,重新判断受影响范围的准入与验证;等待必要取舍期间,可继续无依赖的已授权工作。普通反馈、测试结果和状态更新在当前任务内处理并同步,不自动暂停、重开阶段或再索取授权。范围调整不能移走真实阻塞。
|
|
108
34
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
```markdown
|
|
112
|
-
| 计划 | 状态 | 当前阶段 | 最后更新 | 依赖 | 证据 |
|
|
113
|
-
```
|
|
35
|
+
## 执行与恢复
|
|
114
36
|
|
|
115
|
-
|
|
37
|
+
沿用已确认决定,只展开本次差异、证据缺口及直接依赖。跨阶段工作按 [阶段门](verification.md#step-0-与阶段门)判断,每阶段有自身基线与完成条件;上一阶段通过不自动放行下一阶段,但同一交付范围的独立检查可按 verification 显式复用而不重审。未来事项需注明承接范围与依赖,不以未准入的后续工作阻断无关的当前交付。
|
|
116
38
|
|
|
117
|
-
-
|
|
118
|
-
- 当计划状态、当前阶段、阻塞项、依赖、完成条件、验证结果或实施范围变化时,同步 `最后更新`。
|
|
119
|
-
- `--stale-days` 只对 `候选`、`设计中`、`待实施`、`实施中` 等活跃计划输出 `WARNING`,不自动改变状态。
|
|
120
|
-
- `--stale-days` 省略数值时默认 10 天,可显式传入 N。
|
|
39
|
+
文档变更后做适用链接、结构和引用检查,运行所选版本的 `plan-governance-cli check .`;准入/发布使用 `--strict-readiness`。纯落档不重复代码测试。用户验收与证据真实性按 verification,不以文档格式通过替代。
|
|
121
40
|
|
|
122
|
-
##
|
|
41
|
+
## 格式附录:写入地图时读取
|
|
123
42
|
|
|
124
|
-
|
|
43
|
+
### 推荐状态
|
|
125
44
|
|
|
126
|
-
|
|
45
|
+
| 状态 | 含义 |
|
|
46
|
+
|---|---|
|
|
47
|
+
| 候选 | 尚未承诺实施 |
|
|
48
|
+
| 设计中 | 明确范围、契约与准入 |
|
|
49
|
+
| 待实施 | 当前阶段已准入 |
|
|
50
|
+
| 实施中 | 执行中,或技术完成等待适用用户验收 |
|
|
51
|
+
| 已完成 | 证据、验证及适用验收齐备 |
|
|
52
|
+
| 已替代、已合并、已废弃 | 终态,保留关系和原因 |
|
|
127
53
|
|
|
128
|
-
|
|
54
|
+
### 停滞检查
|
|
129
55
|
|
|
130
|
-
|
|
56
|
+
`## 计划索引` 下依次使用 `### 未完成`、`### 已完成`、`### 已废弃` 三张六列表;每个计划只出现一次。已替代/已合并归入已废弃分表,行内保留真实状态。无对应计划时保留表头。
|
|
131
57
|
|
|
132
|
-
|
|
58
|
+
| 计划 | 状态 | 当前阶段 | 最后更新 | 依赖 | 证据 |
|
|
59
|
+
|---|---|---|---|---|---|
|
|
133
60
|
|
|
134
|
-
|
|
61
|
+
`最后更新` 使用 YYYY-MM-DD,在状态、阶段、依赖、范围、阻塞或验证结果变化时同步;不从文件时间或 Git 推断。`check --stale-days [N]` 可提示活跃计划停滞,默认 10 天,不自动改变状态。
|
|
@@ -1,91 +1,71 @@
|
|
|
1
1
|
# 验证、复核与用户验收
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
本页维护统一复核行为、阶段门和交付证据;CLI 入口为 `guide verification`。写入记录时再读格式部分。
|
|
4
4
|
|
|
5
|
-
##
|
|
6
|
-
|
|
7
|
-
| 情况 | 执行方式 |
|
|
8
|
-
|---|---|
|
|
9
|
-
| 目标、范围和实际后果明确,可撤销,适用验证充分的低风险改动 | 当前 AI 检查差异、验证实际行为,失败后自主修复/复验;简报证据,不为每次改动派独立 subagent 或新建报告 |
|
|
10
|
-
| 安全/权限/隐私/公开暴露、数据完整性/迁移、核心公共契约/生命周期、广泛共享逻辑或难以撤销后果 | 保留独立复核,在关键设计或完成边界安排必要检查;一行代码或配置也可能高影响 |
|
|
11
|
-
| 风险暂不明确、反复修复无效或证据冲突 | 先补事实及定向验证;实质疑点仍在则定向独立检查,不凭自信降为低风险 |
|
|
12
|
-
|
|
13
|
-
按实际变化及其后果判断风险,不因文件位于安全模块、补一个诊断字段、发现下一处错误或完成一次复测,就新设准入和完成双重检查。阶段转换或收尾本身也不决定独立复核。已有计划或用户明确要求的门禁仍须遵循,新风险策略不能自动降级;可逆性评估实际外部后果。执行中影响扩大时重新评估。
|
|
14
|
-
|
|
15
|
-
需要独立复核且范围/授权已明确时,自动启动未参与实施或测试编写的只读 subagent 或等价复核者,不结束任务等用户再次说“复核吧”。同一授权、已审设计范围和不变量内的增量可合并检查,普通决定的文档同步由当前 AI 自查;高影响实施前门禁仍须及时满足。新增接受路径、权限、外部影响或关键假设变化时,先补事实与范围并满足适用准入,不能积压到最终复核追认。
|
|
16
|
-
|
|
17
|
-
调试时沿已知处理链提前准备必要的固定失败分类和值域,并明确输出对象/渠道、留存与清理方式,避免逐字段反复改动。只采集当前失败路径已取得的必要诊断,不为补齐信息继续执行失败后的认证步骤,不默认保存原始材料或异常全文。适用策略允许、无有效独立失败且验证充分时,既定诊断边界内不改变接受条件、控制流、响应契约或外部操作的增量由当前 AI 自验;新增隐私暴露、输出范围或共享影响则按具体风险检查。“只是诊断”及最终仍拒绝都不足以证明安全边界未变。
|
|
5
|
+
## 记录与兼容
|
|
18
6
|
|
|
19
|
-
|
|
7
|
+
新旧计划采用同一复核行为;字段只决定记录格式,不决定是否重审或如何恢复:
|
|
20
8
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
## Step 0 与阶段门
|
|
24
|
-
|
|
25
|
-
阶段 N 完成只关闭 N,N+1 默认设计中。每个当前阶段自身必须有目标/范围/非目标、可观察 Step 0、输入与可执行命令/预期/失败判定/输出位置的样本矩阵、验证与完成条件、失败/回滚边界、无未解决阻塞,以及适用复核通过,才可待实施。机器检查只核对结构,不证明实现、风险分类、证据真实性或用户接受。
|
|
26
|
-
|
|
27
|
-
| 任务 | Step 0 |
|
|
9
|
+
| 记录形式 | 解释 |
|
|
28
10
|
|---|---|
|
|
29
|
-
|
|
|
30
|
-
|
|
|
31
|
-
|
|
|
32
|
-
| 架构探索 | 最小原型、风险实验或关键假设验证 |
|
|
11
|
+
| 单次独立复核 | 当前模板标签 |
|
|
12
|
+
| 风险分流 | 历史标签,按同一单次行为解释,不触发迁移或重审 |
|
|
13
|
+
| 无字段 | 保留六列历史格式,后续自验类型见旧计划兼容 |
|
|
33
14
|
|
|
34
|
-
|
|
15
|
+
显式字段的空值、未知值和重复声明不能放行。升级不批量改写计划;原检查按真实日期、结论、证据和身份保留,旧失败须由真实修复自验闭环,格式统一本身不解决问题。CLI 能力不符时说明缺口,更新按授权处理。
|
|
35
16
|
|
|
36
|
-
|
|
17
|
+
## 按风险复核
|
|
37
18
|
|
|
38
|
-
|
|
19
|
+
以下方式适用于所有计划;普通无计划任务也按实际风险选择检查,不建立阶段表。
|
|
39
20
|
|
|
40
|
-
|
|
21
|
+
普通改动由实施者自验。改变权限、安全、隐私、资金、数据完整性、核心契约或产生广泛/难以撤销后果时,按实际高风险或高影响安排独立检查;不按目录、行数或阶段事件触发。风险不清先查事实。
|
|
41
22
|
|
|
42
|
-
|
|
23
|
+
同一交付范围只检查一次,通常在实现和自验收敛后、交付或高影响外部动作前;需要设计准入时将这一次前置,后续实施与完成自验。已审范围内的增量和修复合并处理,确有新增高风险范围才另行判断,不把每个修复拆成新范围;后续独立通过不能清除这一次检查留下的发现。
|
|
43
24
|
|
|
44
|
-
|
|
25
|
+
授权明确时自动安排一名未参与实现或测试编写的只读复核者,提供范围、差异、风险和证据入口,并要求其自行完成、不得再委派复核。派发前复用已有同范围任务或结果;等待失败先核对准确任务 ID 与实际状态,只有确认原任务不存在或不可恢复才补发。冻结受审范围并告知变化。独立者给出发现后,实施者修复并验证原问题及受影响回归,不再送审,未解决则保持未完成。检查不可用/超时直接记录状态,不能伪填已完成结论;业务缺陷描述中的“超时”不代表检查未完成。复核不代替外部授权。
|
|
45
26
|
|
|
46
|
-
|
|
27
|
+
## Step 0 与阶段门
|
|
47
28
|
|
|
48
|
-
|
|
49
|
-
|---|---|
|
|
50
|
-
| 日期 / 阶段 | YYYY-MM-DD / 与地图当前阶段一致 |
|
|
51
|
-
| 方式 | 自验或独立 |
|
|
52
|
-
| 风险 | 低风险、高影响或待判断;待判断不能通过,自验仅限低风险 |
|
|
53
|
-
| 风险依据 | 实际范围、后果/可逆性、验证充分性的具体依据;非占位 |
|
|
54
|
-
| 结论 | 尚未进行或实际结果;有效通过必须明确通过,不能以空值、失败、不可用、超时、证据冲突代替 |
|
|
55
|
-
| 证据 / 复核者 | 可复验命令/结果或其可访问链接,以及真实执行者身份;自验不假称独立 |
|
|
29
|
+
当前阶段达到待实施,需要目标与范围、可观察 Step 0、可执行样本或替代基线、验证与完成条件、失败/回滚边界、无未解决阻塞及适用复核。样本说明输入、命令、预期结果、失败判定和输出位置。上阶段通过不替代本阶段准入;普通小改不新建整套治理。
|
|
56
30
|
|
|
57
|
-
|
|
31
|
+
Step 0 可采用缺陷复现、迁移快照/兼容样本、新功能验收 fixture 或关键假设实验;不适合固定样本时说明可观察替代方案,不在实施后倒填通过证据。
|
|
58
32
|
|
|
59
|
-
|
|
33
|
+
## 验证、修复与证据复用
|
|
60
34
|
|
|
61
|
-
|
|
35
|
+
验证用户可观察结果和受影响回归,完成项目要求的检查。纯文档做适用文档检查并注明覆盖率不适用。检查通过后,只有新变化、失败或未解疑点才扩大或重复验证。
|
|
62
36
|
|
|
63
|
-
|
|
37
|
+
复用证据须覆盖当前工作树、依赖/配置、环境和验收契约,内容可访问;仅 HEAD/版本相同不足以证明有效。同一交付范围的前置独立检查可跨阶段复用,但须在当前阶段以 `复核基线复用` 明确关联原记录、来源阶段和范围;未来阶段或未解决基线不可复用,新增范围重新判断风险。缺口补受影响验证,未解决冲突如实保留。只有状态、证据或链接同步时,不重新跑代码全套;无关任务失败如实报告,不擅自接管。
|
|
64
38
|
|
|
65
|
-
|
|
39
|
+
自验失败自主修复/复验。保留独立发现,按“发现 → 修复 → 验证”闭环并准确标记自验。机械检查只证明结构,不能证明风险、范围、独立性或证据真实性。可选 attestation 的显式检查见 [CLI](cli.md#初始化模板和检查脚本)。
|
|
66
40
|
|
|
67
|
-
|
|
41
|
+
## 用户可观察验收与交付
|
|
68
42
|
|
|
69
|
-
|
|
43
|
+
用场景、输入/操作、可见结果与证据说明完成;CLI 输出、文件差异和调用方行为也可作为验收。性能结论要有可比实测,不能用字符数或工具次数推算 token、耗时或质量改善。
|
|
70
44
|
|
|
71
|
-
|
|
45
|
+
明确采用用户体验闭环的功能计划,技术完成待用户查看时保持 `实施中`,结构化 `下一动作` 填 `等待用户验收`;用户接受后才关闭,技术通过、提交或沉默不代替接受。普通小改、纯落档不增加逐次签收。原范围问题自主修复,新需求另行归属;等待用户才能提供的结果时可继续无依赖工作。
|
|
72
46
|
|
|
73
|
-
|
|
47
|
+
交付简报完成结果、必要验证、剩余阻塞及证据入口,不复制完整日志。资料不可访问时补可复验要点;记录同步不再安排复核。
|
|
74
48
|
|
|
75
|
-
|
|
49
|
+
## 格式附录:写入阶段记录时读取
|
|
76
50
|
|
|
77
|
-
|
|
51
|
+
### 显式策略格式
|
|
78
52
|
|
|
79
|
-
|
|
53
|
+
显式策略在摘要以 `最新阶段复核` 引用替代旧 `最新独立准入复核`,其余阶段字段保留。最新表采用两列字段/内容:
|
|
80
54
|
|
|
81
|
-
|
|
55
|
+
| 字段 | 要求 |
|
|
56
|
+
|---|---|
|
|
57
|
+
| 日期 / 阶段 | YYYY-MM-DD / 与地图当前阶段一致 |
|
|
58
|
+
| 方式 / 风险 | 自验或独立;低风险、高影响、高风险或待判断 |
|
|
59
|
+
| 风险依据 | 当前实际范围、后果与验证依据 |
|
|
60
|
+
| 结论 | 尚未进行或明确结果;待判断风险不能通过,候选、疑问或占位不算通过 |
|
|
61
|
+
| 证据 / 复核者 | 可复验记录或链接 / 真实执行者身份 |
|
|
82
62
|
|
|
83
|
-
|
|
63
|
+
`阶段复核记录` 按 `日期、类型、阶段、方式、风险、结论、证据、复核者` 八列追加,日期顺序和最新表与当前阶段末条一致。完整结果须有有效日期、证据、身份及已判断风险;普通无计划任务无需建表。
|
|
84
64
|
|
|
85
|
-
|
|
65
|
+
高风险/高影响自验须有同范围完整独立基线。本阶段已有基线直接引用;复用前阶段基线时追加类型 `复核基线复用`、方式 `自验` 的当前阶段记录,证据须包含来源阶段并关联原记录和相同范围。独立发现修复后用类型 `修复自验`、方式 `自验` 追加,证据说明原发现与修复回归,不能以普通自验或第二次独立通过覆盖未修复发现。自验不伪称独立通过;上一阶段历史不自动阻断当前阶段。
|
|
86
66
|
|
|
87
|
-
|
|
67
|
+
结论以明确前缀记录:`通过`、`未通过`等;复核入口不可用/超时直接写其状态,实际查出的证据冲突可作为发现。通过说明不能覆盖结构化未解决事项。check 默认对新准入缺陷 WARNING,待实施/实施中在 `--strict-readiness` 下 ERROR。workset 保持 schema 1:待自验 `verify`、待独立 `independent_review`、通过待地图同步 `sync`、失败 `blocked/resolve_blocker`、歧义 `unknown/unknown`;不自动写状态,hook 只提示。
|
|
88
68
|
|
|
89
|
-
|
|
69
|
+
### 旧计划兼容
|
|
90
70
|
|
|
91
|
-
|
|
71
|
+
没有策略字段时,保留固定标题 `阶段路线图`、`当前阶段`、`阶段准入摘要`、`最新独立准入复核`、`独立复核记录`,不为统一行为重写历史。摘要字段为准入状态、Step 0、样本矩阵、验证方式、失败/回滚边界、当前阻塞项及最新独立准入复核。最新表含日期、阶段、结论、证据、复核者;历史使用日期、类型、阶段、结论、证据、复核者六列。普通改动写类型 `普通自验`;独立发现后的闭环写 `修复自验`,且不能清除未完成或缺证据的检查;跨阶段复用写 `复核基线复用` 并关联原记录。其他历史类型按独立检查解释,最新表与当前阶段末条适用记录一致。标题不加阶段编号,阶段身份以地图为准。
|
|
@@ -217,8 +217,144 @@ def review_outcome(conclusion):
|
|
|
217
217
|
return "unknown"
|
|
218
218
|
|
|
219
219
|
|
|
220
|
+
def valid_review_date(value):
|
|
221
|
+
if not re.fullmatch(r"\d{4}-\d{2}-\d{2}", value or ""):
|
|
222
|
+
return None
|
|
223
|
+
try:
|
|
224
|
+
return parse_plan_date(value)
|
|
225
|
+
except ValueError:
|
|
226
|
+
return None
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
def legacy_review_method(kind):
|
|
230
|
+
"""六列旧记录用类型显式表达自验;其他历史类型仍按独立事实解释。"""
|
|
231
|
+
return "自验" if kind == "复核基线复用" or "自验" in kind else "独立"
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
def phase_precedes(plan_text, candidate, current):
|
|
235
|
+
"""只允许路线图中更早的阶段;标准数字阶段可在精简路线图中回退比较。"""
|
|
236
|
+
roadmap = [row[0] for row in phase_roadmap_rows(plan_text)]
|
|
237
|
+
if candidate in roadmap and current in roadmap:
|
|
238
|
+
return roadmap.index(candidate) < roadmap.index(current)
|
|
239
|
+
candidate_match = re.fullmatch(r"阶段\s*(\d+)", candidate or "")
|
|
240
|
+
current_match = re.fullmatch(r"阶段\s*(\d+)", current or "")
|
|
241
|
+
return bool(candidate_match and current_match
|
|
242
|
+
and int(candidate_match.group(1)) < int(current_match.group(1)))
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
def legacy_review_state(plan_text, phase):
|
|
246
|
+
"""在不改写六列历史的前提下应用统一单次复核闭环。"""
|
|
247
|
+
result = {"passed": False, "pending_action": None, "issues": [], "blockers": []}
|
|
248
|
+
issues, blockers = result["issues"], result["blockers"]
|
|
249
|
+
rows = review_history_rows(plan_text)
|
|
250
|
+
known_phases = {row[0] for row in phase_roadmap_rows(plan_text)}
|
|
251
|
+
valid_rows = []
|
|
252
|
+
previous_dates = {}
|
|
253
|
+
for row in rows:
|
|
254
|
+
if len(row) != 6:
|
|
255
|
+
kind = row[1] if len(row) > 1 else ""
|
|
256
|
+
row_phase = row[2] if len(row) > 2 else ""
|
|
257
|
+
if ("准入" in kind or "readiness" in kind.lower() or "自验" in kind
|
|
258
|
+
or kind == "复核基线复用") and (not row_phase or row_phase == phase):
|
|
259
|
+
issues.append("旧独立复核记录必须使用日期、类型、阶段、结论、证据、复核者六列")
|
|
260
|
+
continue
|
|
261
|
+
kind, conclusion = row[1], row[3]
|
|
262
|
+
if not (
|
|
263
|
+
"准入" in kind
|
|
264
|
+
or "readiness" in kind.lower()
|
|
265
|
+
or "自验" in kind
|
|
266
|
+
or kind == "复核基线复用"
|
|
267
|
+
or (("完成验收" in kind or "完成复核" in kind) and review_outcome(conclusion) == "passed")
|
|
268
|
+
):
|
|
269
|
+
continue
|
|
270
|
+
row_phase = row[2]
|
|
271
|
+
if row_phase != phase and row_phase not in known_phases and not re.fullmatch(r"阶段\s*\d+", row_phase):
|
|
272
|
+
continue
|
|
273
|
+
valid_rows.append(row)
|
|
274
|
+
parsed = valid_review_date(row[0])
|
|
275
|
+
previous = previous_dates.get(row_phase)
|
|
276
|
+
if parsed and previous and parsed < previous:
|
|
277
|
+
issues.append(f"旧独立复核记录 {row_phase} 日期逆序,无法确认最新结果")
|
|
278
|
+
if parsed:
|
|
279
|
+
previous_dates[row_phase] = parsed
|
|
280
|
+
|
|
281
|
+
def qualified(row):
|
|
282
|
+
return (len(row) == 6 and not any(is_placeholder(value) for value in row)
|
|
283
|
+
and valid_review_date(row[0]) is not None)
|
|
284
|
+
|
|
285
|
+
def completed_independent(row):
|
|
286
|
+
conclusion = row[3].strip()
|
|
287
|
+
return qualified(row) and legacy_review_method(row[1]) == "独立" and (
|
|
288
|
+
review_outcome(conclusion) == "passed" or
|
|
289
|
+
conclusion.startswith(("未通过", "不通过", "失败", "不满足", "拒绝", "证据冲突"))
|
|
290
|
+
and review_outcome(conclusion) == "failed"
|
|
291
|
+
)
|
|
292
|
+
|
|
293
|
+
prior_states = {}
|
|
294
|
+
for row in valid_rows:
|
|
295
|
+
if row[2] == phase or not phase_precedes(plan_text, row[2], phase):
|
|
296
|
+
continue
|
|
297
|
+
state = prior_states.setdefault(row[2], {"reviewed": False, "unresolved": False})
|
|
298
|
+
method = legacy_review_method(row[1])
|
|
299
|
+
outcome = review_outcome(row[3])
|
|
300
|
+
if method == "独立":
|
|
301
|
+
state["reviewed"] = completed_independent(row)
|
|
302
|
+
if outcome == "failed":
|
|
303
|
+
state["unresolved"] = True
|
|
304
|
+
elif (row[1] == "修复自验" and state["reviewed"] and outcome == "passed"
|
|
305
|
+
and qualified(row)):
|
|
306
|
+
state["unresolved"] = False
|
|
307
|
+
reusable_prior_phases = {
|
|
308
|
+
source_phase for source_phase, state in prior_states.items()
|
|
309
|
+
if state["reviewed"] and not state["unresolved"]
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
current = [row for row in valid_rows if row[2] == phase]
|
|
313
|
+
reviewed = False
|
|
314
|
+
unresolved = None
|
|
315
|
+
for row in current:
|
|
316
|
+
method = legacy_review_method(row[1])
|
|
317
|
+
outcome = review_outcome(row[3])
|
|
318
|
+
if outcome == "unknown":
|
|
319
|
+
issues.append("旧独立复核记录结论为空或未知")
|
|
320
|
+
if method == "独立":
|
|
321
|
+
reviewed = completed_independent(row)
|
|
322
|
+
if outcome == "failed":
|
|
323
|
+
unresolved = row[3]
|
|
324
|
+
elif row[1] == "复核基线复用":
|
|
325
|
+
referenced = [source for source in reusable_prior_phases if source in row[4]]
|
|
326
|
+
if not referenced:
|
|
327
|
+
issues.append("旧格式复核基线复用须在证据中指向已解决的前阶段独立基线")
|
|
328
|
+
elif outcome == "passed" and qualified(row):
|
|
329
|
+
reviewed = True
|
|
330
|
+
elif row[1] == "修复自验":
|
|
331
|
+
if not reviewed:
|
|
332
|
+
issues.append("旧格式修复自验缺少已完成的独立复核基线")
|
|
333
|
+
elif outcome == "passed" and qualified(row):
|
|
334
|
+
unresolved = None
|
|
335
|
+
if outcome == "passed" and not qualified(row):
|
|
336
|
+
issues.append("旧独立复核记录通过缺少有效日期、类型、证据或身份")
|
|
337
|
+
|
|
338
|
+
if unresolved:
|
|
339
|
+
blockers.append("旧独立复核记录存在未修复的独立发现:" + unresolved)
|
|
340
|
+
if current and review_outcome(current[-1][3]) == "failed":
|
|
341
|
+
blockers.append("旧独立复核记录最新结果:" + current[-1][3])
|
|
342
|
+
|
|
343
|
+
review = key_value_table(fixed_section(plan_text, "最新独立准入复核"))
|
|
344
|
+
outcome = review_outcome(review.get("结论", ""))
|
|
345
|
+
latest_matches = bool(current) and all(
|
|
346
|
+
review.get(field) == current[-1][index]
|
|
347
|
+
for field, index in [("日期", 0), ("阶段", 2), ("结论", 3), ("证据", 4), ("复核者", 5)]
|
|
348
|
+
)
|
|
349
|
+
result["passed"] = outcome == "passed" and latest_matches and not issues and not blockers
|
|
350
|
+
if outcome == "pending" and not issues and not blockers:
|
|
351
|
+
method = legacy_review_method(current[-1][1]) if current else "独立"
|
|
352
|
+
result["pending_action"] = "verify" if method == "自验" else "independent_review"
|
|
353
|
+
return result
|
|
354
|
+
|
|
355
|
+
|
|
220
356
|
def risk_review_state(plan_text, phase):
|
|
221
|
-
"""
|
|
357
|
+
"""显式风险策略的共享事实判定;历史策略名不再改变单次复核行为。"""
|
|
222
358
|
summary_section = fixed_section(top_level_section(plan_text, "当前阶段"), "阶段准入摘要")
|
|
223
359
|
policies = [row for row in markdown_table_rows(summary_section) if row[0] == "复核策略"]
|
|
224
360
|
result = {"enabled": bool(policies), "passed": False, "pending_action": None,
|
|
@@ -226,11 +362,12 @@ def risk_review_state(plan_text, phase):
|
|
|
226
362
|
if not policies:
|
|
227
363
|
return result
|
|
228
364
|
issues, blockers = result["issues"], result["blockers"]
|
|
229
|
-
if len(policies) != 1 or len(policies[0]) != 2 or policies[0][1]
|
|
230
|
-
issues.append("复核策略必须唯一且为
|
|
365
|
+
if len(policies) != 1 or len(policies[0]) != 2 or policies[0][1] not in {"风险分流", "单次独立复核"}:
|
|
366
|
+
issues.append("复核策略必须唯一且为 风险分流/单次独立复核;空值、未知值或重复声明不能准入")
|
|
367
|
+
risks = {"低风险", "高影响", "高风险"}
|
|
231
368
|
summary = key_value_table(summary_section)
|
|
232
369
|
if "最新独立准入复核" in summary:
|
|
233
|
-
issues.append("
|
|
370
|
+
issues.append("显式复核策略的阶段准入摘要应以 最新阶段复核 替代 最新独立准入复核")
|
|
234
371
|
if is_placeholder(summary.get("最新阶段复核")):
|
|
235
372
|
issues.append("阶段准入摘要缺少有效的 最新阶段复核 链接")
|
|
236
373
|
|
|
@@ -245,12 +382,10 @@ def risk_review_state(plan_text, phase):
|
|
|
245
382
|
issues.append(f"最新阶段复核阶段与 PLAN_MAP 当前阶段 {phase} 不一致")
|
|
246
383
|
if review.get("方式") not in {"自验", "独立"}:
|
|
247
384
|
issues.append("最新阶段复核方式必须为 自验/独立")
|
|
248
|
-
if review.get("风险") not in
|
|
249
|
-
issues.append("最新阶段复核风险必须为
|
|
385
|
+
if review.get("风险") not in risks | {"待判断"}:
|
|
386
|
+
issues.append("最新阶段复核风险必须为 " + "/".join(sorted(risks)) + "/待判断")
|
|
250
387
|
if review.get("风险") == "待判断":
|
|
251
388
|
issues.append("最新阶段复核风险待判断,不能准入")
|
|
252
|
-
if review.get("方式") == "自验" and review.get("风险") != "低风险":
|
|
253
|
-
issues.append("自验只适用于低风险,不能代替高影响独立复核")
|
|
254
389
|
if is_placeholder(review.get("风险依据")):
|
|
255
390
|
issues.append("最新阶段复核风险依据为空或占位")
|
|
256
391
|
if outcome == "unknown":
|
|
@@ -258,26 +393,29 @@ def risk_review_state(plan_text, phase):
|
|
|
258
393
|
if outcome == "failed" and review.get("阶段") == phase:
|
|
259
394
|
blockers.append("最新阶段复核:" + review["结论"])
|
|
260
395
|
|
|
261
|
-
|
|
262
|
-
if not re.fullmatch(r"\d{4}-\d{2}-\d{2}", value):
|
|
263
|
-
return None
|
|
264
|
-
try:
|
|
265
|
-
return parse_plan_date(value)
|
|
266
|
-
except ValueError:
|
|
267
|
-
return None
|
|
396
|
+
valid_date = valid_review_date
|
|
268
397
|
|
|
269
398
|
def qualified(row, legacy=False):
|
|
270
|
-
#
|
|
399
|
+
# 独立基线须有完整身份和证据;记录的结论另行判断。
|
|
271
400
|
return (len(row) == (6 if legacy else 8) and not any(is_placeholder(value) for value in row)
|
|
272
401
|
and valid_date(row[0]) is not None
|
|
273
|
-
and (legacy or (row[3] == "独立" and row[4] in
|
|
402
|
+
and (legacy or (row[3] == "独立" and row[4] in risks)))
|
|
403
|
+
|
|
404
|
+
def completed(row):
|
|
405
|
+
# 明确的通过/缺陷结论代表已完成检查;不扫描业务发现中的状态词。
|
|
406
|
+
conclusion = row[5].strip()
|
|
407
|
+
return qualified(row) and (
|
|
408
|
+
review_outcome(conclusion) == "passed" or
|
|
409
|
+
conclusion.startswith(("未通过", "不通过", "失败", "不满足", "拒绝", "证据冲突"))
|
|
410
|
+
and review_outcome(conclusion) == "failed"
|
|
411
|
+
)
|
|
274
412
|
|
|
275
413
|
known_phases = {row[0] for row in phase_roadmap_rows(plan_text)}
|
|
276
414
|
|
|
277
415
|
def known_other_phase(value):
|
|
278
416
|
return value != phase and (value in known_phases or re.fullmatch(r"阶段\s*\d+", value))
|
|
279
417
|
|
|
280
|
-
def
|
|
418
|
+
def history_rows(title, width):
|
|
281
419
|
section = fixed_section(plan_text, title)
|
|
282
420
|
rows = markdown_table_rows(section)
|
|
283
421
|
expected = (["日期", "类型", "阶段", "方式", "风险", "结论", "证据", "复核者"]
|
|
@@ -286,37 +424,95 @@ def risk_review_state(plan_text, phase):
|
|
|
286
424
|
issues.append(f"{title} 必须使用固定的 {width} 列表头")
|
|
287
425
|
if rows.count(expected) > 1:
|
|
288
426
|
issues.append(f"{title} 存在重复表头,无法确认记录结构")
|
|
289
|
-
|
|
290
|
-
|
|
427
|
+
valid = []
|
|
428
|
+
previous_dates = {}
|
|
291
429
|
for row in rows:
|
|
292
430
|
if row == expected:
|
|
293
431
|
continue
|
|
294
432
|
if len(row) > 2 and row[2] != phase:
|
|
295
|
-
if known_other_phase(row[2]):
|
|
433
|
+
if not known_other_phase(row[2]):
|
|
434
|
+
issues.append(f"{title} 记录阶段为空或未知,不能忽略")
|
|
296
435
|
continue
|
|
297
|
-
|
|
298
|
-
continue
|
|
299
|
-
if len(row) != width or len(row) < 3 or row[2] != phase:
|
|
436
|
+
if len(row) != width or len(row) < 3 or (row[2] != phase and not known_other_phase(row[2])):
|
|
300
437
|
issues.append(f"{title} 记录字段不足、列数不符或阶段不明")
|
|
301
438
|
continue
|
|
302
|
-
|
|
439
|
+
valid.append(row)
|
|
303
440
|
if is_placeholder(row[1]):
|
|
304
441
|
issues.append(f"{title} 类型为空或占位")
|
|
305
442
|
parsed = valid_date(row[0])
|
|
306
443
|
if not is_placeholder(row[0]) and parsed is None:
|
|
307
444
|
issues.append(f"{title} 日期不合法:{row[0]}")
|
|
445
|
+
previous_date = previous_dates.get(row[2])
|
|
308
446
|
if parsed and previous_date and parsed < previous_date:
|
|
309
|
-
issues.append(f"{title}
|
|
447
|
+
issues.append(f"{title} {row[2]} 记录日期逆序,无法确认最新结果")
|
|
310
448
|
if parsed:
|
|
311
|
-
|
|
312
|
-
return
|
|
449
|
+
previous_dates[row[2]] = parsed
|
|
450
|
+
return valid
|
|
313
451
|
|
|
314
|
-
|
|
452
|
+
all_history = history_rows("阶段复核记录", 8)
|
|
453
|
+
history = [row for row in all_history if row[2] == phase]
|
|
454
|
+
|
|
455
|
+
prior_states = {}
|
|
456
|
+
for row in all_history:
|
|
457
|
+
if (row[2] == phase or not phase_precedes(plan_text, row[2], phase)
|
|
458
|
+
or row[3] not in {"自验", "独立"}):
|
|
459
|
+
continue
|
|
460
|
+
state = prior_states.setdefault(row[2], {"reviewed_high": False, "unresolved": False})
|
|
461
|
+
if row[3] == "独立":
|
|
462
|
+
if completed(row):
|
|
463
|
+
state["reviewed_high"] = row[4] in {"高风险", "高影响"}
|
|
464
|
+
else:
|
|
465
|
+
state["reviewed_high"] = False
|
|
466
|
+
row_outcome = review_outcome(row[5])
|
|
467
|
+
if row_outcome == "failed":
|
|
468
|
+
state["unresolved"] = True
|
|
469
|
+
elif (row[1] == "修复自验" and state["reviewed_high"]
|
|
470
|
+
and review_outcome(row[5]) == "passed"
|
|
471
|
+
and not any(is_placeholder(value) for value in row)
|
|
472
|
+
and valid_date(row[0]) is not None):
|
|
473
|
+
state["unresolved"] = False
|
|
474
|
+
reusable_prior_phases = {
|
|
475
|
+
source_phase for source_phase, state in prior_states.items()
|
|
476
|
+
if state["reviewed_high"] and not state["unresolved"]
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
reviewed = False
|
|
480
|
+
reviewed_high = False
|
|
481
|
+
repair_date = None
|
|
482
|
+
unresolved_single = None
|
|
315
483
|
for row in history:
|
|
316
|
-
if row[3] not in {"自验", "独立"} or row[4] not in
|
|
484
|
+
if row[3] not in {"自验", "独立"} or row[4] not in risks | {"待判断"}:
|
|
317
485
|
issues.append("阶段复核记录方式或风险未知")
|
|
318
|
-
if row[3] == "
|
|
319
|
-
|
|
486
|
+
if row[3] == "独立":
|
|
487
|
+
if completed(row):
|
|
488
|
+
reviewed = True
|
|
489
|
+
reviewed_high = row[4] in {"高风险", "高影响"}
|
|
490
|
+
else:
|
|
491
|
+
reviewed = reviewed_high = False
|
|
492
|
+
if review_outcome(row[5]) == "failed":
|
|
493
|
+
unresolved_single = row[5]
|
|
494
|
+
elif review_outcome(row[5]) == "passed" and completed(row):
|
|
495
|
+
# 后续独立通过可形成新范围基线,但不能清除既有独立发现。
|
|
496
|
+
pass
|
|
497
|
+
elif row[3] == "自验":
|
|
498
|
+
if row[1] == "复核基线复用":
|
|
499
|
+
referenced = [source for source in reusable_prior_phases if source in row[6]]
|
|
500
|
+
if row[4] not in {"高风险", "高影响"} or not referenced:
|
|
501
|
+
issues.append("复核基线复用须在证据中指向已解决的前阶段高风险/高影响独立基线")
|
|
502
|
+
elif (review_outcome(row[5]) == "passed"
|
|
503
|
+
and not any(is_placeholder(value) for value in row)
|
|
504
|
+
and valid_date(row[0]) is not None):
|
|
505
|
+
reviewed = reviewed_high = True
|
|
506
|
+
elif row[4] in {"高风险", "高影响"} and not reviewed_high:
|
|
507
|
+
issues.append("高风险/高影响自验缺少同范围独立复核基线或明确的跨阶段复用记录")
|
|
508
|
+
if row[1] == "修复自验":
|
|
509
|
+
if not reviewed:
|
|
510
|
+
issues.append("修复自验缺少已完成的独立复核基线")
|
|
511
|
+
elif (review_outcome(row[5]) == "passed" and row[4] in risks
|
|
512
|
+
and not any(is_placeholder(value) for value in row)
|
|
513
|
+
and valid_date(row[0]) is not None):
|
|
514
|
+
unresolved_single = None
|
|
515
|
+
repair_date = valid_date(row[0])
|
|
320
516
|
if review_outcome(row[5]) == "unknown":
|
|
321
517
|
issues.append("阶段复核记录结论为空或未知")
|
|
322
518
|
if review_outcome(row[5]) == "passed" and (
|
|
@@ -339,19 +535,11 @@ def risk_review_state(plan_text, phase):
|
|
|
339
535
|
if (outcome == "passed" or not is_placeholder(review.get("日期"))) and valid_date(review.get("日期", "")) is None:
|
|
340
536
|
issues.append("最新阶段复核日期不合法")
|
|
341
537
|
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
for row in independent:
|
|
345
|
-
row_outcome = review_outcome(row[5])
|
|
346
|
-
if row_outcome == "failed":
|
|
347
|
-
unresolved = row[5]
|
|
348
|
-
elif row_outcome == "passed" and qualified(row):
|
|
349
|
-
unresolved = None
|
|
350
|
-
if unresolved:
|
|
351
|
-
blockers.append("阶段复核记录存在未解除的独立失败:" + unresolved)
|
|
538
|
+
if unresolved_single:
|
|
539
|
+
blockers.append("阶段复核记录存在未修复的独立发现:" + unresolved_single)
|
|
352
540
|
|
|
353
541
|
# 旧独立记录仍是独立事实源;完成失败也不能被新自验覆盖。
|
|
354
|
-
legacy_history =
|
|
542
|
+
legacy_history = [row for row in history_rows("独立复核记录", 6) if row[2] == phase]
|
|
355
543
|
legacy_section = fixed_section(plan_text, "最新独立准入复核")
|
|
356
544
|
legacy = key_value_table(legacy_section)
|
|
357
545
|
legacy_current = legacy.get("阶段") == phase
|
|
@@ -370,18 +558,25 @@ def risk_review_state(plan_text, phase):
|
|
|
370
558
|
unresolved = None
|
|
371
559
|
for row in legacy_history:
|
|
372
560
|
row_outcome = review_outcome(row[3])
|
|
561
|
+
if not qualified(row, legacy=True):
|
|
562
|
+
issues.append("旧独立复核记录缺少有效日期、类型、证据或身份,迁移不能掩盖")
|
|
373
563
|
if row_outcome == "failed":
|
|
374
564
|
unresolved = row[3]
|
|
375
565
|
elif row_outcome == "passed" and qualified(row, legacy=True):
|
|
376
|
-
|
|
566
|
+
pass
|
|
377
567
|
elif row_outcome == "passed":
|
|
378
568
|
issues.append("旧独立通过记录缺少有效日期、类型、证据或身份")
|
|
379
569
|
elif row_outcome == "unknown":
|
|
380
570
|
issues.append("旧独立复核记录结论为空或未知")
|
|
381
|
-
|
|
571
|
+
# 显式迁移后保留旧结论;新记录中的有效修复自验可解除旧发现。
|
|
572
|
+
legacy_dates = [valid_date(row[0]) for row in legacy_history]
|
|
573
|
+
legacy_repaired = (repair_date is not None and bool(legacy_dates)
|
|
574
|
+
and all(date is not None and date <= repair_date for date in legacy_dates)
|
|
575
|
+
and not unresolved_single)
|
|
576
|
+
if unresolved and not legacy_repaired:
|
|
382
577
|
blockers.append("旧独立复核记录存在未解除的失败:" + unresolved)
|
|
383
578
|
if legacy_current:
|
|
384
|
-
if review_outcome(legacy.get("结论", "")) == "failed":
|
|
579
|
+
if review_outcome(legacy.get("结论", "")) == "failed" and not legacy_repaired:
|
|
385
580
|
blockers.append("最新独立准入复核:" + legacy["结论"])
|
|
386
581
|
elif not legacy_history or any(is_placeholder(legacy.get(field))
|
|
387
582
|
for field in ["日期", "阶段", "结论", "证据", "复核者"]) or valid_date(legacy.get("日期", "")) is None:
|
|
@@ -510,10 +705,12 @@ def check_phase_structure(plan_name, data, plan_text, strict, warnings, errors,
|
|
|
510
705
|
history = [
|
|
511
706
|
row
|
|
512
707
|
for row in review_history_rows(plan_text)
|
|
513
|
-
if len(row)
|
|
708
|
+
if len(row) == 6
|
|
514
709
|
and row[2] == current_phase
|
|
515
710
|
and (
|
|
516
711
|
"准入" in row[1]
|
|
712
|
+
or "自验" in row[1]
|
|
713
|
+
or row[1] == "复核基线复用"
|
|
517
714
|
or ("完成验收" in row[1] and row[3].startswith("通过"))
|
|
518
715
|
or ("完成复核" in row[1] and row[3].startswith("通过"))
|
|
519
716
|
or "readiness" in row[1].lower()
|
|
@@ -543,6 +740,16 @@ def check_phase_structure(plan_name, data, plan_text, strict, warnings, errors,
|
|
|
543
740
|
strict,
|
|
544
741
|
f"{plan_name}: 最新独立准入复核与历史记录最后一条结论冲突",
|
|
545
742
|
)
|
|
743
|
+
legacy_state = legacy_review_state(plan_text, current_phase)
|
|
744
|
+
for issue in legacy_state["issues"] + legacy_state["blockers"]:
|
|
745
|
+
readiness_issue(warnings, errors, strict, f"{plan_name}: {issue}")
|
|
746
|
+
if not legacy_state["passed"] and not legacy_state["issues"] and not legacy_state["blockers"]:
|
|
747
|
+
readiness_issue(
|
|
748
|
+
warnings,
|
|
749
|
+
errors,
|
|
750
|
+
strict,
|
|
751
|
+
f"{plan_name}: 旧格式当前阶段记录未满足统一单次复核准入",
|
|
752
|
+
)
|
|
546
753
|
|
|
547
754
|
|
|
548
755
|
def markdown_list_items(section):
|
|
@@ -1751,6 +1958,8 @@ def phase_gate(plan_map_text, plan_name, data, plan_text):
|
|
|
1751
1958
|
if review_state["enabled"]:
|
|
1752
1959
|
blockers.extend(review_state["blockers"])
|
|
1753
1960
|
else:
|
|
1961
|
+
legacy_state = legacy_review_state(plan_text, data["phase"])
|
|
1962
|
+
blockers.extend(legacy_state["blockers"])
|
|
1754
1963
|
review = key_value_table(fixed_section(plan_text, "最新独立准入复核"))
|
|
1755
1964
|
conclusion = review.get("结论", "").strip()
|
|
1756
1965
|
if review.get("阶段") == data["phase"] and conclusion.startswith(("未通过", "不通过", "失败", "不满足", "拒绝")):
|
|
@@ -1790,8 +1999,7 @@ def current_summary(plan_text):
|
|
|
1790
1999
|
|
|
1791
2000
|
|
|
1792
2001
|
def current_review_passes(plan_text, phase):
|
|
1793
|
-
|
|
1794
|
-
return review.get("阶段") == phase and review.get("结论", "").startswith("通过")
|
|
2002
|
+
return legacy_review_state(plan_text, phase)["passed"]
|
|
1795
2003
|
|
|
1796
2004
|
|
|
1797
2005
|
def structured_next_action(plan_text):
|
|
@@ -2155,11 +2363,14 @@ def workset_payload(root, include_history=False, strict=False):
|
|
|
2155
2363
|
"当前阶段复核声明无法确定"),
|
|
2156
2364
|
}
|
|
2157
2365
|
elif not current_review_passes(plan_text, data["phase"]):
|
|
2366
|
+
legacy_state = legacy_review_state(plan_text, data["phase"])
|
|
2367
|
+
kind = legacy_state["pending_action"] or "independent_review"
|
|
2158
2368
|
readiness = "design"
|
|
2159
2369
|
action = {
|
|
2160
2370
|
"state": "known",
|
|
2161
|
-
"kind":
|
|
2162
|
-
"reason": "
|
|
2371
|
+
"kind": kind,
|
|
2372
|
+
"reason": ("当前阶段待自验" if kind == "verify" else
|
|
2373
|
+
"当前阶段尚无可用的单次独立复核基线"),
|
|
2163
2374
|
}
|
|
2164
2375
|
else:
|
|
2165
2376
|
readiness = "design"
|
|
@@ -54,14 +54,14 @@ def init_git(root):
|
|
|
54
54
|
def agent_rules_body():
|
|
55
55
|
return """## 计划治理
|
|
56
56
|
|
|
57
|
-
|
|
57
|
+
本项目使用轻量计划治理。处理已有计划或需要持续协调的工作时,读取实际发现的 `plan-governance` skill;开发或审查该 skill 自身时使用仓库源及对应 CLI。规范来源一次选定后复用,按当前动作读取同源 references。
|
|
58
58
|
|
|
59
59
|
只有 CLI 可用时,运行 `plan-governance-cli guide` 读取入口,再按需运行 `guide planning`、`guide verification` 或 `guide cli`。两者均不可用或所需能力缺失时说明前提,不自动安装,也不猜测/拼接不同版本的规则。
|
|
60
60
|
|
|
61
61
|
最小执行边界:
|
|
62
62
|
|
|
63
|
-
-
|
|
64
|
-
- 只实施已授权、已准入的当前阶段。每个阶段有自身 Step 0
|
|
63
|
+
- 普通一次性任务直接处理;已有计划按共享 verification 验证。新旧计划均为普通改动自验,高风险或高影响同范围独立复核一次,修复后自验。
|
|
64
|
+
- 只实施已授权、已准入的当前阶段。每个阶段有自身 Step 0、验证/完成和失败边界;未解决问题不能记为完成,不可用或超时不能冒充已检查。已有授权不重复索取,新增高影响外部动作按实际授权处理。
|
|
65
65
|
- 首次读取 `docs/PLAN_MAP.md`、当前相关 `docs/plans/*.md` 及适用契约/ADR/migration;恢复先核对当前工作集、最近证据和实际 diff,缺失或冲突再展开。
|
|
66
66
|
- 地图维护状态、阶段、关系、阻塞与证据入口;计划记录本次差异及验证,现行契约优先复用 Schema/OpenAPI。新事实不写回历史草案,变化同步地图及相关引用。
|
|
67
67
|
- 完成时按共享规范记录实际验证与适用用户验收,运行 `plan-governance-cli check .`;准入/CI/发布显式使用 `--strict-readiness`,机械通过不等于业务验收。
|