plan-governance-cli 2.0.1 → 2.0.2

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 CHANGED
@@ -209,9 +209,9 @@ plan-governance-cli graph code impact --repo modelpad --file Sources/ModelPadCor
209
209
 
210
210
  ## 持续推进
211
211
 
212
- 在已授权的跨轮任务中可使用 `goal`;skill 本身不定义整计划自主执行运行时。普通改动自验,高风险或高影响同范围只独立复核一次,修复后自验;旧策略名和旧记录格式采用相同行为并保留历史,无需逐计划迁移。
212
+ 在已授权的跨轮任务中可使用 `goal`;skill 本身不定义整计划自主执行运行时。所有风险级别默认自验,只有用户明确要求时才独立复核;旧策略名和旧记录格式采用相同行为并保留历史,无需逐计划迁移。
213
213
 
214
- [验证规范](resources/skill/references/verification.md)统一维护单次复核、任务复用、证据复用、修复自验与用户验收;其他会话从同一 skill 读取。需要独立复核且授权明确时自动安排只读 subagent,复用已有同范围任务/结果,等待异常先核准任务 ID 和状态,且复核者不得再委派复核;不为普通小改逐次派发。技术完成待用户验收的适用功能计划仍保持实施中。
214
+ [验证规范](resources/skill/references/verification.md)统一维护按需复核、任务复用、证据复用、修复自验与用户验收;其他会话从同一 skill 读取。用户明确要求独立复核时安排只读 subagent,复用已有同范围任务/结果,等待异常先核准任务 ID 和状态,且复核者不得再委派复核。技术完成待用户验收的适用功能计划仍保持实施中。
215
215
 
216
216
  恢复已有计划时可读取派生工作集:
217
217
 
@@ -385,9 +385,9 @@ python3 -m pytest
385
385
 
386
386
  状态与当前阶段由 `docs/PLAN_MAP.md` 维护。每阶段必须具备自身 Step 0、验证/完成和失败边界、无有效阻塞及适用复核,不能凭上一阶段完成自动待实施。
387
387
 
388
- 新旧计划统一采用单次复核行为:普通改动自验,高风险或高影响独立检查一次后由实施者修复自验,原发现保留。旧 `风险分流` 标签和缺省六列格式只作为兼容记录形式,不恢复多次独立门禁;固定字段、历史保护和严格检查语义以[阶段门规范](resources/skill/references/verification.md#step-0-与阶段门)为准。
388
+ 新旧计划统一默认自验:高风险或高影响变更仍按实际后果验证,只有用户明确要求才独立复核,原发现保留。旧 `风险分流`、`单次独立复核` 标签和缺省六列格式只作为兼容记录形式;固定字段、历史保护和严格检查语义以[阶段门规范](resources/skill/references/verification.md#step-0-与阶段门)为准。
389
389
 
390
- 派发独立复核时显式传入推理强度,不依赖全局 subagent 默认值:安全、权限、隐私、资金、数据完整性等高风险复核使用 `high`,其他独立复核使用 `medium`。该分层只影响本次复核的思考强度,不改变是否复核或单次复核规则。
390
+ 收到用户明确请求并派发独立复核时显式传入推理强度:安全、权限、隐私、资金、数据完整性等高风险复核使用 `high`,其他独立复核使用 `medium`。推理强度不决定是否复核。
391
391
 
392
392
  使用 `check --strict-readiness` 做准入/CI/发布机械检查;默认检查兼容告警,机械通过不代替真实风险判断、业务验收或用户接受。
393
393
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "plan-governance-cli",
3
- "version": "2.0.1",
3
+ "version": "2.0.2",
4
4
  "description": "统一入口的计划治理检查 CLI",
5
5
  "repository": {
6
6
  "type": "git",
@@ -13,7 +13,7 @@ description: 维护已有项目计划,或为需要跨阶段、跨会话协调
13
13
 
14
14
  首次接手读取地图、当前相关计划及必要契约;恢复只核对当前阶段、实际差异和最近证据。只实施已授权且准入的范围;实质边界变化时调整受影响计划,日常进展随工作同步。事实源与格式见 planning。
15
15
 
16
- 按 verification 验证:普通改动自验,高风险或高影响的同一交付范围独立检查一次,修复由实施者自验;派发独立复核时显式传入推理强度,高风险用 `high`,其余用 `medium`。旧策略名和旧记录格式不改变此行为。适用检查通过后完成交付;未解决问题如实保留。交付说明完成结果、必要证据和剩余事项,明确约定的用户体验验收继续有效。
16
+ 按 verification 验证:所有风险级别默认由实施者自验,仅在用户明确要求时安排独立复核;派发时显式传入推理强度,高风险用 `high`,其余用 `medium`。旧策略名和旧记录格式不改变此行为。适用检查通过后完成交付;未解决问题如实保留。交付说明完成结果、必要证据和剩余事项,明确约定的用户体验验收继续有效。
17
17
 
18
18
  ## 按需读取
19
19
 
@@ -21,7 +21,7 @@
21
21
 
22
22
  - 当前阶段写细,后续阶段写粗。
23
23
  - 完成时必须记录可验证证据。
24
- - 按共享 skill 的 verification 规范判断风险:普通改动自验,高风险或高影响同范围独立复核一次,修复后自验;不按改动行数或阶段事件一概派发。
24
+ - 按共享 skill 的 verification 规范判断风险和验证范围;所有风险级别默认自验,仅在用户明确要求时独立复核,修复后记录适用验证。
25
25
 
26
26
  ## 影响模块或文件
27
27
 
@@ -47,10 +47,12 @@
47
47
  <!-- 标题必须保持为“阶段准入摘要”,不要改成“阶段 1 准入摘要”等带编号变体;阶段身份以 PLAN_MAP.md 为准。 -->
48
48
  ### 阶段准入摘要
49
49
 
50
+ <!-- 用户明确要求独立复核时,可在下表增加 | 独立复核请求 | [用户请求](#记录该请求的章节) |。 -->
51
+
50
52
  | 字段 | 内容 |
51
53
  |---|---|
52
54
  | 准入状态 | {{status}} |
53
- | 复核策略 | 单次独立复核 |
55
+ | 复核策略 | 默认自验 |
54
56
  | Step 0 | 当前阶段 Step 0 证据的链接或锚点 |
55
57
  | 样本矩阵 | 样本/fixture 或替代基线的链接或锚点 |
56
58
  | 验证方式 | 可执行命令和输出位置的链接或锚点 |
@@ -20,7 +20,7 @@
20
20
 
21
21
  `plan-governance-cli setup --target codex --dry-run` 预览主治理 skill 的同步差异,manifest 包含入口、按需参考、代理元数据和模板。迁移规则位于主 skill 的 `references/migration.md`,可通过 `plan-governance-cli guide migration` 按需读取;不会注册第二个 migration skill。`--destination DIR` 指定主 skill 目标目录。实际覆盖需既有用户授权并按 setup 冲突提示处理,不自动清理目标之外的用户目录。setup 只支持 Codex 单目标同步;项目初始化器只维护 `AGENTS.md`。项目升级不迁移旧计划,受管块外内容必须保持。
22
22
 
23
- 仅更新计划格式不等于更新项目入口。已有项目在任务交接、没有进行中的实施或复核时更新受管入口;升级不批量回写历史,旧策略名和六列格式按 verification 解释为统一单次行为。安装副本与仓库开发规则有差异时说明本次选用来源,不能因版本号相同就混读正文。
23
+ 仅更新计划格式不等于更新项目入口。已有项目在任务交接、没有进行中的实施或复核时更新受管入口;升级不批量回写历史,旧策略名和六列格式按 verification 解释为默认自验,保留真实历史发现。安装副本与仓库开发规则有差异时说明本次选用来源,不能因版本号相同就混读正文。
24
24
 
25
25
  ## 初始化、模板和检查脚本
26
26
 
@@ -48,9 +48,11 @@
48
48
 
49
49
  目标、授权、关键契约或高影响后果变化时,重新判断受影响范围的准入与验证;等待必要取舍期间,可继续无依赖的已授权工作。普通反馈、测试结果和状态更新在当前任务内处理并同步,不自动暂停、重开阶段或再索取授权。范围调整不能移走真实阻塞。
50
50
 
51
+ 发现异常时,先判断是否影响当前目标、验收或安全边界;影响则处理,关系不明则做有限核查,无关则记录后继续。不要因发现新问题自动扩大当前任务;调整既有验收条件时,须保留真实风险并同步阶段门。
52
+
51
53
  ## 执行与恢复
52
54
 
53
- 沿用已确认决定,只展开本次差异、证据缺口及直接依赖。跨阶段工作按 [阶段门](verification.md#step-0-与阶段门)判断,每阶段有自身基线与完成条件;上一阶段通过不自动放行下一阶段,但同一交付范围的独立检查可按 verification 显式复用而不重审。未来事项需注明承接范围与依赖,不以未准入的后续工作阻断无关的当前交付。
55
+ 沿用已确认决定,只展开本次差异、证据缺口及直接依赖。跨阶段工作按 [阶段门](verification.md#step-0-与阶段门)判断,每阶段有自身基线与完成条件;上一阶段通过不自动放行下一阶段,已有独立检查可按 verification 显式复用。未来事项需注明承接范围与依赖,不以未准入的后续工作阻断无关的当前交付。
54
56
 
55
57
  文档变更后做适用链接、结构和引用检查,运行所选版本的 `plan-governance-cli check .`;准入/发布使用 `--strict-readiness`。纯落档不重复代码测试。用户验收与证据真实性按 verification,不以文档格式通过替代。
56
58
 
@@ -8,21 +8,21 @@
8
8
 
9
9
  | 记录形式 | 解释 |
10
10
  |---|---|
11
- | 单次独立复核 | 当前模板标签 |
12
- | 风险分流 | 历史标签,按同一单次行为解释,不触发迁移或重审 |
11
+ | 默认自验 | 当前模板标签 |
12
+ | 单次独立复核、风险分流 | 历史标签,按默认自验解释,不触发迁移或重审 |
13
13
  | 无字段 | 保留六列历史格式,后续自验类型见旧计划兼容 |
14
14
 
15
- 显式字段的空值、未知值和重复声明不能放行。升级不批量改写计划;原检查按真实日期、结论、证据和身份保留,旧失败须由真实修复自验闭环,格式统一本身不解决问题。CLI 能力不符时说明缺口,更新按授权处理。
15
+ 显式字段的空值、未知值和重复声明不能放行。升级不批量改写计划;原检查按真实日期、结论、证据和身份保留,旧复核发现须由真实修复自验闭环,格式统一本身不解决问题。CLI 能力不符时说明缺口,更新按授权处理。
16
16
 
17
- ## 按风险复核
17
+ ## 自验与按需独立复核
18
18
 
19
- 以下方式适用于所有计划;普通无计划任务也按实际风险选择检查,不建立阶段表。
19
+ 以下方式适用于所有计划;普通无计划任务也按实际风险选择验证,不建立阶段表。
20
20
 
21
- 普通改动由实施者自验。改变权限、安全、隐私、资金、数据完整性、核心契约或产生广泛/难以撤销后果时,按实际高风险或高影响安排独立检查;不按目录、行数或阶段事件触发。风险不清先查事实。
21
+ 所有风险级别默认由实施者自验。改变权限、安全、隐私、资金、数据完整性、核心契约或产生广泛/难以撤销后果时,按实际风险扩大验证深度、受影响回归和失败边界;风险不清先查事实。风险、目录、行数、阶段事件或一般实施授权均不自动触发独立复核,也不反复征求用户是否复核。
22
22
 
23
- 同一交付范围只检查一次,通常在实现和自验收敛后、交付或高影响外部动作前;需要设计准入时将这一次前置,后续实施与完成自验。已审范围内的增量和修复合并处理,确有新增高风险范围才另行判断,不把每个修复拆成新范围;后续独立通过不能清除这一次检查留下的发现。
23
+ 用户明确要求独立复核时,按其指定范围安排。已有同范围结果可复用;已审范围内的增量和修复合并处理,不把每个修复拆成新范围。后续独立通过不能清除先前检查留下的真实发现。
24
24
 
25
- 授权明确时自动安排一名未参与实现或测试编写的只读复核者,提供范围、差异、风险和证据入口,并要求其自行完成、不得再委派复核。创建复核 subagent 时必须显式传入推理强度,不依赖全局默认:涉及安全、权限、隐私、资金、数据完整性或同类高风险后果时用 `high`,其他独立复核用 `medium`;推理强度不改变是否复核、复核次数或完成判定。派发前复用已有同范围任务或结果;等待失败先核对准确任务 ID 与实际状态,只有确认原任务不存在或不可恢复才补发。冻结受审范围并告知变化。独立者给出发现后,实施者修复并验证原问题及受影响回归,不再送审,未解决则保持未完成。检查不可用/超时直接记录状态,不能伪填已完成结论;业务缺陷描述中的“超时”不代表检查未完成。复核不代替外部授权。
25
+ 收到明确请求后,安排一名未参与实现或测试编写的只读复核者,提供范围、差异、风险和证据入口,并要求其自行完成、不得再委派复核。创建复核 subagent 时显式传入推理强度:涉及安全、权限、隐私、资金、数据完整性或同类高风险后果时用 `high`,其他独立复核用 `medium`。派发前复用已有同范围任务或结果;等待失败先核对准确任务 ID 与实际状态,只有确认原任务不存在或不可恢复才补发。冻结受审范围并告知变化。独立者给出发现后,实施者修复并验证原问题及受影响回归;未解决则保持未完成。检查不可用/超时如实记录,不伪填已完成结论;仅因历史检查不可用或超时而缺少独立基线时,可转为适用自验。复核不代替外部授权。
26
26
 
27
27
  ## Step 0 与阶段门
28
28
 
@@ -34,7 +34,7 @@ Step 0 可采用缺陷复现、迁移快照/兼容样本、新功能验收 fixtu
34
34
 
35
35
  验证用户可观察结果和受影响回归,完成项目要求的检查。纯文档做适用文档检查并注明覆盖率不适用。检查通过后,只有新变化、失败或未解疑点才扩大或重复验证。
36
36
 
37
- 复用证据须覆盖当前工作树、依赖/配置、环境和验收契约,内容可访问;仅 HEAD/版本相同不足以证明有效。同一交付范围的前置独立检查可跨阶段复用,但须在当前阶段以 `复核基线复用` 明确关联原记录、来源阶段和范围;未来阶段或未解决基线不可复用,新增范围重新判断风险。缺口补受影响验证,未解决冲突如实保留。只有状态、证据或链接同步时,不重新跑代码全套;无关任务失败如实报告,不擅自接管。
37
+ 复用证据须覆盖当前工作树、依赖/配置、环境和验收契约,内容可访问;仅 HEAD/版本相同不足以证明有效。已有独立检查若跨阶段复用,可在当前阶段以 `复核基线复用` 明确关联原记录、来源阶段和范围;未来阶段或未解决基线不可复用。缺口补受影响验证,未解决冲突如实保留。只有状态、证据或链接同步时,不重新跑代码全套;无关任务失败如实报告,不擅自接管。
38
38
 
39
39
  自验失败自主修复/复验。保留独立发现,按“发现 → 修复 → 验证”闭环并准确标记自验。机械检查只证明结构,不能证明风险、范围、独立性或证据真实性。可选 attestation 的显式检查见 [CLI](cli.md#初始化模板和检查脚本)。
40
40
 
@@ -62,10 +62,10 @@ Step 0 可采用缺陷复现、迁移快照/兼容样本、新功能验收 fixtu
62
62
 
63
63
  `阶段复核记录` 按 `日期、类型、阶段、方式、风险、结论、证据、复核者` 八列追加,日期顺序和最新表与当前阶段末条一致。完整结果须有有效日期、证据、身份及已判断风险;普通无计划任务无需建表。
64
64
 
65
- 高风险/高影响自验须有同范围完整独立基线。本阶段已有基线直接引用;复用前阶段基线时追加类型 `复核基线复用`、方式 `自验` 的当前阶段记录,证据须包含来源阶段并关联原记录和相同范围。独立发现修复后用类型 `修复自验`、方式 `自验` 追加,证据说明原发现与修复回归,不能以普通自验或第二次独立通过覆盖未修复发现。自验不伪称独立通过;上一阶段历史不自动阻断当前阶段。
65
+ 高风险/高影响自验无需独立基线,但须有覆盖实际风险的验证证据。已有跨阶段独立基线若选择复用,追加类型 `复核基线复用`、方式 `自验` 的当前阶段记录,证据须包含来源阶段并关联原记录和相同范围。独立发现修复后用类型 `修复自验`、方式 `自验` 追加,证据说明原发现与修复回归,不能以普通自验或第二次独立通过覆盖未修复发现。自验不伪称独立通过;上一阶段历史不自动阻断当前阶段。
66
66
 
67
- 结论以明确前缀记录:`通过`、`未通过`等;复核入口不可用/超时直接写其状态,实际查出的证据冲突可作为发现。通过说明不能覆盖结构化未解决事项。check 默认对新准入缺陷 WARNING,待实施/实施中在 `--strict-readiness` 下 ERROR。workset 保持 schema 1:待自验 `verify`、待独立 `independent_review`、通过待地图同步 `sync`、失败 `blocked/resolve_blocker`、歧义 `unknown/unknown`;不自动写状态,hook 只提示。
67
+ 结论以明确前缀记录:`通过`、`未通过`等;复核入口不可用/超时直接写其状态,实际查出的证据冲突可作为发现。通过说明不能覆盖结构化未解决事项。check 默认对新准入缺陷 WARNING,待实施/实施中在 `--strict-readiness` 下 ERROR。workset 保持 schema 1:默认待自验 `verify`;仅当前阶段摘要有 `独立复核请求` 且填入可追溯的用户明确请求证据时,待办为 `independent_review`,即使自验通过也要等已请求的独立检查完成,不能仅凭 `方式:独立` 或旧策略名推断请求;通过待地图同步 `sync`、失败 `blocked/resolve_blocker`、歧义 `unknown/unknown`。不自动写状态,hook 只提示。
68
68
 
69
69
  ### 旧计划兼容
70
70
 
71
- 没有策略字段时,保留固定标题 `阶段路线图`、`当前阶段`、`阶段准入摘要`、`最新独立准入复核`、`独立复核记录`,不为统一行为重写历史。摘要字段为准入状态、Step 0、样本矩阵、验证方式、失败/回滚边界、当前阻塞项及最新独立准入复核。最新表含日期、阶段、结论、证据、复核者;历史使用日期、类型、阶段、结论、证据、复核者六列。普通改动写类型 `普通自验`;独立发现后的闭环写 `修复自验`,且不能清除未完成或缺证据的检查;跨阶段复用写 `复核基线复用` 并关联原记录。其他历史类型按独立检查解释,最新表与当前阶段末条适用记录一致。标题不加阶段编号,阶段身份以地图为准。
71
+ 没有策略字段时,保留固定标题 `阶段路线图`、`当前阶段`、`阶段准入摘要`、`最新独立准入复核`、`独立复核记录`,不为统一行为重写历史。摘要字段为准入状态、Step 0、样本矩阵、验证方式、失败/回滚边界、当前阻塞项及最新独立准入复核;按需增加 `独立复核请求` 及用户请求证据链接。最新表含日期、阶段、结论、证据、复核者;历史使用日期、类型、阶段、结论、证据、复核者六列。自验写类型 `普通自验`;独立发现后的闭环写 `修复自验`,且不能清除真实未解决发现;跨阶段复用写 `复核基线复用` 并关联原记录。其他历史类型按独立检查解释,最新表与当前阶段末条适用记录一致。标题不加阶段编号,阶段身份以地图为准。
@@ -231,6 +231,17 @@ def legacy_review_method(kind):
231
231
  return "自验" if kind == "复核基线复用" or "自验" in kind else "独立"
232
232
 
233
233
 
234
+ def independent_review_requested(summary):
235
+ """只有明确的用户请求证据才将待办派给独立复核。"""
236
+ request = summary.get("独立复核请求", "").strip()
237
+ return bool(request and request != "无" and not is_placeholder(request))
238
+
239
+
240
+ def review_unavailable(conclusion):
241
+ """工具未完成不等于审出了实质问题。"""
242
+ return conclusion.strip().startswith(("超时", "复核工具超时", "复核入口不可用"))
243
+
244
+
234
245
  def phase_precedes(plan_text, candidate, current):
235
246
  """只允许路线图中更早的阶段;标准数字阶段可在精简路线图中回退比较。"""
236
247
  roadmap = [row[0] for row in phase_roadmap_rows(plan_text)]
@@ -243,7 +254,7 @@ def phase_precedes(plan_text, candidate, current):
243
254
 
244
255
 
245
256
  def legacy_review_state(plan_text, phase):
246
- """在不改写六列历史的前提下应用统一单次复核闭环。"""
257
+ """在不改写六列历史的前提下应用默认自验和真实发现闭环。"""
247
258
  result = {"passed": False, "pending_action": None, "issues": [], "blockers": []}
248
259
  issues, blockers = result["issues"], result["blockers"]
249
260
  rows = review_history_rows(plan_text)
@@ -299,7 +310,7 @@ def legacy_review_state(plan_text, phase):
299
310
  outcome = review_outcome(row[3])
300
311
  if method == "独立":
301
312
  state["reviewed"] = completed_independent(row)
302
- if outcome == "failed":
313
+ if outcome == "failed" and not review_unavailable(row[3]):
303
314
  state["unresolved"] = True
304
315
  elif (row[1] == "修复自验" and state["reviewed"] and outcome == "passed"
305
316
  and qualified(row)):
@@ -319,7 +330,7 @@ def legacy_review_state(plan_text, phase):
319
330
  issues.append("旧独立复核记录结论为空或未知")
320
331
  if method == "独立":
321
332
  reviewed = completed_independent(row)
322
- if outcome == "failed":
333
+ if outcome == "failed" and not review_unavailable(row[3]):
323
334
  unresolved = row[3]
324
335
  elif row[1] == "复核基线复用":
325
336
  referenced = [source for source in reusable_prior_phases if source in row[4]]
@@ -328,9 +339,7 @@ def legacy_review_state(plan_text, phase):
328
339
  elif outcome == "passed" and qualified(row):
329
340
  reviewed = True
330
341
  elif row[1] == "修复自验":
331
- if not reviewed:
332
- issues.append("旧格式修复自验缺少已完成的独立复核基线")
333
- elif outcome == "passed" and qualified(row):
342
+ if outcome == "passed" and qualified(row) and reviewed:
334
343
  unresolved = None
335
344
  if outcome == "passed" and not qualified(row):
336
345
  issues.append("旧独立复核记录通过缺少有效日期、类型、证据或身份")
@@ -346,15 +355,19 @@ def legacy_review_state(plan_text, phase):
346
355
  review.get(field) == current[-1][index]
347
356
  for field, index in [("日期", 0), ("阶段", 2), ("结论", 3), ("证据", 4), ("复核者", 5)]
348
357
  )
349
- result["passed"] = outcome == "passed" and latest_matches and not issues and not blockers
358
+ summary = key_value_table(fixed_section(top_level_section(plan_text, "当前阶段"), "阶段准入摘要"))
359
+ requested = independent_review_requested(summary)
360
+ awaiting_requested_review = requested and outcome == "passed" and not reviewed
361
+ result["passed"] = outcome == "passed" and latest_matches and not issues and not blockers and not awaiting_requested_review
350
362
  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"
363
+ result["pending_action"] = "independent_review" if requested else "verify"
364
+ elif awaiting_requested_review and not issues and not blockers:
365
+ result["pending_action"] = "independent_review"
353
366
  return result
354
367
 
355
368
 
356
369
  def risk_review_state(plan_text, phase):
357
- """显式风险策略的共享事实判定;历史策略名不再改变单次复核行为。"""
370
+ """显式风险策略的共享事实判定;历史策略名不改变默认自验行为。"""
358
371
  summary_section = fixed_section(top_level_section(plan_text, "当前阶段"), "阶段准入摘要")
359
372
  policies = [row for row in markdown_table_rows(summary_section) if row[0] == "复核策略"]
360
373
  result = {"enabled": bool(policies), "passed": False, "pending_action": None,
@@ -362,8 +375,8 @@ def risk_review_state(plan_text, phase):
362
375
  if not policies:
363
376
  return result
364
377
  issues, blockers = result["issues"], result["blockers"]
365
- if len(policies) != 1 or len(policies[0]) != 2 or policies[0][1] not in {"风险分流", "单次独立复核"}:
366
- issues.append("复核策略必须唯一且为 风险分流/单次独立复核;空值、未知值或重复声明不能准入")
378
+ if len(policies) != 1 or len(policies[0]) != 2 or policies[0][1] not in {"风险分流", "单次独立复核", "默认自验"}:
379
+ issues.append("复核策略必须唯一且为 默认自验/风险分流/单次独立复核;空值、未知值或重复声明不能准入")
367
380
  risks = {"低风险", "高影响", "高风险"}
368
381
  summary = key_value_table(summary_section)
369
382
  if "最新独立准入复核" in summary:
@@ -464,7 +477,7 @@ def risk_review_state(plan_text, phase):
464
477
  else:
465
478
  state["reviewed_high"] = False
466
479
  row_outcome = review_outcome(row[5])
467
- if row_outcome == "failed":
480
+ if row_outcome == "failed" and not review_unavailable(row[5]):
468
481
  state["unresolved"] = True
469
482
  elif (row[1] == "修复自验" and state["reviewed_high"]
470
483
  and review_outcome(row[5]) == "passed"
@@ -489,7 +502,7 @@ def risk_review_state(plan_text, phase):
489
502
  reviewed_high = row[4] in {"高风险", "高影响"}
490
503
  else:
491
504
  reviewed = reviewed_high = False
492
- if review_outcome(row[5]) == "failed":
505
+ if review_outcome(row[5]) == "failed" and not review_unavailable(row[5]):
493
506
  unresolved_single = row[5]
494
507
  elif review_outcome(row[5]) == "passed" and completed(row):
495
508
  # 后续独立通过可形成新范围基线,但不能清除既有独立发现。
@@ -503,12 +516,8 @@ def risk_review_state(plan_text, phase):
503
516
  and not any(is_placeholder(value) for value in row)
504
517
  and valid_date(row[0]) is not None):
505
518
  reviewed = reviewed_high = True
506
- elif row[4] in {"高风险", "高影响"} and not reviewed_high:
507
- issues.append("高风险/高影响自验缺少同范围独立复核基线或明确的跨阶段复用记录")
508
519
  if row[1] == "修复自验":
509
- if not reviewed:
510
- issues.append("修复自验缺少已完成的独立复核基线")
511
- elif (review_outcome(row[5]) == "passed" and row[4] in risks
520
+ if (reviewed and review_outcome(row[5]) == "passed" and row[4] in risks
512
521
  and not any(is_placeholder(value) for value in row)
513
522
  and valid_date(row[0]) is not None):
514
523
  unresolved_single = None
@@ -560,7 +569,7 @@ def risk_review_state(plan_text, phase):
560
569
  row_outcome = review_outcome(row[3])
561
570
  if not qualified(row, legacy=True):
562
571
  issues.append("旧独立复核记录缺少有效日期、类型、证据或身份,迁移不能掩盖")
563
- if row_outcome == "failed":
572
+ if row_outcome == "failed" and not review_unavailable(row[3]):
564
573
  unresolved = row[3]
565
574
  elif row_outcome == "passed" and qualified(row, legacy=True):
566
575
  pass
@@ -581,9 +590,13 @@ def risk_review_state(plan_text, phase):
581
590
  elif not legacy_history or any(is_placeholder(legacy.get(field))
582
591
  for field in ["日期", "阶段", "结论", "证据", "复核者"]) or valid_date(legacy.get("日期", "")) is None:
583
592
  issues.append("旧最新独立准入复核缺少有效字段或当前阶段历史,不能降级放行")
584
- result["passed"] = outcome == "passed" and not issues and not blockers
593
+ requested = independent_review_requested(summary)
594
+ awaiting_requested_review = requested and outcome == "passed" and not reviewed
595
+ result["passed"] = outcome == "passed" and not issues and not blockers and not awaiting_requested_review
585
596
  if outcome == "pending" and not issues and not blockers:
586
- result["pending_action"] = "verify" if review.get("方式") == "自验" else "independent_review"
597
+ result["pending_action"] = "independent_review" if requested else "verify"
598
+ elif awaiting_requested_review and not issues and not blockers:
599
+ result["pending_action"] = "independent_review"
587
600
  return result
588
601
 
589
602
 
@@ -748,7 +761,7 @@ def check_phase_structure(plan_name, data, plan_text, strict, warnings, errors,
748
761
  warnings,
749
762
  errors,
750
763
  strict,
751
- f"{plan_name}: 旧格式当前阶段记录未满足统一单次复核准入",
764
+ f"{plan_name}: 旧格式当前阶段记录未满足自验或已请求复核的准入",
752
765
  )
753
766
 
754
767
 
@@ -2406,19 +2419,19 @@ def workset_payload(root, include_history=False, strict=False):
2406
2419
  "state": "known" if kind else "unknown",
2407
2420
  "kind": kind or "unknown",
2408
2421
  "reason": ("当前阶段复核通过,待同步 PLAN_MAP 准入状态" if kind == "sync" else
2409
- "低风险阶段待自验" if kind == "verify" else
2422
+ "当前阶段待自验" if kind == "verify" else
2410
2423
  "当前阶段待独立复核" if kind == "independent_review" else
2411
2424
  "当前阶段复核声明无法确定"),
2412
2425
  }
2413
2426
  elif not current_review_passes(plan_text, data["phase"]):
2414
2427
  legacy_state = legacy_review_state(plan_text, data["phase"])
2415
- kind = legacy_state["pending_action"] or "independent_review"
2428
+ kind = legacy_state["pending_action"] or "verify"
2416
2429
  readiness = "design"
2417
2430
  action = {
2418
2431
  "state": "known",
2419
2432
  "kind": kind,
2420
2433
  "reason": ("当前阶段待自验" if kind == "verify" else
2421
- "当前阶段尚无可用的单次独立复核基线"),
2434
+ "用户已明确要求独立复核"),
2422
2435
  }
2423
2436
  else:
2424
2437
  readiness = "design"
@@ -58,7 +58,7 @@ def agent_rules_body():
58
58
 
59
59
  最小执行边界:
60
60
 
61
- - 普通一次性任务直接处理;已有计划按共享 verification 验证。新旧计划均为普通改动自验,高风险或高影响同范围独立复核一次,修复后自验。
61
+ - 普通一次性任务直接处理;已有计划按共享 verification 验证。新旧计划均默认自验,仅在用户明确要求时独立复核;未解决问题继续阻塞。
62
62
  - 只实施已授权、已准入的当前阶段。每个阶段有自身 Step 0、验证/完成和失败边界;未解决问题不能记为完成,不可用或超时不能冒充已检查。已有授权不重复索取,新增高影响外部动作按实际授权处理。
63
63
  - 首次读取 `docs/PLAN_MAP.md`、索引登记的相关 `docs/plans/` 计划及适用契约/ADR/migration;恢复先核对当前工作集、最近证据和实际 diff,缺失或冲突再展开。
64
64
  - 地图维护状态、阶段、关系、阻塞与证据入口;计划记录本次差异及验证,现行契约优先复用 Schema/OpenAPI。新事实不写回历史草案,变化同步地图及相关引用。