sillyspec 3.20.6 → 3.21.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.
Files changed (80) hide show
  1. package/.claude/CLAUDE.md +23 -0
  2. package/.claude/skills/sillyspec-archive/SKILL.md +86 -21
  3. package/.claude/skills/sillyspec-auto/SKILL.md +98 -83
  4. package/.claude/skills/sillyspec-brainstorm/SKILL.md +78 -44
  5. package/.claude/skills/sillyspec-doctor/SKILL.md +61 -31
  6. package/.claude/skills/sillyspec-execute/SKILL.md +90 -30
  7. package/.claude/skills/sillyspec-explore/SKILL.md +96 -109
  8. package/.claude/skills/sillyspec-export/SKILL.md +4 -0
  9. package/.claude/skills/sillyspec-init/SKILL.md +7 -0
  10. package/.claude/skills/sillyspec-plan/SKILL.md +74 -21
  11. package/.claude/skills/sillyspec-propose/SKILL.md +61 -21
  12. package/.claude/skills/sillyspec-quick/SKILL.md +84 -21
  13. package/.claude/skills/sillyspec-scan/SKILL.md +74 -21
  14. package/.claude/skills/sillyspec-state/SKILL.md +11 -1
  15. package/.claude/skills/sillyspec-status/SKILL.md +55 -21
  16. package/.claude/skills/sillyspec-verify/SKILL.md +82 -21
  17. package/.claude/skills/sillyspec-workspace/SKILL.md +12 -0
  18. package/CLAUDE.md +4 -0
  19. package/package.json +2 -2
  20. package/src/change-list.js +51 -16
  21. package/src/contract-matrix.js +67 -0
  22. package/src/doctor-diagnostics.js +575 -0
  23. package/src/hooks/worktree-guard.js +111 -111
  24. package/src/index.js +154 -82
  25. package/src/progress.js +59 -9
  26. package/src/run.js +139 -38
  27. package/src/stage-contract.js +14 -2
  28. package/src/stages/execute.js +37 -18
  29. package/src/stages/plan-postcheck.js +234 -0
  30. package/src/stages/plan.js +13 -0
  31. package/src/sync.js +13 -3
  32. package/src/worktree.js +69 -28
  33. package/docs/brainstorm-plan-contract.md +0 -64
  34. package/docs/plan-execute-contract.md +0 -123
  35. package/docs/platform-scan-protocol.md +0 -298
  36. package/docs/revision-mode.md +0 -115
  37. package/docs/sillyspec/file-lifecycle/known-implementation-gaps.md +0 -99
  38. package/docs/sillyspec/file-lifecycle/platform-workflows-sync.md +0 -223
  39. package/docs/sillyspec/file-lifecycle/stage-artifacts.md +0 -167
  40. package/docs/sillyspec/file-lifecycle/storage-and-state.md +0 -148
  41. package/docs/sillyspec/file-lifecycle/worktree-and-guard.md +0 -211
  42. package/docs/sillyspec/file-lifecycle.md +0 -132
  43. package/docs/workflow-contract-regression.md +0 -106
  44. package/docs/worktree-isolation.md +0 -252
  45. package/test/brainstorm-plan-contract.test.mjs +0 -273
  46. package/test/check-syntax.mjs +0 -26
  47. package/test/cli-top-level-aliases.test.mjs +0 -174
  48. package/test/contract-artifacts.test.mjs +0 -323
  49. package/test/decision-ref-version.mjs +0 -85
  50. package/test/decision-supersede.test.mjs +0 -277
  51. package/test/knowledge-match.test.mjs +0 -231
  52. package/test/plan-execute-contract.test.mjs +0 -357
  53. package/test/plan-optimization.test.mjs +0 -572
  54. package/test/platform-artifacts.test.mjs +0 -190
  55. package/test/platform-failure-samples.test.mjs +0 -199
  56. package/test/platform-recovery-chain.test.mjs +0 -179
  57. package/test/platform-recovery.test.mjs +0 -167
  58. package/test/platform-scan-p0.test.mjs +0 -186
  59. package/test/quick-recommend.test.mjs +0 -146
  60. package/test/revision-v1.test.mjs +0 -1145
  61. package/test/run-sanitize-project-name.test.mjs +0 -51
  62. package/test/run-scan-postcheck-fail.test.mjs +0 -64
  63. package/test/run-scan-project-parse.test.mjs +0 -200
  64. package/test/run-tests.mjs +0 -48
  65. package/test/runtime-cleanup-keeps-worktree.test.mjs +0 -107
  66. package/test/scan-docs-yaml-placeholders.test.mjs +0 -84
  67. package/test/scan-knowledge.test.mjs +0 -175
  68. package/test/scan-paths.test.mjs +0 -68
  69. package/test/scan-postcheck-project-priority.test.mjs +0 -85
  70. package/test/scan-postcheck.test.mjs +0 -197
  71. package/test/scan-workflow-anyfailed-block.test.mjs +0 -52
  72. package/test/spec-dir.test.mjs +0 -206
  73. package/test/stage-contract-failed-post-check.test.mjs +0 -102
  74. package/test/stage-contract.test.mjs +0 -301
  75. package/test/stage-definitions.test.mjs +0 -39
  76. package/test/wait-gates.test.mjs +0 -501
  77. package/test/workflow-spec-base.test.mjs +0 -142
  78. package/test/worktree-deps-provision.test.mjs +0 -148
  79. package/test/worktree-guard.test.mjs +0 -136
  80. package/test/worktree-native-overlay.test.mjs +0 -188
@@ -1,21 +1,74 @@
1
- ---
2
- name: sillyspec:scan
3
- description: 用于扫描项目代码库,生成架构文档、代码约定、目录结构等。适合用户说"扫描项目、分析代码库、生成文档、scan"。产出 7 份扫描文档 + 模块映射。
4
- ---
5
-
6
- ## 多变更说明
7
-
8
- 如果项目有多个活跃变更(`.sillyspec/changes/` 下有多个目录),所有 `sillyspec run` 命令需要加 `--change <变更名>`。只有一个变更时可省略(CLI 自动检测)。
9
-
10
- ## 执行
11
-
12
- **你必须使用 exec 工具(shell)执行以下命令,不要自己编造流程:**
13
-
14
- 1. 运行 `sillyspec run scan` 读取输出的步骤 prompt
15
- 2. 按照输出的 prompt **严格执行**,不要跳过或自行添加步骤
16
- 3. 步骤完成后,运行 `sillyspec run scan --done --output "你的摘要"`
17
- 4. 重复 2-3 直到阶段完成
18
- 5. **禁止**在没有运行 CLI 的情况下自行决定流程
19
-
20
- ## 用户指令
21
- $ARGUMENTS
1
+ ---
2
+ name: sillyspec:scan
3
+ description: 用于扫描项目代码库,生成架构文档、代码约定、目录结构等。适合用户说"扫描项目、分析代码库、生成文档、scan"。产出 7 份扫描文档 + 模块映射。
4
+ ---
5
+
6
+ ## 何时使用
7
+
8
+ - 用户说"扫描项目、分析代码库、生成文档、scan"
9
+ - 棕地项目首次接入 sillyspec,生成架构/约定/结构等基础文档
10
+ - 产出 7 份 scan 文档(PROJECT/ARCHITECTURE/CONVENTIONS/STRUCTURE/INTEGRATIONS/TESTING/CONCERNS)+ 模块映射
11
+
12
+ ## 多变更说明
13
+
14
+ scan 是辅助阶段,通常不需要 `--change`。但项目有多个活跃变更时,所有 `sillyspec run` 命令加 `--change <变更名>` 可指定操作目标。
15
+
16
+ ## 步骤生命周期(所有阶段通用)
17
+
18
+ > `sillyspec scan` 是 `sillyspec run scan` 的顶层别名,两者等价。
19
+
20
+ ```bash
21
+ sillyspec run scan # 输出当前步骤 prompt
22
+ sillyspec run scan --done --output "摘要" # 完成当前步骤
23
+ sillyspec run scan --status # 查看阶段进度
24
+ sillyspec run scan --skip # 跳过可选步骤
25
+ sillyspec run scan --reset # 重置阶段(从头开始)
26
+ ```
27
+
28
+ ## 通用参数(所有阶段适用)
29
+
30
+ | 参数 | 说明 |
31
+ |---|---|
32
+ | `--spec-dir <path>` | 指定规范目录(默认 `<项目>/.sillyspec`) |
33
+ | `--non-interactive` | CI/脚本下禁用交互式 prompt |
34
+ | `--skip-approval` | 跳过审批/校验门控 |
35
+ | `--json` | 输出 JSON(程序化读取) |
36
+
37
+ ## scan 特有参数
38
+
39
+ | 参数 | 说明 |
40
+ |---|---|
41
+ | `--deep` | 强制 deep 扫描 profile(完整流程,不按规模裁剪) |
42
+ | `--force-rescan` | 覆盖已有 scan 文档的保护(默认覆盖需 source_commit/updated_at 匹配) |
43
+
44
+ ### scanProfile(按项目规模自动裁剪)
45
+
46
+ CLI 根据源码规模自动选择 profile,无需手动指定:
47
+
48
+ | profile | 触发条件 | 行为 |
49
+ |---|---|---|
50
+ | quick | ≤30 文件 且 ≤80KB 且 ≤3 项目 | 3 步,0 子代理,5 份核心文档 |
51
+ | standard | ≤200 文件 且 ≤800KB | 压缩步骤,最多 1 子代理 |
52
+ | deep | 大项目或 `--deep` | 完整流程 |
53
+
54
+ ### post-check
55
+
56
+ scan 完成时 CLI 自动校验 7 份文档齐全。缺失会设状态为 `failed_post_check`,阻断进入主流程下游(brainstorm/plan 等),需修复后重跑 scan。
57
+
58
+ ## 阶段流转
59
+
60
+ ```
61
+ (项目起点) → scan → brainstorm
62
+ ```
63
+
64
+ scan 完成后,运行 `sillyspec run brainstorm "<需求>"` 开始具体变更的设计。
65
+
66
+ ## 铁律
67
+
68
+ - **必须用 exec 工具(shell)执行 CLI,不要自己编造流程**
69
+ - 只做当前步骤 prompt 描述的操作,不跳过
70
+ - scan 文档写入 `{DOCS_ROOT}/scan/`(平台模式用占位符路径,不写裸 `.sillyspec/`)
71
+ - 完成后立即 `--done`,不跳过
72
+
73
+ ## 用户指令
74
+ $ARGUMENTS
@@ -46,9 +46,19 @@ sillyspec progress show
46
46
  >
47
47
  > 进度数据会在 `sillyspec init` 时自动创建到 SQLite 数据库中。
48
48
 
49
+ ## progress 完整子命令(只读查询 / 诊断)
50
+
51
+ ```bash
52
+ sillyspec progress show [--change <名>] # 当前工作状态(本 skill 主命令)
53
+ sillyspec progress check # 状态一致性检查(只报告,不修复)
54
+ sillyspec progress repair # 修复状态元数据(dry-run,加 --apply 才真改)
55
+ sillyspec progress validate # 校验并修复
56
+ sillyspec progress reset [--stage <阶段>] # 重置进度(破坏性,慎用)
57
+ ```
58
+
49
59
  ### 注意
50
60
 
51
- - 这是只读命令,**不修改任何文件**
61
+ - 这是只读命令,**不修改任何文件**(repair/validate/reset 除外)
52
62
  - `/sillyspec:status` 查看项目整体进度(change 文件级别)
53
63
  - `/sillyspec:state` 查看当前工作状态(阶段/步骤级别)
54
64
  - 两者互补:status 看"有什么",state 看"在做什么"
@@ -1,21 +1,55 @@
1
- ---
2
- name: sillyspec:status
3
- description: 用于查看 SillySpec 当前进度和状态。适合用户说"看下状态、当前进度、status"。显示当前阶段、步骤完成度、活跃变更。
4
- ---
5
-
6
- ## 多变更说明
7
-
8
- 如果项目有多个活跃变更(`.sillyspec/changes/` 下有多个目录),所有 `sillyspec run` 命令需要加 `--change <变更名>`。只有一个变更时可省略(CLI 自动检测)。
9
-
10
- ## 执行
11
-
12
- **你必须使用 exec 工具(shell)执行以下命令,不要自己编造流程:**
13
-
14
- 1. 运行 `sillyspec run status` 读取输出的步骤 prompt
15
- 2. 按照输出的 prompt **严格执行**,不要跳过或自行添加步骤
16
- 3. 步骤完成后,运行 `sillyspec run status --done --output "你的摘要"`
17
- 4. 重复 2-3 直到阶段完成
18
- 5. **禁止**在没有运行 CLI 的情况下自行决定流程
19
-
20
- ## 用户指令
21
- $ARGUMENTS
1
+ ---
2
+ name: sillyspec:status
3
+ description: 用于查看 SillySpec 当前进度和状态。适合用户说"看下状态、当前进度、status"。显示当前阶段、步骤完成度、活跃变更。
4
+ ---
5
+
6
+ ## 何时使用
7
+
8
+ - 用户说"看下状态、当前进度、status"
9
+ - 查看项目整体进度:活跃变更、各阶段状态、步骤完成度
10
+
11
+ ## status vs state 分工
12
+
13
+ - **`/sillyspec:status`**(本 skill)— 查看项目整体进度(change 文件级别、各阶段状态)。走 `sillyspec run status` 阶段。
14
+ - **`/sillyspec:state`** 查看当前工作状态(阶段/步骤级别、下一步建议)。走 `sillyspec progress show`。
15
+
16
+ 两者互补:status 看"有什么",state "在做什么"
17
+
18
+ ## 多变更说明
19
+
20
+ status 是辅助阶段。项目有多个活跃变更时,加 `--change <变更名>` 查看指定变更的详情;不指定时汇总显示所有变更。
21
+
22
+ ## 步骤生命周期(所有阶段通用)
23
+
24
+ > `sillyspec status` 是 `sillyspec run status` 的顶层别名,两者等价。status 是辅助阶段,只读。
25
+
26
+ ```bash
27
+ sillyspec run status # 输出当前步骤 prompt
28
+ sillyspec run status --done --output "摘要" # 完成阶段
29
+ sillyspec run status --status # 查看阶段状态
30
+ ```
31
+
32
+ ## 通用参数(所有阶段适用)
33
+
34
+ | 参数 | 说明 |
35
+ |---|---|
36
+ | `--change <名>` | 指定变更名(多变更时查看指定变更详情) |
37
+ | `--spec-dir <path>` | 指定规范目录(默认 `<项目>/.sillyspec`) |
38
+ | `--json` | 输出 JSON(程序化读取) |
39
+
40
+ ## 配套的只读查询命令(不经 run)
41
+
42
+ ```bash
43
+ sillyspec progress show # 当前工作状态(阶段/步骤级)
44
+ sillyspec progress show --change <名> # 指定变更详情
45
+ sillyspec progress check # 状态一致性检查
46
+ ```
47
+
48
+ ## 铁律
49
+
50
+ - status 是只读阶段,**不修改任何文件**
51
+ - **必须用 exec 工具(shell)执行 CLI**
52
+ - 完成后立即 `--done`,不跳过
53
+
54
+ ## 用户指令
55
+ $ARGUMENTS
@@ -1,21 +1,82 @@
1
- ---
2
- name: sillyspec:verify
3
- description: 用于验证代码实现是否符合 design 和模块文档。适合用户说"验证下、检查下、跑 verify"。对照 design.md + 模块文档检查任务完成度、设计一致性、运行测试。
4
- ---
5
-
6
- ## 多变更说明
7
-
8
- 如果项目有多个活跃变更(`.sillyspec/changes/` 下有多个目录),所有 `sillyspec run` 命令需要加 `--change <变更名>`。只有一个变更时可省略(CLI 自动检测)。
9
-
10
- ## 执行
11
-
12
- **你必须使用 exec 工具(shell)执行以下命令,不要自己编造流程:**
13
-
14
- 1. 运行 `sillyspec run verify` — 读取输出的步骤 prompt
15
- 2. 按照输出的 prompt **严格执行**,不要跳过或自行添加步骤
16
- 3. 步骤完成后,运行 `sillyspec run verify --done --output "你的摘要"`
17
- 4. 重复 2-3 直到阶段完成
18
- 5. **禁止**在没有运行 CLI 的情况下自行决定流程
19
-
20
- ## 用户指令
21
- $ARGUMENTS
1
+ ---
2
+ name: sillyspec:verify
3
+ description: 用于验证代码实现是否符合 design 和模块文档。适合用户说"验证下、检查下、跑 verify"。对照 design.md + 模块文档检查任务完成度、设计一致性、运行测试。
4
+ ---
5
+
6
+ ## 何时使用
7
+
8
+ - 用户说"验证下、检查下、跑 verify"
9
+ - 对照 design.md + 模块文档检查任务完成度
10
+ - 设计一致性检查 + 运行测试套件
11
+ - 产出 `verify-result.md`(PASS / PASS WITH NOTES / FAIL)
12
+
13
+ ## 多变更说明
14
+
15
+ 项目有多个活跃变更(`.sillyspec/changes/` 下有多个目录)时,所有 `sillyspec run` 命令需加 `--change <变更名>` 指定操作目标;只有一个变更时可省略(CLI 自动检测)。
16
+
17
+ ## 步骤生命周期(所有阶段通用)
18
+
19
+ > `sillyspec verify` 是 `sillyspec run verify` 的顶层别名,两者等价。
20
+
21
+ ```bash
22
+ sillyspec run verify # 输出当前步骤 prompt
23
+ sillyspec run verify --done --output "摘要" # 完成当前步骤(--input "用户原话" 记录输入)
24
+ sillyspec run verify --status # 查看阶段进度
25
+ sillyspec run verify --skip # 跳过可选步骤
26
+ sillyspec run verify --reset # 重置阶段(从头开始)
27
+ sillyspec run verify --reopen --from-step N # 重新打开已完成阶段修订(N=序号或名称)
28
+ ```
29
+
30
+ ## 通用参数(所有阶段适用)
31
+
32
+ | 参数 | 说明 |
33
+ |---|---|
34
+ | `--change <名>` | 指定变更名(多活跃变更必填,单变更可省略自动检测) |
35
+ | `--spec-dir <path>` | 指定规范目录(默认 `<项目>/.sillyspec`) |
36
+ | `--non-interactive` | CI/脚本下禁用交互式 prompt |
37
+ | `--skip-approval` | 跳过审批/校验门控(需明确意图) |
38
+ | `--json` | 输出 JSON(程序化读取) |
39
+
40
+ ## verify 特有:完成门控(重要)
41
+
42
+ verify 是只读阶段(**禁止改代码/改 git 状态**,只检查 + 写报告)。完成时有硬校验:
43
+
44
+ - **必须产出 `verify-result.md`**——不存在则阻断完成(不能跳过报告直接 `--done`)
45
+ - **结论为 `FAIL` 则阻断完成**——不能带着 FAIL 标记 verify 完成
46
+ - **`integration-critical` / `deployment-critical` 变更**(design/plan 含 daemon/session/lease/lifecycle 等关键词):结论 PASS WITH NOTES 降级为 FAIL,必须有真实集成证据(Runtime Evidence section)
47
+ - `verify-required-evidence.json`(execute 写入)中每条 missing evidence → 阻断
48
+
49
+ 被阻断时 CLI 打印 ❌ 校验失败,不会提示"验证通过"。修复 `verify-result.md` 后重新 `--done`。
50
+
51
+ ## verify-result.md 格式
52
+
53
+ ```markdown
54
+ # 验证报告
55
+ ## 结论
56
+ PASS / PASS WITH NOTES / FAIL ← 必须有此章节,FAIL 会阻断 verify 完成
57
+ ## 任务完成度
58
+ ## 设计一致性
59
+ ## 探针结果
60
+ ## 测试结果
61
+ ## 变更风险等级
62
+ ## Runtime Evidence(integration/deployment-critical 必填)
63
+ ```
64
+
65
+ ## 阶段流转
66
+
67
+ ```
68
+ execute → verify → archive
69
+ ```
70
+
71
+ verify 通过(PASS)后,运行 `sillyspec run archive --change <变更名>` 归档。FAIL 则修复后重跑 `sillyspec run verify`。
72
+
73
+ ## 铁律
74
+
75
+ - **必须用 exec 工具(shell)执行 CLI,不要自己编造流程**
76
+ - verify 阶段**绝对禁止** git checkout/restore/reset、删除/覆盖源码文件——只检查 + 报告
77
+ - 发现问题只报告,不尝试修复(修复回 execute)
78
+ - `verify-result.md` 结论必须基于证据,不写"看起来没问题"
79
+ - 完成后立即 `--done`,不跳过
80
+
81
+ ## 用户指令
82
+ $ARGUMENTS
@@ -12,6 +12,18 @@ description: 工作区管理 — 初始化、管理多项目工作区,查看
12
12
 
13
13
  ---
14
14
 
15
+ ## CLI 边界(重要)
16
+
17
+ sillyspec CLI **没有 `workspace` 顶层命令**。本 skill 通过直接读写 `.sillyspec/projects/*.yaml` 管理工作区,配套用 `sillyspec scan` 扫描子项目、`sillyspec init` 安装命令模板。**不要编造 `sillyspec workspace ...` 子命令。**
18
+
19
+ ## 相关 CLI 命令
20
+
21
+ ```bash
22
+ sillyspec scan # 扫描子项目生成文档
23
+ sillyspec init [--tool <名>] # 安装命令模板
24
+ sillyspec progress show # 查看进度
25
+ ```
26
+
15
27
  你现在是 SillySpec 的工作区管理器。
16
28
 
17
29
  ## 用户指令
package/CLAUDE.md CHANGED
@@ -1,5 +1,9 @@
1
1
  # Claude Code 指引
2
2
 
3
+ # SillySpec工具定位
4
+ 1. 是给Agent调用的CLI工具,非人类直接使用
5
+ 2. 管理Agent工作流的CLI工具,非业务工具
6
+
3
7
  ## 文件生命周期文档同步
4
8
  每次修改 `src/stages/` 下的阶段定义(prompt、步骤、输出文件名等)或 `src/run.js`、`src/progress.js` 等影响文件生命周期的代码后,**必须同步更新** `docs/sillyspec/file-lifecycle.md`,确保文档与代码一致。
5
9
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sillyspec",
3
- "version": "3.20.6",
3
+ "version": "3.21.0",
4
4
  "description": "SillySpec CLI — 流程状态机,让 AI 严格按步骤来",
5
5
  "icon": "logo.jpg",
6
6
  "homepage": "https://sillyspec.ppdmq.top/",
@@ -10,7 +10,7 @@
10
10
  },
11
11
  "type": "module",
12
12
  "bin": {
13
- "sillyspec": "./bin/sillyspec.js"
13
+ "sillyspec": "bin/sillyspec.js"
14
14
  },
15
15
  "engines": {
16
16
  "node": ">=18"
@@ -1,7 +1,28 @@
1
1
  import { readFileSync, existsSync } from 'fs'
2
2
 
3
3
  /**
4
- * design.md 解析文件变更清单
4
+ * 归一化清单/allowed_paths 中的路径项:
5
+ * - 去反引号、去行内括号注释(`src/foo.js (新增)` / `src/foo.js(说明)`)
6
+ * - 统一为正斜杠、去首尾空白与尾部斜杠
7
+ * @param {string} raw
8
+ * @returns {string}
9
+ */
10
+ function normalizeEntry(raw) {
11
+ if (!raw) return ''
12
+ return raw
13
+ .replace(/`/g, '')
14
+ .replace(/\s*([^)]*)\s*$/, '')
15
+ .replace(/\s*\([^)]*\)\s*$/, '')
16
+ .replace(/\\/g, '/')
17
+ .replace(/\/+$/, '')
18
+ .trim()
19
+ }
20
+
21
+ /**
22
+ * 从 design.md 解析文件变更清单。兼容两种真实写法:
23
+ * ① 表格:`| 操作 | 文件路径 | 说明 |`(brainstorm 模板默认)
24
+ * ② 分类列表:`### 新增文件` / `### 修改文件` / `### 不修改文件` 下的 `- path`
25
+ * 始终忽略 `.sillyspec/` 内的路径与占位符;「不修改/保留」子段下的路径会被排除。
5
26
  * @param {string} designMdPath - design.md 文件路径
6
27
  * @returns {Set<string>} 文件路径集合(相对路径,如 "src/worktree.js")
7
28
  */
@@ -10,7 +31,7 @@ export function parseFileChangeList(designMdPath) {
10
31
 
11
32
  if (!designMdPath || !existsSync(designMdPath)) return result
12
33
 
13
- const content = readFileSync(designMdPath, 'utf8')
34
+ const content = readFileSync(designMdPath, 'utf8').replace(/\r\n/g, '\n')
14
35
 
15
36
  // 定位"文件变更清单"标题
16
37
  const sectionRegex = /^#{2,3}\s*文件变更清单/m
@@ -24,28 +45,42 @@ export function parseFileChangeList(designMdPath) {
24
45
  ? afterSection.slice(0, nextSectionMatch.index)
25
46
  : afterSection
26
47
 
27
- // 解析表格行
48
+ const isExcluded = (p) => !p || p === '—' || p === '-' || p.startsWith('.sillyspec/')
49
+
28
50
  const lines = relevantContent.split('\n')
29
51
  let headerSkipped = false
30
- for (const line of lines) {
31
- // 跳过分隔行和非表格行
32
- if (!line.startsWith('|') || /^\|[-:\s|]+\|$/.test(line)) continue
52
+ // 分类列表的当前子段模式:include(新增/修改/删除)或 exclude(不修改/保留)
53
+ let listMode = 'include'
33
54
 
34
- const cells = line.split('|').slice(1, -1) // 去掉首尾空元素
35
- if (cells.length < 2) continue
36
-
37
- // 跳过 header 行(包含「文件路径」的表头)
38
- if (!headerSkipped) {
39
- headerSkipped = true
55
+ for (const line of lines) {
56
+ // 分类列表子标题:### 新增文件 / ### 修改文件 / ### 不修改文件
57
+ const subHeader = line.match(/^###\s+(.+?)\s*$/)
58
+ if (subHeader) {
59
+ listMode = /不修改|不变|保留|无变更|未变更|不改动/.test(subHeader[1]) ? 'exclude' : 'include'
40
60
  continue
41
61
  }
42
62
 
43
- const filePath = cells[1].trim().replace(/^`|`$/g, '')
63
+ // 表格行
64
+ if (line.startsWith('|')) {
65
+ if (/^\|[-:\s|]+\|$/.test(line)) continue // 分隔行
66
+ const cells = line.split('|').slice(1, -1) // 去掉首尾空元素
67
+ if (cells.length < 2) continue
68
+ if (!headerSkipped) { headerSkipped = true; continue } // 跳过表头
44
69
 
45
- // 忽略空路径、注释、.sillyspec/ 内的路径
46
- if (!filePath || filePath === '—' || filePath === '-' || filePath.startsWith('.sillyspec/')) continue
70
+ const filePath = normalizeEntry(cells[1])
71
+ if (isExcluded(filePath)) continue
72
+ result.add(filePath)
73
+ continue
74
+ }
47
75
 
48
- result.add(filePath)
76
+ // 分类列表项:`- path` / `- \`path\``
77
+ const listItem = line.match(/^\s*-\s+(.+)/)
78
+ if (listItem) {
79
+ const filePath = normalizeEntry(listItem[1])
80
+ if (isExcluded(filePath)) continue
81
+ if (listMode === 'exclude') result.delete(filePath)
82
+ else result.add(filePath)
83
+ }
49
84
  }
50
85
 
51
86
  return result
@@ -15,6 +15,7 @@ import {
15
15
  scanFrontendApiCalls,
16
16
  normalizePath,
17
17
  } from './endpoint-extractor.js'
18
+ import { parseTaskContracts } from './stages/plan-postcheck.js'
18
19
 
19
20
  // ─── 关键词检测 ─────────────────────────────────────────────────────────
20
21
 
@@ -218,6 +219,72 @@ export function buildConsumerInjection(changeDir, specBase, taskName, contracts)
218
219
  return parts.join('\n')
219
220
  }
220
221
 
222
+ /**
223
+ * 为 consumer task 构建字段级契约注入:对比 expects_from.needs vs provider.provides.fields
224
+ *
225
+ * 让 consumer 子代理带着明确的字段清单核验上游产出:
226
+ * - provider 已承诺 → 编码时只使用 provides.fields,运行时缺字段上报
227
+ * - provider 未承诺某 needs 字段 → 标 CONTRACT_GAP,要求 stop and report(禁止 fallback 编造)
228
+ *
229
+ * 命中场景:provider task 漏实现某字段(如 DaemonRuntimeRead 缺 daemon_instance_id),
230
+ * consumer 若 fallback 编造 → 运行时 403/500。此处把"缺字段"暴露在子代理启动前。
231
+ *
232
+ * 注:plan-postcheck 已对账 expects_from↔provides,此处是 execute 时的二次保险,
233
+ * 拦截 plan-postcheck 之后 task 文件被手改、或 provider 实际实现漏字段的情况。
234
+ *
235
+ * @param {string} changeDir - changes/<name>/ 目录
236
+ * @param {string} taskName - consumer task(如 task-11)
237
+ * @returns {string|null} 注入文本,无 expects_from 时返回 null
238
+ */
239
+ export function buildContractFieldInjection(changeDir, taskName) {
240
+ const consumerFile = join(changeDir, 'tasks', `${taskName}.md`)
241
+ if (!existsSync(consumerFile)) return null
242
+ const { expectsFrom } = parseTaskContracts(readFileSync(consumerFile, 'utf8'))
243
+ const providers = Object.keys(expectsFrom)
244
+ if (providers.length === 0) return null
245
+
246
+ const lines = []
247
+ lines.push('## Upstream Contract Fields(字段级核验)')
248
+ lines.push('')
249
+
250
+ for (const providerTask of providers) {
251
+ const providerFile = join(changeDir, 'tasks', `${providerTask}.md`)
252
+ let providerProvides = []
253
+ if (existsSync(providerFile)) {
254
+ providerProvides = parseTaskContracts(readFileSync(providerFile, 'utf8')).provides
255
+ }
256
+
257
+ for (const c of expectsFrom[providerTask]) {
258
+ const providerEntry = providerProvides.find(p => p.contract === c.contract)
259
+ if (!providerEntry) {
260
+ lines.push(`### ⚠️ CONTRACT_GAP: ${providerTask} → ${c.contract}`)
261
+ lines.push(`你需要字段 [${c.needs.join(', ')}],但 ${providerTask} 的 provides 未声明此契约。`)
262
+ lines.push(`**立即停止编码并上报**:不要 fallback、不要编造字段,先确认 ${providerTask} 是否应产出此契约。`)
263
+ } else {
264
+ const providerFields = new Set(providerEntry.fields)
265
+ const missing = c.needs.filter(f => !providerFields.has(f))
266
+ if (missing.length > 0) {
267
+ lines.push(`### ⚠️ CONTRACT_GAP: ${providerTask} → ${c.contract}`)
268
+ lines.push(`你需要字段 [${missing.join(', ')}],但 ${providerTask}.provides 仅承诺 [${providerEntry.fields.join(', ')}]。`)
269
+ lines.push(`**立即停止编码并上报**:不要 fallback、不要编造字段,先要求 ${providerTask} 在 provides.fields 补上 [${missing.join(', ')}]。`)
270
+ } else {
271
+ lines.push(`### ✅ ${providerTask} → ${c.contract}`)
272
+ lines.push(`你需要的字段 [${c.needs.join(', ')}] 均在 ${providerTask}.provides 承诺内:[${providerEntry.fields.join(', ')}]。`)
273
+ lines.push(`编码时**只使用上述字段**;若运行时实际返回缺字段,说明 provider 实现漏了 → 上报 CONTRACT_GAP,不要 fallback。`)
274
+ }
275
+ }
276
+ lines.push('')
277
+ }
278
+ }
279
+
280
+ lines.push('### 字段级铁律')
281
+ lines.push('1. 禁止 fallback 编造:若上游返回缺字段,停止并上报 CONTRACT_GAP,不要用 `x || defaultValue` 之类的防御性回退掩盖契约破裂。')
282
+ lines.push('2. 只消费 provides 承诺的字段;需要新字段必须先让 provider 更新 provides。')
283
+ lines.push('3. 启动子代理前,先读 provider task 的 review.json / acceptance,确认其已声明完成上述契约字段。')
284
+
285
+ return lines.join('\n')
286
+ }
287
+
221
288
  // ─── Verify 阶段:parity check ──────────────────────────────────────────
222
289
 
223
290
  /**