plan-governance-cli 2.0.1 → 3.0.0

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
@@ -39,7 +39,7 @@ docs/
39
39
  <plan-name>.md
40
40
  ```
41
41
 
42
- `docs/plans/*.md` 是兼容读取的旧式平铺位置;新计划一律放在日期目录中。存量计划只有在用户明确要求时才迁移;迁移规则由 `plan-governance` 按需读取,不单独注册 migration skill。
42
+ `docs/plans/*.md` 是兼容读取的旧式平铺位置;新计划一律放在日期目录中,初始化仅接受 候选/设计中,写入前预检可预测冲突。存量计划只有在用户明确要求时才迁移;迁移规则由 `plan-governance` 按需读取,不单独注册 migration skill。
43
43
 
44
44
  按需增加:
45
45
 
@@ -175,6 +175,18 @@ plan-governance-cli init \
175
175
  plan-governance-cli check .
176
176
  ```
177
177
 
178
+ 普通 check 的业务阻塞/失败/准入材料缺口为 WARNING,文档没有硬错误时退出 0;严格 check 保留准入阻断。已完成但缺证据或仍有当前阻塞、无效身份/日期/路径、必要输入读取失败等始终退出 1。合法未初始化目录为 not_applicable/0,严格为 1;参数错误为 2。
179
+
180
+ 多计划、多会话通过显式参数聚焦,焦点不写入全项目状态:
181
+
182
+ ```bash
183
+ plan-governance-cli workset . --json --plan api-compat-migration
184
+ plan-governance-cli check . --json --plan api-compat-migration
185
+ plan-governance-cli check . --strict-readiness --plan api-compat-migration
186
+ ```
187
+
188
+ `--plan` 可重复,只接受唯一登记 ID。聚焦结果只带请求计划、必要前置与相关证据/共享写入约束;无关计划的局部阻塞不会导致失败。workset 保留 schema 1,新增 scope;check JSON 提供 mode/status/exit_code/scope/summary/diagnostics,stdout 不混日志。相关事实改变时工作集为 unknown,严格失败;无关地图更新不使聚焦版本失效。AI 必须在调用中传焦点,CLI 无法自动识别聊天任务。完整接口和输出约定见 [CLI 参考](resources/skill/references/cli.md)。
189
+
178
190
  可选检查:
179
191
 
180
192
  ```bash
@@ -209,9 +221,9 @@ plan-governance-cli graph code impact --repo modelpad --file Sources/ModelPadCor
209
221
 
210
222
  ## 持续推进
211
223
 
212
- 在已授权的跨轮任务中可使用 `goal`;skill 本身不定义整计划自主执行运行时。普通改动自验,高风险或高影响同范围只独立复核一次,修复后自验;旧策略名和旧记录格式采用相同行为并保留历史,无需逐计划迁移。
224
+ 在已授权的跨轮任务中可使用 `goal`;skill 本身不定义整计划自主执行运行时。所有风险级别默认自验,只有用户明确要求时才独立复核;旧策略名和旧记录格式采用相同行为并保留历史,无需逐计划迁移。
213
225
 
214
- [验证规范](resources/skill/references/verification.md)统一维护单次复核、任务复用、证据复用、修复自验与用户验收;其他会话从同一 skill 读取。需要独立复核且授权明确时自动安排只读 subagent,复用已有同范围任务/结果,等待异常先核准任务 ID 和状态,且复核者不得再委派复核;不为普通小改逐次派发。技术完成待用户验收的适用功能计划仍保持实施中。
226
+ [验证规范](resources/skill/references/verification.md)统一维护按需复核、任务复用、证据复用、修复自验与用户验收;其他会话从同一 skill 读取。用户明确要求独立复核时安排只读 subagent,复用已有同范围任务/结果,等待异常先核准任务 ID 和状态,且复核者不得再委派复核。技术完成待用户验收的适用功能计划仍保持实施中。
215
227
 
216
228
  恢复已有计划时可读取派生工作集:
217
229
 
@@ -289,10 +301,10 @@ plan-governance-cli hook --event stop
289
301
 
290
302
  事件语义:
291
303
 
292
- - `session-start`:摘要活跃计划、当前阶段、最后更新、阻塞项和证据链接。
293
- - `pre-write`:按活跃计划的 `影响模块或文件` 匹配路径,提示当前阶段门禁。
304
+ - `session-start`:未设焦点时列轻量索引;传入 `--plan ID` 时展示聚焦工作集。
305
+ - `pre-write`:有焦点时给自己的门禁和共享约束;路径匹配只提供候选,不自动选计划。
294
306
  - `post-write`:写入 `PLAN_MAP.md` 或计划文档后,提示同步状态、证据、覆盖率和反向引用检查。
295
- - `stop`:运行 `plan-governance-cli check .` 并转发结果;该事件是非阻塞提示,不实现强制 gate。
307
+ - `stop`:使用同包检查器按焦点检查并转发结果;该事件是非阻塞提示,不实现强制 gate。
296
308
 
297
309
  旧项目如果仍使用五列 `PLAN_MAP.md`,先显式迁移:
298
310
 
@@ -385,9 +397,9 @@ python3 -m pytest
385
397
 
386
398
  状态与当前阶段由 `docs/PLAN_MAP.md` 维护。每阶段必须具备自身 Step 0、验证/完成和失败边界、无有效阻塞及适用复核,不能凭上一阶段完成自动待实施。
387
399
 
388
- 新旧计划统一采用单次复核行为:普通改动自验,高风险或高影响独立检查一次后由实施者修复自验,原发现保留。旧 `风险分流` 标签和缺省六列格式只作为兼容记录形式,不恢复多次独立门禁;固定字段、历史保护和严格检查语义以[阶段门规范](resources/skill/references/verification.md#step-0-与阶段门)为准。
400
+ 新旧计划统一默认自验:高风险或高影响变更仍按实际后果验证,只有用户明确要求才独立复核,原发现保留。旧 `风险分流`、`单次独立复核` 标签和缺省六列格式只作为兼容记录形式;固定字段、历史保护和严格检查语义以[阶段门规范](resources/skill/references/verification.md#step-0-与阶段门)为准。
389
401
 
390
- 派发独立复核时显式传入推理强度,不依赖全局 subagent 默认值:安全、权限、隐私、资金、数据完整性等高风险复核使用 `high`,其他独立复核使用 `medium`。该分层只影响本次复核的思考强度,不改变是否复核或单次复核规则。
402
+ 收到用户明确请求并派发独立复核时显式传入推理强度:安全、权限、隐私、资金、数据完整性等高风险复核使用 `high`,其他独立复核使用 `medium`。推理强度不决定是否复核。
391
403
 
392
404
  使用 `check --strict-readiness` 做准入/CI/发布机械检查;默认检查兼容告警,机械通过不代替真实风险判断、业务验收或用户接受。
393
405
 
@@ -7,9 +7,10 @@ import {
7
7
  mkdirSync,
8
8
  readFileSync,
9
9
  renameSync,
10
+ statSync,
10
11
  writeFileSync,
11
12
  } from "node:fs";
12
- import { homedir, tmpdir } from "node:os";
13
+ import { homedir } from "node:os";
13
14
  import { basename, dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
14
15
  import { spawnSync } from "node:child_process";
15
16
  import { fileURLToPath } from "node:url";
@@ -21,11 +22,13 @@ const hookRuntime = resolve(packageRoot, "scripts", "plan_governance_hook.py");
21
22
  const graphRuntime = resolve(packageRoot, "scripts", "graph_governance.mjs");
22
23
  const manifestPath = resolve(packageRoot, "resources", "manifest.json");
23
24
 
24
- function fail(message) {
25
+ function fail(message, status = 1) {
25
26
  console.error(`plan-governance-cli: ${message}`);
26
- return 1;
27
+ return status;
27
28
  }
28
29
 
30
+ class UsageError extends Error { code = 2; }
31
+
29
32
  function runPython(script, args) {
30
33
  try {
31
34
  accessSync(script, constants.R_OK);
@@ -89,28 +92,34 @@ function parseSetupArgs(args) {
89
92
  const arg = args[index];
90
93
  if (arg === "--target") {
91
94
  if (options.target !== null) {
92
- throw new Error("setup 的 --target 只能指定一次");
95
+ throw new UsageError("setup 的 --target 只能指定一次");
93
96
  }
94
97
  const value = args[++index];
95
- if (!value || value.startsWith("-")) {
96
- throw new Error("setup 的 --target 缺少值");
98
+ if (!value?.trim() || value.startsWith("-")) {
99
+ throw new UsageError("setup 的 --target 缺少值");
97
100
  }
98
101
  options.target = value;
99
102
  } else if (arg === "--destination") {
100
- options.destination = args[++index];
103
+ if (options.destination !== null) throw new UsageError("setup 的 --destination 只能指定一次");
104
+ const value = args[++index];
105
+ if (!value?.trim() || value.startsWith("-")) throw new UsageError("setup 的 --destination 缺少路径");
106
+ options.destination = value;
101
107
  } else if (arg === "--dry-run") {
102
108
  options.dryRun = true;
103
109
  } else if (arg === "--force") {
104
110
  options.force = true;
105
111
  } else if (arg === "-h" || arg === "--help") {
106
- throw new Error("setup 的 --help 必须单独使用");
112
+ throw new UsageError("setup 的 --help 必须单独使用");
107
113
  } else {
108
- throw new Error(`setup 不支持参数:${arg}`);
114
+ throw new UsageError(`setup 不支持参数:${arg}`);
115
+ }
116
+ if (["--dry-run", "--force"].includes(arg) && args.indexOf(arg) !== index) {
117
+ throw new UsageError(`setup 的 ${arg} 只能指定一次`);
109
118
  }
110
119
  }
111
120
 
112
121
  if (options.target !== "codex") {
113
- throw new Error("setup 仅支持 --target codex");
122
+ throw new UsageError("setup 仅支持 --target codex");
114
123
  }
115
124
  return options;
116
125
  }
@@ -195,6 +204,10 @@ function setup(args) {
195
204
  for (const file of resourceFiles(skill.definition, skill.prefix)) {
196
205
  accessSync(file.source, constants.R_OK);
197
206
  const destination = resolve(target.root, file.relative);
207
+ for (let parent = dirname(destination); ; parent = dirname(parent)) {
208
+ if (existsSync(parent) && !statSync(parent).isDirectory()) throw new Error(`目标父路径不是目录:${parent}`);
209
+ if (dirname(parent) === parent) break;
210
+ }
198
211
  const existing = existsSync(destination) ? readFileSync(destination, "utf8") : null;
199
212
  const sourceContent = readFileSync(file.source, "utf8");
200
213
  if (existing !== null && existing !== sourceContent && !options.force) {
@@ -207,18 +220,26 @@ function setup(args) {
207
220
  return fail(error.message);
208
221
  }
209
222
 
210
- for (const plan of plans) {
223
+ const written = [];
224
+ try { for (const plan of plans) {
211
225
  if (plan.existing === plan.sourceContent) {
212
226
  console.log(`已是最新:${plan.destination}`);
213
227
  } else if (options.dryRun) {
214
228
  console.log(`将写入:${plan.destination}`);
215
229
  } else {
216
230
  mkdirSync(dirname(plan.destination), { recursive: true });
217
- const temporary = join(tmpdir(), `plan-governance-${process.pid}-${Date.now()}-${Math.random().toString(16).slice(2)}`);
218
- writeFileSync(temporary, plan.sourceContent, "utf8");
219
- renameSync(temporary, plan.destination);
231
+ if (plan.existing !== null) {
232
+ writeFileSync(plan.destination, plan.sourceContent, "utf8");
233
+ } else {
234
+ const temporary = join(dirname(plan.destination), `.plan-governance-${process.pid}-${Math.random().toString(16).slice(2)}`);
235
+ writeFileSync(temporary, plan.sourceContent, "utf8");
236
+ renameSync(temporary, plan.destination);
237
+ }
238
+ written.push(plan.destination);
220
239
  console.log(`已同步:${plan.destination}`);
221
240
  }
241
+ } } catch (error) {
242
+ return fail(`同步失败:${error.message};已写入:${written.join("、") || "无"};未完成,请按实际文件恢复或重试。`);
222
243
  }
223
244
  if (options.dryRun) console.log("setup dry-run 完成,未修改目标目录。");
224
245
  return 0;
@@ -232,13 +253,13 @@ function guide(args) {
232
253
  ["verification", "references/verification.md"],
233
254
  ["cli", "references/cli.md"],
234
255
  ]);
235
- if (args.length === 1 && args[0] === "--help") {
256
+ if (args.length === 1 && ["-h", "--help"].includes(args[0])) {
236
257
  console.log("用法:plan-governance-cli guide [overview|planning|migration|verification|cli]\n缺省 overview;只读当前包内规则,不依赖 cwd 或 Python。");
237
258
  return 0;
238
259
  }
239
260
  const topic = args[0] ?? "overview";
240
261
  if (args.length > 1 || !topics.has(topic)) {
241
- return fail("guide 仅接受一个主题:overview、planning、migration、verification、cli;用 guide --help 查看用法。");
262
+ return fail("guide 仅接受一个主题:overview、planning、migration、verification、cli;用 guide --help 查看用法。", 2);
242
263
  }
243
264
  const path = resolve(packageRoot, "resources", "skill", topics.get(topic));
244
265
  try {
@@ -254,6 +275,19 @@ function guide(args) {
254
275
  function main() {
255
276
  const args = process.argv.slice(2);
256
277
  const command = args[0];
278
+ if (["-h", "--help"].includes(command) && args.length === 1) {
279
+ console.log(`用法:plan-governance-cli <command> [options]
280
+ check [root] [--plan ID] [--json] [--strict-readiness]
281
+ workset [root] [--plan ID] [--json] [--evidence-limit N]
282
+ guide [overview|planning|migration|verification|cli]
283
+ setup --target codex [--destination DIR] [--dry-run] [--force]
284
+ init --root DIR --plan ID
285
+ hook --root DIR --event EVENT [--plan ID] [--paths PATH ...]
286
+ graph validate|impact|code [options]
287
+ plan impact --input FILE [root]
288
+ 各命令 --help 查看详细用法;退出码 0 成功、1 检查/运行失败、2 参数错误。`);
289
+ return 0;
290
+ }
257
291
  if (command === "guide") return guide(args.slice(1));
258
292
  if (command === "setup") return setup(args.slice(1));
259
293
  if (command === "workset") return runPython(checker, ["--workset", ...args.slice(1)]);
@@ -261,18 +295,22 @@ function main() {
261
295
  if (command === "hook") return runPython(hookRuntime, args.slice(1));
262
296
  if (command === "graph") return runNode(graphRuntime, args.slice(1));
263
297
  if (command === "plan" && args[1] === "next") {
264
- return fail("plan next 已移除;在 Codex 中请使用 goal 管理跨轮持续推进。");
298
+ return fail("plan next 已移除;在 Codex 中请使用 goal 管理跨轮持续推进。", 2);
265
299
  }
266
300
  if (command === "plan" && args[1] === "steps" && args[2] === "validate") {
267
- return fail("自主执行步骤校验已移除;在 Codex 中请使用 goal 管理跨轮持续推进。");
301
+ return fail("自主执行步骤校验已移除;在 Codex 中请使用 goal 管理跨轮持续推进。", 2);
268
302
  }
269
303
  if (command === "plan") return runNode(graphRuntime, args);
270
304
  if (command === "check") return runPython(checker, args.slice(1));
271
- return runPython(checker, args);
305
+ if (!command || command.startsWith("-") || command === "." || command.startsWith("./") || command.startsWith("../") || isAbsolute(command)) {
306
+ console.error("plan-governance-cli: 旧式直接检查入口已弃用,请显式使用 check。");
307
+ return runPython(checker, args);
308
+ }
309
+ return fail(`未知命令:${command};请使用 --help 查看命令。`, 2);
272
310
  }
273
311
 
274
312
  try {
275
313
  process.exitCode = main();
276
314
  } catch (error) {
277
- process.exitCode = fail(error.message);
315
+ process.exitCode = fail(error.message, error.code === 2 ? 2 : 1);
278
316
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "plan-governance-cli",
3
- "version": "2.0.1",
3
+ "version": "3.0.0",
4
4
  "description": "统一入口的计划治理检查 CLI",
5
5
  "repository": {
6
6
  "type": "git",
@@ -11,9 +11,11 @@ description: 维护已有项目计划,或为需要跨阶段、跨会话协调
11
11
 
12
12
  根据当前请求、上下文和已有授权持续推进。用户指令优先于本 skill 的流程建议;保留用户明确的实现、外部操作和验收边界。能自行查证或作可逆决定的事项自主处理;只有缺失信息会实质改变结果或超出授权时才询问,并继续不依赖该答案的工作。
13
13
 
14
+ 多计划/多会话并行时,从当前任务确定本会话的已登记计划 ID,在 check/workset/hook 中显式传入同一组 `--plan ID`;不使用全项目可变焦点,不把其他计划的阻塞算作自己的门禁。必要前置、证据引用和共享写入约束仍按 cli 读取;写入前重读相关事实并协调共享文件。焦点不明确时先读轻量索引,多个候选不能自动接管。
15
+
14
16
  首次接手读取地图、当前相关计划及必要契约;恢复只核对当前阶段、实际差异和最近证据。只实施已授权且准入的范围;实质边界变化时调整受影响计划,日常进展随工作同步。事实源与格式见 planning。
15
17
 
16
- 按 verification 验证:普通改动自验,高风险或高影响的同一交付范围独立检查一次,修复由实施者自验;派发独立复核时显式传入推理强度,高风险用 `high`,其余用 `medium`。旧策略名和旧记录格式不改变此行为。适用检查通过后完成交付;未解决问题如实保留。交付说明完成结果、必要证据和剩余事项,明确约定的用户体验验收继续有效。
18
+ 按 verification 验证:所有风险级别默认由实施者自验,仅在用户明确要求时安排独立复核;派发时显式传入推理强度,高风险用 `high`,其余用 `medium`。旧策略名和旧记录格式不改变此行为。适用检查通过后完成交付;未解决问题如实保留。交付说明完成结果、必要证据和剩余事项,明确约定的用户体验验收继续有效。
17
19
 
18
20
  ## 按需读取
19
21
 
@@ -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
  | 验证方式 | 可执行命令和输出位置的链接或锚点 |
@@ -2,25 +2,29 @@
2
2
 
3
3
  只读规则入口:`plan-governance-cli guide [overview|planning|migration|verification|cli]`。缺省 overview;`guide --help` 列用法。五主题从同一包根读取本入口及参考,任意 cwd 可用且无需 Python。正文中的相对文档链接在 CLI 环境通过对应 guide 主题读取,不相对当前项目查找。
4
4
 
5
- 成功退出 0;未知主题、多余参数、缺失/不可读/空资源退出 1 并报错。不下载、安装、修改项目或回退别的版本。skill/CLI 来源按入口一次选定:开发本 skill 用仓库源与对应 CLI,其他项目用安装资源。出现内容变化或能力缺口时再核对,版本号相同不证明资源一致。
5
+ 成功退出 0;未知主题、多余参数退出 2;缺失/不可读/空资源退出 1 并报错。不下载、安装、修改项目或回退别的版本。skill/CLI 来源按入口一次选定:开发本 skill 用仓库源与对应 CLI,其他项目用安装资源。出现内容变化或能力缺口时再核对,版本号相同不证明资源一致。
6
6
 
7
7
  已安装 skill 不等于 CLI 存在;既无 skill 也无 CLI 时说明治理前提缺失,仅阻塞依赖治理规则的工作,不自动安装。CLI 不支持新策略或 guide 时明确能力缺失,按授权更新。仅需阅读规则不调用 init/setup。
8
8
 
9
9
  ## 当前工作集
10
10
 
11
- `plan-governance-cli workset . --json` 派生当前计划、阶段、阻塞、下一动作、并行提示和最近证据;加 `--strict-readiness` 对活跃计划的机械缺陷返回非零。它不写计划、不自动执行动作、不替代授权或业务验收,也不自动核对快照和环境漂移。恢复先读工作集与相关当前记录,必要时再展开原计划。
11
+ `plan-governance-cli workset . --json --plan <ID>` 派生会话聚焦计划及必要前置/共享约束;未传 plan 时派生当前计划、阶段、阻塞、下一动作、并行提示和最近证据;加 `--strict-readiness` 对活跃计划的机械缺陷返回非零。它不写计划、不自动执行动作、不替代授权或业务验收,也不自动核对快照和环境漂移。恢复先读工作集与相关当前记录,必要时再展开原计划。
12
12
 
13
13
  1.0.1 起,恢复阅读可显式用 `workset . --json --evidence-limit 3`,按原追加顺序显示每个计划末尾三条记录。N 必须为正整数且仅适用于 workset JSON;默认仍返回完整记录。限量模式的 `recent_evidence_window` 提供 `total`、`omitted` 和原文 `source`(实际 `path`/`section`;无法确认时为 null),不改变完整数据派生的阻塞、动作、诊断或退出码。没有记录章节时 section 为 null。
14
14
 
15
15
  限量窗口不等于完整验收证据;有阻塞、缺关键上下文或需要复核结论时,按 source 回查,或去掉 `--evidence-limit` 读取完整记录。`--include-history` 仍只控制是否纳入历史计划。普通文本不显示记录表,不需要此参数;旧 CLI(包括 1.0.0)不支持限量时,继续用完整输出,按已获授权更新版本。
16
16
 
17
- 面向当前任务读取时,可在工具内解析完整 JSON 后只展示目标计划的状态、阶段、下一动作、有效阻塞和少量证据。跨计划检查仍保留直接依赖和不能归属的诊断;展示限量不裁剪底层门禁或清除阻塞。先选取相关字段再回传,不先以过小输出预算截断、随后又打印全部正文。按来源展开缺口,不为已知无关的警告反复倾倒所有计划。
17
+ 同一项目多会话并行时,各会话根据当前任务明确自己的已登记计划 ID,并在 check/workset/hook 中重复传入同一组 `--plan ID`。焦点不保存为全项目可变状态;未知/重复 ID 返回 2,多个候选不能靠路径匹配自动归属。未设置焦点的 session-start 仅列轻量索引并提示选择,不能据此判断所有计划准入。
18
+
19
+ 聚焦 workset 的 `plans` 只包含请求对象,schema 1 原字段保留,新增 `scope` 列出请求阶段、实际检查范围、必要前置、证据引用、共享写入和 `facts_version`。必要前置按显式依赖和适用阶段 hard_gate 展开;evidence/soft_context 和同文件写入只带入相关约束,其他计划的业务阻塞不会混入当前任务。历史来源阶段只凭有效的该阶段记录判通过,不用上游后续阶段替代。相关事实在本次读取期间改变时返回 unknown,严格模式失败;无关地图行更新不使焦点失效。facts_version 只是读取版本,写入前须重读相关事实并自行安排单一写入者、串行合并或 worktree。
20
+
21
+ `--plan` 是调用方明确传入的信息范围;工具无法从聊天 ID 自动知道 AI 的任务。宿主没有传入焦点时不能声称已经隔离。必要输入不可读、索引身份不可信等运行/文档错误返回 1;普通工作集的业务阻塞仍是成功查询 0,严格模式为 1。
18
22
 
19
23
  ## 分发与资源
20
24
 
21
25
  `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
26
 
23
- 仅更新计划格式不等于更新项目入口。已有项目在任务交接、没有进行中的实施或复核时更新受管入口;升级不批量回写历史,旧策略名和六列格式按 verification 解释为统一单次行为。安装副本与仓库开发规则有差异时说明本次选用来源,不能因版本号相同就混读正文。
27
+ 仅更新计划格式不等于更新项目入口。已有项目在任务交接、没有进行中的实施或复核时更新受管入口;升级不批量回写历史,旧策略名和六列格式按 verification 解释为默认自验,保留真实历史发现。安装副本与仓库开发规则有差异时说明本次选用来源,不能因版本号相同就混读正文。
24
28
 
25
29
  ## 初始化、模板和检查脚本
26
30
 
@@ -44,7 +48,7 @@ plan-governance-cli init \
44
48
  --update-agent-rules
45
49
  ```
46
50
 
47
- 如果目标目录还不是 Git 仓库,初始化流程会先执行 `git init`;已有 `.git/` 时跳过。
51
+ 新计划仅允许 候选/设计中,phase 必须是阶段身份。初始化先预检全部可预测目标冲突和模式参数,再执行 `git init` 和写入;已有 `.git/` 时跳过。升级/入口-only/迁移模式拒绝无关参数。
48
52
 
49
53
  只更新已有项目的代理规则,不修改 `docs/`:
50
54
 
@@ -79,6 +83,15 @@ plan-governance-cli init \
79
83
  plan-governance-cli check .
80
84
  ```
81
85
 
86
+ 普通 `check` 检查文档一致性:未完成阶段的真实业务阻塞、失败或准入材料缺口返回 WARNING/0,不表示可实施。`--strict-readiness` 对待实施/实施中计划实施门禁;显式 `--plan ID` 时也评估候选/设计中的当前阶段。已完成声明缺少证据或仍有当前阻塞/失败、身份/路径/日期/依赖环损坏、必要输入读取失败为 ERROR/1。合法未初始化目录普通 check 为 not_applicable/0,严格为 1;不存在或非目录 root 为 1。
87
+
88
+ ```bash
89
+ plan-governance-cli check . --plan api-compat-migration --json
90
+ plan-governance-cli check --root . --plan api-compat-migration --strict-readiness
91
+ ```
92
+
93
+ check JSON 的 `schema_version=1`,含 command、mode(consistency/readiness)、status(passed/warning/failed/not_applicable)、exit_code、scope、summary(errors/warnings)及 diagnostics;每条诊断提供 code、category、severity、plan、phase、message 和 sources。JSON stdout 只有结果对象;文本 ERROR 在 stderr,告警在 stdout。0 表示本次操作成功,1 为领域/运行失败,2 为语法、未知参数、漏值、重复单值或互斥组合错误;help 为 0。单个 root 可用位置参数或 `--root`,不能同时传。旧直接检查 root 入口输出弃用提示;未知裸命令不再猜成目录。
94
+
82
95
  可选检查:
83
96
 
84
97
  ```bash
@@ -93,8 +106,8 @@ plan-governance-cli check . --stale-days 10
93
106
  - drift 会自动覆盖活跃计划自身文件、`PLAN_MAP.md` 中可唯一归属的变更行和当前阶段 `### 阶段证据` 中声明的合法相对路径;关闭窗口内,已完成计划自身也在本次变更中时,仅额外覆盖该计划自身和显式阶段证据。跨计划、非法路径或无法归属的变更继续输出 `WARNING`。
94
107
  - `--stale-days`:检查活跃计划是否超过阈值未更新;默认 10 天。
95
108
  - 这些可选检查输出 `WARNING`,不改变退出码。
96
- - `--attest <plan-name>`:为已登记计划创建或覆盖 `docs/attestations/<plan-name>.json` 完成快照。
97
- - `--attest-purpose <purpose>`:与 `--attest` 一起创建带 `purpose`、`snapshot_id`、`supersedes` 和 `review_status` 的关系快照;支持 `phase_completion`、`release_gate`、`compliance`。不传时保持旧 JSON 路径和格式。
109
+ - `--attest <plan-name>`:检查失败时不写入任何快照;完成/发布目的还需目标当前阶段符合准入/完成事实,不因普通检查降级告警而放行。为已登记计划创建或覆盖 `docs/attestations/<plan-name>.json` 完成快照。
110
+ - `--attest-purpose <purpose>`:与 `--attest` 一起创建带 `purpose`、`snapshot_id`、`supersedes` 和 `review_status` 的关系快照;支持 `phase_completion`、`release_gate`、`compliance`。不传时保持旧 JSON 路径和格式。快照写入拒绝与 workset/plan/drift/pre-commit 混用;supersedes/review-status 须与 attest-purpose 一起使用。
98
111
  - `--supersedes <path>`、`--review-status <status>`:创建关系快照时声明替代目标和初始复核状态。
99
112
  - `--check-attestations`:检查完成快照中的计划文件和 `PLAN_MAP.md` hash 是否漂移,并派生 `current`、`superseded` 或 `needs_review`;旧 JSON 缺少 `purpose` 时按 `phase_completion` 兼容读取。漂移、缺失或 JSON 损坏默认只输出 `WARNING`,严格模式才将新增结构错误提升为 `ERROR`。
100
113
  - `--attest-file <path>`:可重复,仅与 `--attest`、`--attest-purpose` 组合启用范围绑定;记录显式文件、目标及必要上游计划的实际内容和相关地图行。文件须为仓库内普通文件,不接受重复、目录、glob、越界或 symlink。无关地图更新不失效;相关内容改变/删除或绑定无效会要求复核,`--check-attestations --strict-readiness` 显式阻断。旧快照不回填、旧默认行为保持,CI/发布不自动开启此检查。
@@ -126,6 +139,8 @@ plan-governance-cli plan impact \
126
139
 
127
140
  `plan impact` 默认只查询功能层;API 契约、数据迁移、安全、外部边界或明确要求代码定位时,才升级到架构层或代码层。输出包含查询层级、升级原因、影响节点、行动分级、测试映射和未查询层级。`graph validate`、`graph impact`、代码查询和 `plan impact` 全部只读,不修改 YAML、计划、代码或 GitNexus 索引;失败也不能被解释为“无影响”。
128
141
 
142
+ graph 命令拒绝未知/重复/漏值参数,depth 为 1–10 整数、format 为 text/json。候选 kind 为 class/function;指定 file 必须是 root 内存在的普通文件,缺失不会扩大到全仓库。外部 impact kind 支持 class/function/method,错误/空/非法响应返回 1;无候选和多候选仍为有效查询 0。
143
+
129
144
  GitNexus 只在需要代码定位时使用。CLI 不自动执行 `gitnexus analyze`,也不自动写回 UID 或 YAML;候选映射由 LLM 依据文件、符号、类型和测试等证据确认,证据不足或存在多个合理候选时再请求用户确认。
130
145
 
131
146
  `影响模块或文件` 的作用域匹配规则:
@@ -141,9 +156,9 @@ GitNexus 只在需要代码定位时使用。CLI 不自动执行 `gitnexus analy
141
156
 
142
157
  ```bash
143
158
  plan-governance-cli hook --event session-start
144
- plan-governance-cli hook --event pre-write --paths scripts/check_plan_governance.py
159
+ plan-governance-cli hook --event pre-write --plan api-compat-migration --paths src/api.py
145
160
  plan-governance-cli hook --event post-write --paths docs/PLAN_MAP.md
146
- plan-governance-cli hook --event stop
161
+ plan-governance-cli hook --event stop --plan api-compat-migration
147
162
  ```
148
163
 
149
- 该脚本不自动安装 `.codex/hooks.json` 或修改全局配置。`stop` 事件只做非阻塞提示,不实现强制 gate。
164
+ 该脚本不自动安装 `.codex/hooks.json` 或修改全局配置。`--paths` 仅适用于 pre-write/post-write,须在 root 内;路径匹配仅给候选,不授权接管。聚焦 pre-write 展示自己及相关共享约束。`stop` 使用同包检查器,领域告警/错误均为非阻塞提示 0,执行故障返回 1 并说明未完成检查;不依赖目标仓库的旧脚本。
@@ -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,12 @@ 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、样本矩阵、验证方式、失败/回滚边界、当前阻塞项及最新独立准入复核;按需增加 `独立复核请求` 及用户请求证据链接。最新表含日期、阶段、结论、证据、复核者;历史使用日期、类型、阶段、结论、证据、复核者六列。自验写类型 `普通自验`;独立发现后的闭环写 `修复自验`,且不能清除真实未解决发现;跨阶段复用写 `复核基线复用` 并关联原记录。其他历史类型按独立检查解释,最新表与当前阶段末条适用记录一致。标题不加阶段编号,阶段身份以地图为准。
72
+
73
+ 普通 check 的真实未完成阻塞/失败/准入缺口为 WARNING/0;严格模式对当前准入对象 ERROR/1,已完成但缺证据或仍有当前阻塞始终 ERROR。各会话使用 `check . --plan <ID>` 与 `workset . --json --plan <ID>` 聚焦,严格评估同时包含必要前置;显式历史阶段关系以来源阶段证据为准。JSON、退出码和并行读取约束见 [cli](cli.md)。