@michaelxwb/code-flow 0.5.1 → 0.6.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 (76) hide show
  1. package/docs/migrations/spec-workflow-0.6.0.md +42 -0
  2. package/package.json +4 -2
  3. package/src/adapters/claude/CLAUDE.md +11 -17
  4. package/src/adapters/claude/commands/cf-init.md +5 -4
  5. package/src/adapters/claude/commands/cf-spec.md +50 -0
  6. package/src/adapters/claude/commands/cf-task/align.md +29 -3
  7. package/src/adapters/claude/commands/cf-task/archive.md +32 -4
  8. package/src/adapters/claude/commands/cf-task/plan.md +54 -1
  9. package/src/adapters/claude/commands/cf-task/prd.md +24 -0
  10. package/src/adapters/claude/commands/cf-task/start.md +56 -14
  11. package/src/adapters/claude/settings.local.json +1 -11
  12. package/src/adapters/codex/AGENTS.md +11 -17
  13. package/src/adapters/codex/hooks.json +6 -4
  14. package/src/adapters/codex/skills/cf-init/SKILL.md +5 -4
  15. package/src/adapters/codex/skills/cf-spec/SKILL.md +55 -0
  16. package/src/adapters/codex/skills/cf-task-align/SKILL.md +29 -3
  17. package/src/adapters/codex/skills/cf-task-archive/SKILL.md +32 -4
  18. package/src/adapters/codex/skills/cf-task-plan/SKILL.md +54 -1
  19. package/src/adapters/codex/skills/cf-task-prd/SKILL.md +24 -0
  20. package/src/adapters/codex/skills/cf-task-start/SKILL.md +56 -14
  21. package/src/adapters/costrict/CLAUDE.md +11 -17
  22. package/src/adapters/costrict/commands/cf-init.md +5 -4
  23. package/src/adapters/costrict/commands/cf-spec.md +50 -0
  24. package/src/adapters/costrict/commands/cf-task/align.md +29 -3
  25. package/src/adapters/costrict/commands/cf-task/archive.md +32 -4
  26. package/src/adapters/costrict/commands/cf-task/plan.md +54 -1
  27. package/src/adapters/costrict/commands/cf-task/prd.md +24 -0
  28. package/src/adapters/costrict/commands/cf-task/start.md +56 -14
  29. package/src/adapters/costrict/settings.local.json +1 -11
  30. package/src/adapters/opencode/AGENTS.md +11 -17
  31. package/src/adapters/opencode/commands/cf-init.md +4 -3
  32. package/src/adapters/opencode/commands/cf-spec.md +54 -0
  33. package/src/adapters/opencode/commands/cf-task/align.md +30 -3
  34. package/src/adapters/opencode/commands/cf-task/archive.md +33 -4
  35. package/src/adapters/opencode/commands/cf-task/plan.md +55 -1
  36. package/src/adapters/opencode/commands/cf-task/prd.md +25 -0
  37. package/src/adapters/opencode/commands/cf-task/start.md +57 -14
  38. package/src/adapters/opencode/plugins/code-flow/index.js +0 -1
  39. package/src/cli.js +51 -2
  40. package/src/core/code-flow/config.yml +13 -16
  41. package/src/core/code-flow/scripts/cf_checks.py +30 -0
  42. package/src/core/code-flow/scripts/cf_core.py +12 -255
  43. package/src/core/code-flow/scripts/cf_log.py +8 -0
  44. package/src/core/code-flow/scripts/cf_post_hook.py +33 -9
  45. package/src/core/code-flow/scripts/cf_pre_tool_hook.py +65 -0
  46. package/src/core/code-flow/scripts/cf_spec_context.py +1233 -0
  47. package/src/core/code-flow/scripts/cf_spec_gate.py +224 -0
  48. package/src/core/code-flow/scripts/cf_spec_metadata.py +304 -0
  49. package/src/core/code-flow/scripts/cf_spec_migrate.py +512 -0
  50. package/src/core/code-flow/scripts/cf_spec_resolver.py +271 -0
  51. package/src/core/code-flow/scripts/cf_spec_router.py +145 -0
  52. package/src/core/code-flow/scripts/cf_spec_session.py +122 -0
  53. package/src/core/code-flow/scripts/cf_spec_verify.py +293 -0
  54. package/src/core/code-flow/scripts/cf_stats.py +40 -5
  55. package/src/core/code-flow/scripts/cf_stop_hook.py +107 -7
  56. package/src/core/code-flow/scripts/cf_task_runtime.py +142 -0
  57. package/src/core/code-flow/scripts/cf_user_prompt_hook.py +34 -360
  58. package/src/core/code-flow/specs/backend/code-quality-performance.md +13 -1
  59. package/src/core/code-flow/specs/backend/database.md +13 -1
  60. package/src/core/code-flow/specs/backend/directory-structure.md +13 -1
  61. package/src/core/code-flow/specs/backend/logging.md +13 -1
  62. package/src/core/code-flow/specs/backend/platform-rules.md +13 -1
  63. package/src/core/code-flow/specs/frontend/component-specs.md +13 -1
  64. package/src/core/code-flow/specs/frontend/directory-structure.md +13 -1
  65. package/src/core/code-flow/specs/frontend/quality-standards.md +13 -1
  66. package/src/core/code-flow/specs/shared/design/design-frontend.md +20 -10
  67. package/src/core/code-flow/specs/shared/design/design-full.md +36 -24
  68. package/src/core/code-flow/specs/shared/design/design-lite.md +21 -9
  69. package/src/core/code-flow/specs/shared/prd-template.md +14 -3
  70. package/src/migrate/spec-workflow.js +238 -0
  71. package/src/adapters/claude/commands/cf-inject.md +0 -55
  72. package/src/adapters/codex/skills/cf-inject/SKILL.md +0 -56
  73. package/src/adapters/costrict/commands/cf-inject.md +0 -55
  74. package/src/adapters/opencode/commands/cf-inject.md +0 -59
  75. package/src/core/code-flow/scripts/cf_inject_hook.py +0 -178
  76. package/src/core/code-flow/scripts/cf_session_hook.py +0 -42
@@ -2,6 +2,7 @@
2
2
  description: 从需求出发通过结构化对话产出设计简报(.design.md)
3
3
  ---
4
4
 
5
+
5
6
  # cf-task:align
6
7
 
7
8
  从需求出发,通过结构化对话产出设计简报(`.design.md`),为 `cf-task:plan` 提供足够丰富的输入。支持三种输入:纯文本需求、`.prd.md` 产品需求文档、`.design.md` 草稿。
@@ -114,6 +115,18 @@ PRD 已覆盖的维度如无需调整,将直接使用。
114
115
  - Step 3 中已从 PRD 获得充分信息的维度**不重复提问**,除非用户主动修改
115
116
  - design 中 FEAT 的"来源"列必须引用 PRD 中对应的 US-XX
116
117
 
118
+ ### 2.7. 继承并刷新 Spec Context(硬门禁)
119
+
120
+ 进入技术讨论前先定位需求目录中的 `spec-context.yml`:
121
+
122
+ 1. PRD 派生/恢复模式必须执行 `python3 .code-flow/scripts/cf_spec_context.py refresh --task-dir <需求目录> --root "$PWD" --json`;missing/stale/conflict 必须先处理。
123
+ 2. 已有 binding 必须从 PRD Context 原样继承,**不得重新选择**、降级 enforcement 或清空 PRD 阶段 refs/decision/evidence。根据预计实现路径运行 `catalog --stage design`,只对新增 design-stage 候选做增量 bind,并记录 reason。
124
+ 3. 为每条 design-stage required Rule 预建 `Spec Compliance Matrix` 行:`Spec/Rule`、enforcement、设计影响、具体设计落点、验证场景/verifier、状态或 N/A 理由。
125
+ 4. required Rule 没有具体 heading/item 和验证方式时不得进入可评审状态;只写“遵循规范”无效。
126
+ 5. N/A/waiver 必须逐 Rule 请求用户确认并调用 `decision`;`batch` 必须为 false,Agent 不得代确认。无有效确认来源时 fail-closed。
127
+
128
+ > 无 PRD 的新建模式也必须先 catalog/bind 并创建 Context,再写 Design;不能因为缺 PRD 跳过 Specs。
129
+
117
130
  ### 3. 交互式细化
118
131
 
119
132
  围绕选中的维度逐步推进,**每轮提 2-3 个问题**(不要一次性抛 10 个问题):
@@ -164,6 +177,8 @@ CLI 场景:
164
177
  **验收标准**(必选)
165
178
  - 怎么判断做完了?正常 / 异常场景各列至少一条
166
179
  - 可验证、可转为自动化测试断言的条件(明确输入与可观测输出);场景 ID(S-/E-/B-)即测试用例来源
180
+ - 为每个场景指定测试层级(unit / integration / E2E / manual)和关键真实边界;跨 API、存储、运行时生成、渲染或用户可见结果等多个边界时必须选 E2E
181
+ - RULE 与高影响 RISK 必须映射到至少一个 S/E/B 场景;manual 仅限无法自动化的外部条件,必须写明原因
167
182
 
168
183
  **妥协与技术债**(按需)
169
184
  - 本次有意接受的取舍或欠债?后续偿还设想?
@@ -192,7 +207,8 @@ CLI 场景:
192
207
  3. **只包含适用的章节**,模板中有但需求不涉及的章节跳过
193
208
  4. 用 `{}` 占位符替换为实际内容
194
209
  5. PRD 派生模式:功能清单的"来源"列填 PRD 中对应的 US-XX
195
- 6. 展示生成的设计简报
210
+ 6. 生成 `Spec Compliance Matrix`,每条 required Rule 的验证场景必须引用 S-/E-/B- 或说明仅可人工验证的外部边界
211
+ 7. 展示生成的设计简报
196
212
 
197
213
  **章节映射**(Lite 模板,对应 `design-lite.md` 实际章节):
198
214
 
@@ -244,8 +260,10 @@ CLI 场景:
244
260
  **追溯硬约束(plan 拆解与质量门的基础)**:
245
261
  - 功能清单每个 FEAT 保留"来源"列(PRD 派生填 US-XX;无 PRD 填需求描述)
246
262
  - 接口(API/CLI/函数)与验收场景标注其实现 / 覆盖的 FEAT-XX
263
+ - 每个验收场景写明测试层级、关键真实边界和最终可观测结果;编码阶段不得把 E2E 自行降级为 mock 后的 unit/integration 测试
264
+ - 每个 RULE 与高影响 RISK 映射到 S/E/B 场景;只能手动验证时记录原因
247
265
  - Full 模板 §6 需求追溯矩阵必须闭合:US → FEAT → API → TC 不留断点,TC 引用 §2.5.2 场景 ID
248
- - 任一缺口(FEAT 无来源 / FEAT 无验收 / 矩阵断点)在展示时显式列出并提示补全
266
+ - 任一缺口(FEAT 无来源 / FEAT 无验收 / 场景无测试层级或真实边界 / RULE·RISK 无验证场景 / 矩阵断点)在展示时显式列出并提示补全
249
267
 
250
268
  展示后询问用户:**确认写入?可以调整任何部分。**
251
269
 
@@ -267,6 +285,14 @@ CLI 场景:
267
285
  - **全栈需求**:前后端各写一份到同一需求目录
268
286
  4. 用 Write 写入最终路径
269
287
 
288
+ 写入后,对 Matrix 每一行调用 `bind --stage design` 的 `applications` 回填 design artifact、`section_id`、稳定 `item_id` 和脚本计算的 hash;随后执行:
289
+
290
+ ```bash
291
+ python3 .code-flow/scripts/cf_spec_gate.py --task-dir <需求目录> --stage design --json
292
+ ```
293
+
294
+ 只有 Design Gate `decision=pass` 才能输出 Plan 下一步。缺 Matrix 行、artifact ref、验证方式、用户确认或发生 drift 时保留草稿并修复,不得推迟到 Coding/Done 才发现。
295
+
270
296
  ### 6. 输出摘要
271
297
 
272
298
  ```
@@ -277,7 +303,8 @@ CLI 场景:
277
303
  内容:
278
304
  - <按实际包含的章节列出>
279
305
  - 验收标准: N 项
280
- - 追溯自检: 每个 FEAT 有来源(US/需求)✓、有验收场景✓;Full 模板 §6 矩阵 US→FEAT→API→TC 闭合,缺口显式列出
306
+ - 追溯自检: 每个 FEAT 有来源(US/需求)✓、有验收场景✓;每个场景有测试层级和关键真实边界✓;RULE/高影响 RISK 有验证场景✓;Full 模板 §6 矩阵闭合
307
+ - Spec Compliance: required Rules N/N 已落点;Design Gate: pass
281
308
  - 性能自检: 标记性能敏感则方案须体现最优性能设计依据(§3.1/§3.5 或 lite §3.4),否则提示补全或显式声明"无性能敏感点"
282
309
 
283
310
  下一步:
@@ -2,9 +2,10 @@
2
2
  description: 归档已完成的 task 文件
3
3
  ---
4
4
 
5
+
5
6
  # cf-task:archive
6
7
 
7
- 归档已完成的整个 task 文件。归档前执行三维校验,归档后提示更新 specs。
8
+ 归档已完成的整个 task 文件。归档前执行四维校验,归档后提示更新 specs。
8
9
 
9
10
  ## 输入
10
11
 
@@ -36,7 +37,14 @@ description: 归档已完成的 task 文件
36
37
 
37
38
  ### 2. 归档前校验(Verify)
38
39
 
39
- 所有子任务 done 后,执行三维校验:
40
+ **Spec Context / Code Gate(先于四维校验)**:
41
+
42
+ - 需求目录必须存在 `spec-context.yml`;执行 `python3 .code-flow/scripts/cf_spec_context.py refresh --task-dir <需求目录> --root "$PWD" --json`
43
+ - 执行 `python3 .code-flow/scripts/cf_spec_gate.py --task-dir <需求目录> --stage code --json`;required Rule 的 stale/pending/conflict/unverified 任一存在即 FAIL
44
+ - 若 `.code-flow/.active-task.json` 仍指向本需求,必须先完成对应 TASK 的 Done Gate,并以 `{"gate_passed": true}` 调用 `active complete`;不得通过归档绕过 active 状态
45
+ - Context 与 Evidence 随需求目录一并归档,`_session` 仅是可重建投影,不是事实源
46
+
47
+ 所有子任务 done 后,执行四维校验:
40
48
 
41
49
  **完整性**:
42
50
  - 所有 Checklist 项已勾选
@@ -46,6 +54,13 @@ description: 归档已完成的 task 文件
46
54
  - 如果 `.code-flow/validation.yml` 存在,Read 读取验证规则,用 Bash 执行其中匹配的 `command`(如 `npx tsc --noEmit`、`python3 -m pytest` 等)
47
55
  - 检查本次变更涉及的文件是否通过 lint/type check
48
56
 
57
+ **验收追溯**:
58
+ - 来源 design 含结构化 S-/E-/B- 场景时,逐项对照 `## Acceptance Coverage`,P0/P1 场景以及 RULE/高影响 RISK 映射场景必须全部存在且状态为 `verified`
59
+ - 每个负责任务的 `Acceptance-Refs`、`Acceptance Contract`、`Acceptance Evidence` 必须闭合,不得残留 `planned` / `pending` / `TBD`
60
+ - 测试层级不得低于 design;E2E 的真实边界和关键断言必须有文件/用例位置与 fixture/构造证据
61
+ - 汇总并重新执行契约中所有唯一验收命令;测试未收集、未执行或失败均为 FAIL
62
+ - `manual` 场景必须有用户确认和可复核记录。旧 design 没有结构化场景时注明“不适用”,不得伪造覆盖
63
+
49
64
  **一致性**:
50
65
  - 读取 task 文件的 `## Proposal`,对照实际代码变更,检查意图是否已实现
51
66
 
@@ -55,6 +70,7 @@ description: 归档已完成的 task 文件
55
70
  归档前校验:
56
71
  [PASS] 完整性: 所有 Checklist 已完成,无未解决 Notes
57
72
  [PASS] 正确性: cf-validate 通过
73
+ [PASS] 验收追溯: 6/6 场景 verified,2 条 E2E 命令通过
58
74
  [WARN] 一致性: Proposal 提到"支持 OAuth 登录",但未发现相关实现
59
75
 
60
76
  WARN 不阻塞归档,但建议确认后再继续。继续归档?
@@ -82,7 +98,19 @@ mv .code-flow/tasks/<日期>/<需求>/ .code-flow/tasks/archived/<日期>/<需
82
98
  ```
83
99
  3. 同名 `.design.md` 存在则一并移动:`mv .code-flow/tasks/<日期目录>/<file>.design.md .code-flow/tasks/archived/<日期目录>/`
84
100
  4. 同名 `.prd.md` 存在则一并移动:`mv .code-flow/tasks/<日期目录>/<file>.prd.md .code-flow/tasks/archived/<日期目录>/`
85
- 5. 如果原日期目录为空,删除空目录
101
+
102
+ **归档后统一收尾(布局 A/B 都必须执行)**:
103
+
104
+ 1. 保存源日期目录为 `.code-flow/tasks/<日期>/`,完成移动后检查该目录是否还有其他需求或任务。
105
+ 2. 如果目录为空,必须执行安全删除;非空则保留并在摘要中列出剩余条目:
106
+ ```bash
107
+ source_date_dir=".code-flow/tasks/<日期>"
108
+ if [ -d "$source_date_dir" ] && [ -z "$(find "$source_date_dir" -mindepth 1 -maxdepth 1 -print -quit)" ]; then
109
+ rmdir "$source_date_dir"
110
+ fi
111
+ ```
112
+ 3. 复查归档目标存在、原任务/需求路径不存在;如果源日期目录仍存在但为空,视为归档未完成,立即删除后再继续。
113
+ 4. 源日期目录包含其他条目时不得删除,摘要明确写“保留(仍有 N 个条目)”。
86
114
 
87
115
  **临时约束清理(FEAT-08)**:删除 `.code-flow/specs/_session/task-<name>.md`(存在时)。该文件由 cf-task:start 生成,归档后不得残留。
88
116
 
@@ -131,5 +159,6 @@ Spec 同步建议:
131
159
  - 创建日期: 2026-03-15
132
160
  - 归档日期: 2026-03-20
133
161
  - 历时: 5 天
134
- - 校验: 3/3 PASS
162
+ - 校验: 4/4 PASS
163
+ - 源日期目录: 已删除(为空)/ 已保留(仍有 N 个条目)
135
164
  ```
@@ -2,6 +2,7 @@
2
2
  description: 从设计文档拆解需求,生成结构化任务文件
3
3
  ---
4
4
 
5
+
5
6
  # cf-task:plan
6
7
 
7
8
  从设计文档或设计简报拆解需求,生成结构化任务文件。
@@ -157,6 +158,22 @@ AI 从设计文档中识别关键缺口,输出结构化分析并与用户交
157
158
  设计简报模式:记录**章节名**,无需行号。
158
159
  引用格式:`<name>.design.md#3.3 数据设计`, `<name>.design.md#3.4 接口设计` 等(章节名须与模板真实标题一致)。
159
160
 
161
+ **验收契约拆解(硬约束)**:
162
+
163
+ 1. 从全部 design 提取 P0/P1 的 S-/E-/B- 场景,以及 RULE 和高影响 RISK 到场景的映射。
164
+ 2. 优先沿用 design 指定的测试层级与关键真实边界。旧 design 未填写时,跨 API、存储、运行时生成、渲染或最终用户可见结果的场景推断为 `E2E`,局部模块协作推断为 `integration`,纯函数约束才可用 `unit`。
165
+ 3. 每个场景必须分配给一个负责的 TASK,并写入 `Acceptance-Refs`;同一场景可被多个任务引用,但必须有且仅有一个最终验收负责人。
166
+ 4. `manual` 只允许用于无法自动化的外部条件,必须记录原因并经用户明确确认。agent 不得把 design 的 E2E 自行降级为 unit/integration。
167
+ 5. Checklist 禁止使用泛化的“编写测试”。必须写成 `[场景ID][测试层级] + 真实边界 + 关键断言`,并为新功能/缺陷修复安排先写测试和 RED 记录。
168
+ 6. 如果场景缺测试层级、真实边界或 RULE/高影响 RISK 没有验证场景,先以 `#NOTES` 列为设计缺口;未解决前不得开始编码。
169
+
170
+ **Spec 责任拆解(硬约束)**:
171
+
172
+ 1. 先执行 Context `refresh`,读取 Design `Spec Compliance Matrix` 与每个 design-stage/plan-stage Rule 的 `verifier_ref`,继承已有 applied refs,禁止重新选择或降级。
173
+ 2. 每条 plan-stage required Rule 必须有且仅有一个责任 TASK;该 TASK 的 `Spec-Refs` 写完整 `{spec-id}#{rule-ref}`,Checklist 写具体 verifier 命令/输入,Acceptance Contract 写测试层级、不得 Mock 的真实边界和关键断言。
174
+ 3. Rule 可被其他 TASK 引用为依赖,但 Acceptance Coverage 只能有一个最终负责人。E2E 层级**不得降级**;manual 仍仅限经用户确认的外部边界。
175
+ 4. 缺 `verifier_ref`、责任 TASK 为 0/多个、Checklist/Contract 不含 Rule-specific 验证责任时,任务文件只能保留为缺口草稿,不能启动。
176
+
160
177
  ### 4. 生成任务文件方案
161
178
 
162
179
  将拆解结果按以下格式组织,展示给用户确认:
@@ -187,12 +204,25 @@ AI 从设计文档中识别关键缺口,输出结构化分析并与用户交
187
204
 
188
205
  ---
189
206
 
207
+ ## Acceptance Coverage
208
+
209
+ | 场景ID | 来源设计 | 测试层级 | 关键真实边界 | 负责任务 | 状态 |
210
+ |--------|---------|---------|-------------|---------|------|
211
+ | S-01 | xxx.design.md#2.5 验收条件 | E2E | API → Store → Renderer | TASK-001 | planned |
212
+ | E-01 | xxx.design.md#2.5 验收条件 | integration | Service → Store | TASK-001 | planned |
213
+
214
+ > 本表必须覆盖 design 中全部 P0/P1 场景,以及 RULE/高影响 RISK 映射的场景;存在缺口时不生成可启动任务。
215
+
216
+ ---
217
+
190
218
  ## TASK-001: <子任务标题>
191
219
 
192
220
  - **Status**: draft
193
221
  - **Priority**: P0
194
222
  - **Depends**:
195
223
  - **Source**: docs/xxx.md#§3.1 数据模型(L83-L110)
224
+ - **Spec-Refs**: product-rules#RULE-product-001
225
+ - **Acceptance-Refs**: S-01, E-01, RULE-01
196
226
 
197
227
  ### Description
198
228
  <从设计文档提取的需求重点,不必复制全文>
@@ -200,7 +230,21 @@ AI 从设计文档中识别关键缺口,输出结构化分析并与用户交
200
230
  ### Checklist
201
231
  - [ ] <具体实现步骤1>
202
232
  - [ ] <具体实现步骤2>
203
- - [ ] <编写测试>
233
+ - [ ] [S-01][E2E] 修改生产代码前,按 API → Store → Renderer 真实边界编写验收测试并记录 RED
234
+ - [ ] [S-01] 断言 <最终可观测结果 1> 与 <最终可观测结果 2>
235
+ - [ ] [E-01][integration] 覆盖 <异常输入> 与 <可观测失败行为>
236
+ - [ ] 运行验收命令并填写 Acceptance Evidence
237
+
238
+ ### Acceptance Contract
239
+
240
+ | 场景ID | 测试层级 | 不得 Mock 的真实边界 | 关键断言 | 测试文件 / 用例 | 执行命令 | 状态 |
241
+ |--------|---------|--------------------|---------|----------------|---------|------|
242
+ | S-01 | E2E | API、Store、Renderer | <断言列表> | planned | planned | planned |
243
+ | E-01 | integration | Service、Store | <断言列表> | planned | planned | planned |
244
+
245
+ ### Acceptance Evidence
246
+
247
+ > `cf-task-start` 在编码期填写 RED/GREEN 结果、每个关键断言的位置和真实组件证据;全部状态 verified 后任务才可 done。
204
248
 
205
249
  ### Log
206
250
  - [<当前日期>] created (draft)
@@ -225,6 +269,7 @@ AI 从设计文档中识别关键缺口,输出结构化分析并与用户交
225
269
 
226
270
  TASK-001: <标题> [P0]
227
271
  来源: §3.1 数据模型 (L83-L110)
272
+ 验收: S-01(E2E), E-01(integration)
228
273
  描述: ...
229
274
  Checklist: N 项
230
275
  依赖: 无
@@ -252,6 +297,14 @@ TASK-002: <标题> [P1]
252
297
  3. 如果输入为 docs/ 设计文档(无需求目录)→ 按当前日期创建需求目录 `.code-flow/tasks/<YYYY-MM-DD>/<需求>/` 并写入其中
253
298
  4. 用 Write 写入
254
299
 
300
+ 写入后用 `bind --stage plan` 的 `applications` 将每条 required Rule 指向任务文件内唯一 TASK item,并执行:
301
+
302
+ ```bash
303
+ python3 .code-flow/scripts/cf_spec_gate.py --task-dir <需求目录> --stage plan --artifact <任务文件> --json
304
+ ```
305
+
306
+ 只有 Context Plan 状态与任务结构校验都 `decision=pass` 才输出 Start 下一步;缺唯一 owner、`verifier_ref`、测试层级或真实边界时必须回到拆解修复。
307
+
255
308
  ### 6. 输出摘要
256
309
 
257
310
  ```
@@ -260,6 +313,7 @@ TASK-002: <标题> [P1]
260
313
  - P0: x 个, P1: y 个, P2: z 个
261
314
  - 依赖链深度: N 层
262
315
  - 详设引用覆盖: §3.1, §3.2, §3.3, §3.5 (共 4 个章节)
316
+ - 验收覆盖: P0/P1 场景 x/x,RULE/高影响 RISK 映射 y/y,E2E 场景 z 个
263
317
 
264
318
  建议执行顺序:
265
319
  1. TASK-001 (无依赖)
@@ -2,6 +2,7 @@
2
2
  description: 从一句话需求出发产生产品需求文档(PRD)
3
3
  ---
4
4
 
5
+
5
6
  # cf-task:prd
6
7
 
7
8
  从一句话需求出发,通过结构化对话产出产品需求文档(PRD),为 `cf-task:align` 提供足够丰富的输入。
@@ -38,6 +39,22 @@ description: 从一句话需求出发产生产品需求文档(PRD)
38
39
 
39
40
  目的:**让 PRD 基于实际业务背景**,而非泛泛的通用描述。本步只读不提问,所有提问统一放到 Step 3。
40
41
 
42
+ ### 2.1 先解析并绑定 Existing Specs(硬门禁)
43
+
44
+ 在开始写需求内容前,先确定 Step 5 的需求目录和预计涉及路径,再执行:
45
+
46
+ ```bash
47
+ python3 .code-flow/scripts/cf_spec_context.py catalog --stage prd --root "$PWD" --paths <预计路径...> --json
48
+ ```
49
+
50
+ 1. 必须检查完整 `candidates`,不得按展示条数截断;只消费 resolver 返回的 `stages` 含 `prd` 的产品、安全、兼容、业务约束,`code-only` Spec 不得进入 PRD。
51
+ 2. 对每个适用候选记录 `selected_by` 和具体 `reason`,立即用 `bind --stage prd` 写入需求目录的 `spec-context.yml`(首次 `applications` 为空),然后再进入需求细化。
52
+ 3. required Rule 必须进入 PRD 的 `Existing Spec Constraints`,并明确影响哪个范围项、FEAT 验收或 NFR;只复制一句“遵循规范”不算承接。
53
+ 4. 不适用的 required Rule 必须逐 Rule 说明具体理由并请求用户确认,再调用 `decision` 保存 `not_applicable`;禁止批量 N/A,**Agent 不得代确认**。
54
+ 5. Catalog/Context 失败、同级 required 冲突或 required Rule 尚未决定时 fail-closed:保留草稿并修复,不得绕过后继续产出可批准 PRD。
55
+
56
+ > 预计路径尚不明确时仍须以空 `--paths` 运行,确保 global/task-stage 规则先进入 Context;后续范围明确时重新 catalog,并补充绑定新增候选。
57
+
41
58
  ### 3. 交互式细化
42
59
 
43
60
  围绕 PRD 各要素逐步推进,**每轮提 2-3 个问题**:
@@ -98,6 +115,7 @@ description: 从一句话需求出发产生产品需求文档(PRD)
98
115
  | 5. 非功能需求 | Step 3 的"非功能需求"(按需,分配 NFR 编号) |
99
116
  | 6. 范围与边界 | Step 3 的"范围与边界" |
100
117
  | 7. 依赖与风险 | Step 3 的"依赖与风险"(按需) |
118
+ | 8. Existing Spec Constraints | Step 2.1 的 prd-stage Rule;逐条关联范围/FEAT 验收/NFR |
101
119
 
102
120
  **编号与追溯(硬约束,`cf-task:align` 派生的基础)**:
103
121
  - 用户故事按出现顺序分配 `US-01`、`US-02`…,写入 §3.2 用户故事表
@@ -120,6 +138,12 @@ description: 从一句话需求出发产生产品需求文档(PRD)
120
138
  3. **重名检查**:需求目录已存在且非本次草稿时,目录名追加序号 `<name>-2`、`<name>-3`… 避免覆盖;恢复模式复用原目录、不改名
121
139
  4. 用 Write 写入 `.code-flow/tasks/<YYYY-MM-DD>/<name>/<name>.prd.md`
122
140
 
141
+ 写入后必须完成 Context 回填和 PRD Gate:
142
+
143
+ 1. 再次调用 `bind --stage prd`,`selections` 可为空,`applications` 为每条已承接 Rule 写入 `artifact`、`section_id`、`item_id`;artifact 必须指向本需求目录内 PRD,由脚本计算 `artifact_sha256`。
144
+ 2. 执行 `python3 .code-flow/scripts/cf_spec_gate.py --task-dir <需求目录> --stage prd --json`。
145
+ 3. 只有 `decision=pass` 才能把 PRD 标为可评审并输出下一步;pending/conflict/stale、缺少范围/验收落点或无有效用户确认的 N/A 均保持阻断。
146
+
123
147
  **文件位置说明**:PRD、设计简报(`<name>.frontend.design.md` / `<name>.backend.design.md`)、任务文件(`<name>.md`)同放**需求目录** `.code-flow/tasks/<日期>/<name>/`,`cf-task:archive` 按整个需求目录归档。
124
148
 
125
149
  ### 6. 输出摘要
@@ -132,6 +156,7 @@ description: 从一句话需求出发产生产品需求文档(PRD)
132
156
  - 用户故事: N 个
133
157
  - 功能: N 个(P0: X, P1: Y)
134
158
  - 追溯自检: FEAT↔US 全部关联 ✓ | 有缺口则列出「未关联 FEAT-XX / 未覆盖 US-XX」
159
+ - Existing Specs: N 个 / required Rules: N 条 / PRD Gate: pass
135
160
 
136
161
  > 自检发现缺口(FEAT 无来源 US 或 US 无 FEAT 覆盖)时,在摘要显式标注并提示用户补 US 或降级,不要静默放过。
137
162
 
@@ -2,6 +2,7 @@
2
2
  description: 激活子任务并开始编码
3
3
  ---
4
4
 
5
+
5
6
  # cf-task:start
6
7
 
7
8
  激活子任务并开始编码。支持单任务模式和整文件模式。
@@ -27,7 +28,7 @@ description: 激活子任务并开始编码
27
28
 
28
29
  **状态检查**:Status 必须为 `draft`。若为其他状态:
29
30
  - `in-progress` → 提示"任务已在进行中,继续编码"(不阻塞,直接跳到步骤 2)
30
- - `done` → 提示"任务已完成",结束
31
+ - `done` → 先执行验收契约检查;全部 verified 才提示"任务已完成"并结束,否则恢复为 `in-progress`,列出缺口并继续补齐测试与证据
31
32
  - `blocked` → 提示"任务被阻塞",列出 Notes 中的阻塞原因,结束
32
33
 
33
34
  **#NOTES 检查**:扫描该子任务段落全文(Description、Checklist 等)
@@ -39,6 +40,12 @@ description: 激活子任务并开始编码
39
40
  - 所有依赖必须为 `done`
40
41
  - 未满足 → 输出:`前置检查失败:以下依赖未完成\n- TASK-001: in-progress\n- TASK-003: draft`
41
42
 
43
+ **验收契约检查**:
44
+ - 如果 task 含 `## Acceptance Coverage`,当前子任务必须有 `Acceptance-Refs`、`Acceptance Contract` 和 `Acceptance Evidence`
45
+ - `Acceptance-Refs` 中每个 S-/E-/B- 都必须出现在全局覆盖表和当前契约中,测试层级与关键真实边界必须和 design 一致
46
+ - 旧 task 没有覆盖表但来源 design 含结构化验收场景时,先从 design 回填覆盖表与契约;回填完成前不得修改生产代码
47
+ - 非行为类任务可显式写 `Acceptance-Refs: N/A`;不得用 N/A 跳过已有设计场景
48
+
42
49
  ### 2. 加载详设上下文
43
50
 
44
51
  前置检查通过后,**编码前先加载关联的详设文档章节**:
@@ -48,39 +55,75 @@ description: 激活子任务并开始编码
48
55
  - 提取:文件路径 + 行号范围
49
56
  2. 用 Read 按行号范围读取详设文档的对应章节(使用 offset/limit 参数)
50
57
  3. 将读取的章节内容作为编码上下文,与 Checklist 一起指导实现
58
+ 4. 按 `Acceptance-Refs` 精确读取验收场景及其关联 RULE/RISK;不得只依赖摘要,也不得遗漏场景的测试层级、关键真实边界和预期结果
51
59
 
52
60
  示例:Source 为 `docs/auth.md#§3.2 API 接口(L111-L155), docs/auth.md#§3.5 错误码(L201-L220)`
53
61
  → Read `docs/auth.md` offset=111 limit=45
54
62
  → Read `docs/auth.md` offset=201 limit=20
55
63
 
56
- ### 3. 激活并编码
64
+ ### 3. 激活并准备验收测试
57
65
 
58
- 1. 用 Edit 更新子任务 Status 为 `in-progress`
59
- 2. 在 `### Log` 追加:`- [<当前日期>] started (in-progress)`
60
- 3. 更新文件头 `Updated` 日期
61
- 4. 结合详设上下文 + Checklist,逐项执行编码工作
62
- 5. 每完成一个 checklist 项 → 用 Edit 将 `- [ ]` 改为 `- [x]`
66
+ 在改状态或生产代码前,顺序固定且不得跳步:
67
+
68
+ 1. 调用 `cf_spec_context.py refresh --task-dir ...`,执行 Start Gate;stale/pending/conflict 或依赖未闭合均不得继续。
69
+ 2. 重新读取 refresh 后的 Context hash,再调用 `cf_spec_context.py active start`,传 task/context hash 和逐路径确认的 pre-existing ownership;已有/损坏 marker、未归属 diff 或 hash 不一致立即阻断。禁止先 start 再 refresh,避免 active marker 在编码前自行漂移。
70
+ 3. 调用 `cf_spec_session.py`,只根据当前 TASK 的 `Spec-Refs`、Source 与 Acceptance Contract 覆盖写入 `_session/task-<name>.md`。禁止重新 catalog 或猜测规则。
71
+ 4. 只有前三步全部成功,才用 Edit 更新子任务 Status 为 `in-progress`、追加 started log 并更新文件头日期。
72
+ 5. 在修改任何生产代码前,为每个 Acceptance-Ref 填写测试文件、包含场景 ID 的测试用例名和可单独执行的命令
73
+ 6. 先编写验收测试。E2E 测试必须经过契约声明的真实边界,不得用 mock 绕过 Store、Resolver、Builder、Renderer、Browser 等指定组件
74
+ 7. 新功能或缺陷修复先执行一次测试并记录 RED:失败命令、失败用例和与预期缺陷对应的失败原因。纯重构或已有行为补测无法 RED 时,记录原因,不得伪造失败
75
+
76
+ RED 证据写入 `Acceptance Evidence`:
77
+
78
+ ```markdown
79
+ | 场景ID | RED | GREEN | 断言位置 | 真实边界证据 | 状态 |
80
+ |--------|-----|-------|---------|-------------|------|
81
+ | S-01 | FAIL: generation 未递增 | pending | pending | real Store + Renderer fixture | test-written |
82
+ ```
63
83
 
64
- ### 3.5 会话级临时约束(FEAT-08)
84
+ ### 3.1 实现与逐项更新
85
+
86
+ 1. RED 证据就绪后,结合详设上下文、Acceptance Contract 与 Checklist 修改生产代码
87
+ 2. 每完成一个 checklist 项,用 Edit 将 `- [ ]` 改为 `- [x]`
88
+ 3. 如果实现中发现必须改变测试层级或真实边界,停止编码并记录 `#NOTES`,经用户确认并同步 design 后才能继续;agent 不得自行降级
89
+
90
+ ### 3.2 GREEN 与验收证据
91
+
92
+ 1. 执行每个契约中的验收命令,再执行受影响范围的回归测试
93
+ 2. 对每个预期结果记录对应的测试断言位置,并指出 fixture/构造路径如何证明关键边界使用真实组件
94
+ 3. 将 `Acceptance Contract` 行状态改为 `verified`,将 `Acceptance Evidence` 补齐 GREEN、断言位置和边界证据
95
+ 4. 负责该场景的任务验证完成后,将全局 `Acceptance Coverage` 对应行改为 `verified`
96
+ 5. 测试文件存在但未被测试框架收集、命令未实际执行、只有场景 ID 没有关键断言,都视为未验证
97
+
98
+ ### 3.5 TASK-bound Spec Session
65
99
 
66
100
  激活后,若任务文件头 Source 指向的 design 文档含验收条件章节(如 §2.5 验收条件 / 验收标准):
67
101
 
68
- 1. 提取该章节的场景与 RULE 约束,生成 `.code-flow/specs/_session/task-<name>.md`:
102
+ 1. 只提取当前任务 `Spec-Refs` 与 `Acceptance-Refs` 对应的 Rule hash、verifier、artifact refs、场景、测试层级和真实边界,生成 `.code-flow/specs/_session/task-<name>.md`:
69
103
  - frontmatter:`description: 当前任务 <name> 的验收约束(cf-task:start 生成,archive 清理)`
70
- - 正文:验收场景与约束的精简列表(≤300 token)
71
- 2. Spec Catalog 目录扫描自动纳入该文件,无需配置;编辑代码时的自动注入同样生效
72
- 3. design 无验收章节时静默跳过;同名文件已存在则覆盖(同任务重复 start)
104
+ - 正文:验收场景与约束的精简列表(≤300 token);必须保留全部引用 ID、层级、边界和预期结果,不能为压缩而删除场景
105
+ 2. 任务模式直接读取这个投影,禁止经 Spec Catalog 二次选择;同名文件已存在则原子覆盖。
106
+ 3. 50 Rule 等超预算任务按 required/当前阶段优先输出受控摘要,完整 Context 保留不丢失,并明确提示拆 TASK;不得用截断隐藏 required 缺口。
73
107
 
74
108
  > `_session/` 不入库(.gitignore 模板已覆盖)、不参与规范审计与预算。
75
109
 
76
110
  ### 4. 自动完成
77
111
 
78
- 当所有 checklist 项都勾选为 `[x]` 后:
112
+ 只有同时满足以下条件才能自动完成:
113
+ - 所有 checklist 项均为 `[x]`
114
+ - 每个 Acceptance-Ref 在契约、证据和全局覆盖表中均为 `verified`,不存在 `planned` / `pending` / `TBD`
115
+ - 测试层级未低于 design,关键真实边界没有被 mock 绕过
116
+ - 每个预期结果都有具体断言位置,验收命令和回归测试均已实际通过
117
+ - `manual` 场景已有用户确认和可复核记录
118
+
119
+ 满足后:
79
120
  1. 用 Edit 更新 Status 为 `done`
80
121
  2. 在 `### Log` 追加:`- [<当前日期>] completed (done)`
81
122
  3. 更新文件头 `Updated` 日期
82
123
  4. 输出:`TASK-xxx 已完成`
83
124
 
125
+ 任一验收条件不满足时保持 `in-progress`,明确列出缺口,不能标记为 `done`。
126
+
84
127
  ### 5. 文档同步检查
85
128
 
86
129
  子任务完成后,轻量检查本次编码是否引入了需要同步到 specs 或导航地图的内容:
@@ -148,7 +191,7 @@ Spec 同步提示:
148
191
 
149
192
  ### 4. 按序执行
150
193
 
151
- 对每个可激活的子任务,执行单任务模式的步骤 3-4(详设已在步骤 2 加载,无需重复读取)。
194
+ 对每个可激活的子任务,执行单任务模式的步骤 3-4,包括先写验收测试、RED、实现、GREEN 和证据核对(详设已在步骤 2 加载,无需重复读取)。
152
195
 
153
196
  完成一个子任务后,检查是否解锁了新的子任务(依赖已满足),如果是则继续执行。
154
197
 
@@ -60,7 +60,6 @@ export const CodeFlow = async (ctx) => {
60
60
  const sid = input.event?.properties?.info?.id || "";
61
61
  debugLog(projectRoot, `session.created sid=${sid}`);
62
62
  if (sid) sessionContext.delete(sid);
63
- callHook(projectRoot, "cf_session_hook.py", { session_id: sid });
64
63
  }
65
64
  if (input.event?.type === "session.idle") {
66
65
  const sid =
package/src/cli.js CHANGED
@@ -7,9 +7,11 @@ const path = require('path');
7
7
  const { spawnSync } = require('child_process');
8
8
 
9
9
  const pkg = require('../package.json');
10
+ const specWorkflowMigration = require('./migrate/spec-workflow');
10
11
 
11
12
  const usage = [
12
13
  'Usage: code-flow init [--force] [--platform=<claude|codex|costrict|opencode>]',
14
+ ' code-flow migrate --spec-workflow <--dry-run|--prepare|--apply --plan <path>|--rollback <id>>',
13
15
  ' code-flow -v | --version',
14
16
  ' code-flow -h | --help'
15
17
  ].join('\n');
@@ -469,15 +471,19 @@ function parsePlatform(args) {
469
471
  // --- Main init ---
470
472
 
471
473
  function runInit(force, platform) {
474
+ const cwd = process.cwd();
475
+ const installedVersion = readInstalledVersion(cwd);
476
+ if (installedVersion && compareVersions(installedVersion, '0.4.2') >= 0 && compareVersions(installedVersion, '0.6.0') < 0) {
477
+ process.stdout.write(`${JSON.stringify({ status: 'migration_required', version: installedVersion, command: 'code-flow migrate --spec-workflow --dry-run' })}\n`);
478
+ process.exit(3);
479
+ }
472
480
  ensurePython3();
473
481
 
474
- const cwd = process.cwd();
475
482
  const baseDir = __dirname;
476
483
  const coreDir = path.join(baseDir, 'core');
477
484
  const adaptersDir = path.join(baseDir, 'adapters');
478
485
 
479
486
  // Determine mode
480
- const installedVersion = readInstalledVersion(cwd);
481
487
  let mode;
482
488
  if (force) {
483
489
  mode = 'force';
@@ -778,6 +784,45 @@ function runInit(force, platform) {
778
784
 
779
785
  const args = process.argv.slice(2);
780
786
 
787
+ function argumentValue(values, name) {
788
+ const index = values.indexOf(name);
789
+ return index >= 0 ? values[index + 1] || '' : '';
790
+ }
791
+
792
+ function migrationExitCode(result) {
793
+ if (result.status === 'rolled_back') return 4;
794
+ if (result.status === 'recovery_required') return 5;
795
+ if (result.status === 'prepared_blocked') return 3;
796
+ return 0;
797
+ }
798
+
799
+ function runMigrate(values) {
800
+ if (!values.includes('--spec-workflow')) fail('Error: migrate requires --spec-workflow.');
801
+ const actions = ['--dry-run', '--prepare', '--apply', '--rollback'].filter(action => values.includes(action));
802
+ if (actions.length !== 1) fail('Error: choose exactly one migrate action.');
803
+ let result;
804
+ try {
805
+ if (actions[0] === '--dry-run') result = specWorkflowMigration.preview(process.cwd());
806
+ if (actions[0] === '--prepare') result = specWorkflowMigration.prepare(process.cwd());
807
+ if (actions[0] === '--apply') {
808
+ const plan = argumentValue(values, '--plan');
809
+ if (!plan) fail('Error: --apply requires --plan <path>.');
810
+ result = specWorkflowMigration.apply(path.resolve(plan));
811
+ }
812
+ if (actions[0] === '--rollback') {
813
+ const id = argumentValue(values, '--rollback');
814
+ if (!id) fail('Error: --rollback requires a migration id.');
815
+ const plan = path.join(process.cwd(), '.code-flow', 'migrations', id, 'migration-plan.json');
816
+ result = specWorkflowMigration.rollback(plan);
817
+ }
818
+ process.stdout.write(`${JSON.stringify(result)}\n`);
819
+ process.exit(migrationExitCode(result));
820
+ } catch (error) {
821
+ process.stdout.write(`${JSON.stringify({ status: 'unresolved', error: String(error.message || error) })}\n`);
822
+ process.exit(3);
823
+ }
824
+ }
825
+
781
826
  if (args.includes('-v') || args.includes('--version')) {
782
827
  process.stdout.write(`${pkg.version}\n`);
783
828
  process.exit(0);
@@ -799,6 +844,10 @@ if (args[0] === 'init') {
799
844
  runInit(force, platform);
800
845
  }
801
846
 
847
+ if (args[0] === 'migrate') {
848
+ runMigrate(args.slice(1));
849
+ }
850
+
802
851
  if (args.length === 0) {
803
852
  fail('Error: missing command.');
804
853
  }
@@ -1,5 +1,14 @@
1
1
  version: 1
2
2
 
3
+ spec_workflow:
4
+ schema_version: 1
5
+ enforcement: required
6
+ drift_check: true
7
+ scope_expansion_check: true
8
+ metrics: true
9
+ catalog:
10
+ dedup_window: 5
11
+
3
12
  budget:
4
13
  total: 2500
5
14
  l0_max: 800
@@ -8,17 +17,6 @@ budget:
8
17
  # catalog 模式下 spec 目录的 token 上限(超出按 Tier1 优先截断并告警)
9
18
  catalog_max: 200
10
19
 
11
- inject:
12
- auto: true
13
- # 注入模式:
14
- # catalog — UserPromptSubmit 仅注入极小 spec 目录(每 spec 一行适用场景),
15
- # 由模型按语义自行 Read 相关 spec 全文;prompt 含明确文件路径且
16
- # 命中 domain 时仍直接注入完整约束。PreToolUse 不受影响(编辑时
17
- # 始终强制注入完整约束兜底)。
18
- # full — 旧行为:UserPromptSubmit 按 tag 词表匹配注入完整 spec。
19
- # 仅 literal "catalog" 启用目录模式;缺失/其他值一律 full(升级用户行为不变)。
20
- mode: catalog
21
-
22
20
  # 质量闭环(v0.5):合规反馈 + 学习采集。
23
21
  # enabled 仅 literal true 启用(缺失即关闭,升级用户行为不变);
24
22
  # 子开关缺省跟随 enabled,仅 literal false 单独关闭。
@@ -31,11 +29,6 @@ quality_loop:
31
29
  # 注入时对 spec 内容做保守无损压缩(去行尾空白、折叠多空行、剥 HTML 注释、去重 bullet)
32
30
  # 缺省视为 true;仅显式 false 关闭
33
31
  compress: true
34
- # UserPromptSubmit 去重窗口:同一 spec 在最近 N 个 prompt 内不重复注入。
35
- # 0 或负数 关闭去重(每次都注入,等同旧行为);缺省 5。
36
- # 窗口"限速"而非"永久跳过",超过 N 个 prompt 后会再次注入,保证 auto-compaction
37
- # 丢掉早期上下文后仍能恢复 spec。
38
- dedup_window: 5
39
32
  code_extensions:
40
33
  - ".py"
41
34
  - ".ts"
@@ -73,7 +66,9 @@ quality_loop:
73
66
  - "*.config.*"
74
67
  - ".code-flow/**"
75
68
  - ".claude/**"
69
+ - ".codex/**"
76
70
  - ".costrict/**"
71
+ - ".opencode/**"
77
72
  - "node_modules/**"
78
73
  - "dist/**"
79
74
  - "build/**"
@@ -112,6 +107,8 @@ path_mapping:
112
107
  - "src/components/**"
113
108
  - "src/pages/**"
114
109
  - "src/hooks/**"
110
+ - "src/composables/**"
111
+ - "src/services/**"
115
112
  - "src/styles/**"
116
113
  - "**/*.tsx"
117
114
  - "**/*.jsx"
@@ -16,6 +16,7 @@ import fnmatch
16
16
  import os
17
17
  import re
18
18
  import sys
19
+ from typing import Mapping
19
20
  from concurrent.futures import ThreadPoolExecutor
20
21
  from concurrent.futures import TimeoutError as FutureTimeout
21
22
 
@@ -304,3 +305,32 @@ def run_checks(
304
305
  "severity": check["severity"],
305
306
  })
306
307
  return violations, skipped
308
+
309
+
310
+ def run_regex_verifier(
311
+ pattern: str,
312
+ files: str,
313
+ contents: Mapping[str, str],
314
+ timeout: float = CHECK_TIMEOUT_SECONDS,
315
+ ) -> tuple[list[dict[str, object]], list[dict[str, object]]]:
316
+ """Execute one verifier regex over explicit file contents."""
317
+ try:
318
+ compiled = re.compile(pattern)
319
+ except re.error as exc:
320
+ return [], [{"check_id": "verifier", "reason": f"invalid_regex:{exc}"}]
321
+ check = {
322
+ "id": "verifier",
323
+ "type": "regex",
324
+ "pattern": pattern,
325
+ "regex": compiled,
326
+ "files": files,
327
+ "message": "Rule verifier matched forbidden content",
328
+ "severity": "warn",
329
+ }
330
+ violations: list[dict[str, object]] = []
331
+ skipped: list[dict[str, object]] = []
332
+ for relative, content in contents.items():
333
+ file_violations, file_skipped = run_checks([check], relative, content, timeout=timeout)
334
+ violations.extend(file_violations)
335
+ skipped.extend(file_skipped)
336
+ return violations, skipped