agent-project-sdlc 0.1.9 → 0.1.10

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
@@ -25,8 +25,9 @@ npx sdlc-harness init --adopt
25
25
  | Managed file sync | `npx sdlc-harness sync` | Materializes package canonical assets into the configured Harness root while preserving project state, docs and local overrides. |
26
26
  | Upgrade | `npx sdlc-harness upgrade` | Runs migrations and sync for already-adopted projects. |
27
27
  | Diagnostics | `npx sdlc-harness doctor` | Reports Harness root, package version, schema version and key managed paths. |
28
- | Validators | `npx sdlc-harness validate-*`, `make validate-current`, `make validate-harness` | Checks phase deliverables, active plan shape, prompt language contract and generated overview freshness. |
28
+ | Validators | `npx sdlc-harness validate-*`, `make validate-current`, `make validate-harness` | Checks product, design, development, review, test, release, RFC, active plan shape, prompt language contract and generated overview freshness. |
29
29
  | Lifecycle workflow | `<harnessRoot>/state/lifecycle.yaml`, `<harnessRoot>/state/plan.yaml`, `.docs/**` | Tracks REQUIREMENT_GATHERING, ARCHITECTING, SPRINTING, REVIEWING, TESTING, RELEASING and RFC_RECALIBRATION facts. |
30
+ | Stage task control | `plan.yaml`, `make validate-plan`, `npx sdlc-harness validate-plan` | Keeps each stage's agent work in small `TASK-*` tasks with `phase` metadata and scoped paths/gates. |
30
31
  | Natural-language control | `AGENTS.md` plus workflow skills | Lets users say things like "continue", "start development", "run tests" or "requirements changed"; agents map these to workflow actions. |
31
32
  | Optional parallel execution contract | `plan.yaml#parallel_execution` | Enabled only when users explicitly request multi-agent, parallel or multi-worktree execution; supports runtime-managed subagents or user-orchestrated worker prompts. |
32
33
  | Workflow skills | `<harnessRoot>/skills/pjsdlc_*/SKILL.md` | Provides role prompts for product, architecture, development, implementation docs, review, testing, release and RFC recalibration. |
@@ -69,6 +70,10 @@ The default workflow is serial. Agents should only create `parallel_execution` i
69
70
 
70
71
  The CLI does not promise to automatically launch Codex agents. Workers do not need to communicate with each other; the main agent owns final fact-source updates such as PRD, plan, implementation docs, test results and generated overviews.
71
72
 
73
+ ## Stage Task Control
74
+
75
+ Every stage's agent work is plan-controlled. Conversational PRD or design creation, existing document slicing, fact-source-based synthesis, development, review, testing, release preparation and RFC recalibration should create or resume one small `TASK-*` task in `plan.yaml` with a valid `phase`, write the current task's `result_docs` or `implementation_doc`, update indexes/overviews, run `validate-plan`, and remove the task after completion. Phase exit validators reject remaining open tasks.
76
+
72
77
  ## Common Commands
73
78
 
74
79
  ```sh
@@ -77,6 +82,11 @@ npx sdlc-harness init --adopt
77
82
  npx sdlc-harness sync
78
83
  npx sdlc-harness upgrade
79
84
  npx sdlc-harness doctor
85
+ npx sdlc-harness validate-plan
86
+ npx sdlc-harness validate-review
87
+ npx sdlc-harness validate-test
88
+ npx sdlc-harness validate-release
89
+ npx sdlc-harness validate-rfc
80
90
  make validate-current
81
91
  make validate-harness
82
92
  make docs-overview
@@ -30,9 +30,11 @@
30
30
 
31
31
  ## Plan Protocol
32
32
 
33
- - `plan.yaml` 是当前和未来任务的短期执行计划事实源。open task 直接包含 `allowed_paths`、`required_gates`、`acceptance_criteria` 和必要的 `working_notes`。
34
- - `next_task_sequence` 记录下一个可分配的 `DEV-*` 序号,避免删除历史 task 后发生 id 冲突。
35
- - task 完成并写入或更新相关 implementation doc 后,从 `plan.yaml` `tasks` 列表移除该 task;不要长期保留 done/cancelled task 摘要。
33
+ - `plan.yaml` 是当前和未来任务的短期执行计划事实源。每个阶段的每个 Agent 主任务都应先检查是否过长,必要时拆成大小合适的 open task;open task 直接包含 `phase`、`allowed_paths`、`required_gates`、`acceptance_criteria` 和必要的 `working_notes`。
34
+ - 新建任务统一使用 `TASK-*` id,并通过 `phase` 标明属于 `REQUIREMENT_GATHERING`、`ARCHITECTING`、`SPRINTING`、`REVIEWING`、`TESTING`、`RELEASING` `RFC_RECALIBRATION`;历史 `PRD-*`、`DES-*`、`DEV-*` 只作为兼容旧记录和旧提交的 provenance。
35
+ - `next_task_sequence` 记录下一个可分配的 `TASK-*` 序号,避免删除历史 task 后发生 id 冲突。
36
+ - 文档、Review、测试、发布和 RFC 类 task 使用 `result_docs` 指向本 task 产出的 PRD、architecture、tech plan、ADR、review report、test plan、release note、RFC 或 `plan.draft.yaml`;开发 task 使用 `implementation_doc` 指向模块级实现事实。
37
+ - task 完成并写入或更新相关事实源后,从 `plan.yaml` 的 `tasks` 列表移除该 task;不要长期保留 done/cancelled task 摘要。
36
38
  - `plan.draft.yaml` 是架构阶段生成的计划草案,不自动覆盖 `plan.yaml`。
37
39
  - 不维护 checkpoint 文件;任务现场只存在于 open task 的 plan 条目里。
38
40
  - 历史动作记录以 git commit 为准,产物结果以模块、子系统或核心数据流级 implementation doc 为准。
@@ -133,8 +135,8 @@ Strong success criteria 可以让你 independent loop。Weak criteria,例如
133
135
  1. 选择任何角色或 skill 前,先读取 `.codex/state/lifecycle.yaml`。
134
136
  2. 除非用户明确要求其它工作流动作,否则使用 `active_skill` 指定的 workflow skill。
135
137
  3. 产品文档和技术方案未形成前,不写业务代码。
136
- 4. `SPRINTING` 阶段,一次只执行一个任务。
137
- 5. `SPRINTING` 阶段,只编辑当前 open task 的 `allowed_paths` 允许的文件。
138
+ 4. 每个阶段一次只执行一个 open task;如果任务过长,先拆成多个 `TASK-*`,当前轮只推进 `current_task_id`。
139
+ 5. 每个阶段只编辑当前 open task 的 `allowed_paths` 允许的文件;只读角色仍不得修改源码。
138
140
  6. 不要在当前 open task 的 `required_gates` 通过前把任务标记为 `done`。
139
141
  7. 代码 gate 通过后,更新相关 implementation doc 和 `.docs/INDEX.md`。
140
142
  8. `reviewer` 角色只读,不直接修改源码。
@@ -142,7 +144,7 @@ Strong success criteria 可以让你 independent loop。Weak criteria,例如
142
144
  10. task/release 历史动作记录使用 git commit、tag 或外部 release 系统,不维护 `<harnessRoot>/archive/` 常规归档。
143
145
  11. 在 `SPRINTING` 阶段,task 完成闭环必须先创建 task implementation commit,再提交移除该 task 后的 task completion ledger commit;如果没有 remote/upstream、权限或凭证导致无法 push,不要开始下一个 task,先报告 blocker。
144
146
  12. 文档 slice 发生变化后,运行 `make docs-overview` 刷新对应 `overview.md`。
145
- 13. open task 必须在 `plan.yaml` 中包含完整执行合同,再继续推进或交接。
147
+ 13. open task 必须在 `plan.yaml` 中包含完整执行合同,再继续推进或交接;方案生成、现有文档切片、基于上一阶段事实源生成、Review、测试、发布和 RFC 处理都必须先落到 `plan.yaml` task。
146
148
  14. 默认不读取过去 task 或 phase 执行流水;修 bug、补功能和阶段交付以当前代码、测试、PRD、技术方案和模块级 implementation doc 为准。
147
149
  15. gate 证据写入当前 task `working_notes` 或相关 implementation doc 的 `Verification`;不要维护独立 gate results state。
148
150
  16. 如果信息缺失,或 gate 因基础设施原因失败,停止推进并报告 blocker。
@@ -157,8 +159,8 @@ Strong success criteria 可以让你 independent loop。Weak criteria,例如
157
159
  - “继续 / 下一步 / 推进” → 等价 `/next`。
158
160
  - “能进入下一阶段吗 / 进入下一步” → 等价 `/advance`。
159
161
  - “需求变了 / 这个设计要改” → 进入 RFC workflow。
160
- - “完善产品方案 / 写 PRD / 我提供信息,你帮我完善产品方案” → 等价 `/prd`。
161
- - “设计技术方案 / 做架构方案 / 根据 PRD 做技术方案” → 等价 `/design`。
162
+ - “完善产品方案 / 写 PRD / 文档切片 / 我提供信息,你帮我完善产品方案” → 等价 `/prd`,一次只执行一个 `phase: "REQUIREMENT_GATHERING"` 的 `TASK-*`。
163
+ - “设计技术方案 / 做架构方案 / 根据 PRD 做技术方案 / 切技术方案” → 等价 `/design`,一次只执行一个 `phase: "ARCHITECTING"` 的 `TASK-*`。
162
164
  - “开始开发 / 做当前任务 / 做下一个任务” → 等价 `/dev`。
163
165
  - “开始循环:写任务,执行任务 / 把开发循环跑完” → 等价 `/devloop`。
164
166
  - “跑测试 / 验证一下” → 运行当前 task 或阶段对应 gate。
@@ -175,9 +177,9 @@ Parallel Execution 是可选协作协议,不是默认模式,也不是 CLI
175
177
  - `/next`:运行当前阶段映射的 workflow skill。
176
178
  - `/advance`:校验当前阶段出口 gate,通过后才尝试流转。
177
179
  - `/rfc <file>`:挂起当前流程并进入 RFC recalibration。
178
- - `/prd`:在 `REQUIREMENT_GATHERING` 调用产品方案工作流,澄清需求并更新 PRD、验收标准、open questions、`.docs/INDEX.md` 和 overview。
179
- - `/design`:在 `ARCHITECTING` 调用架构和技术方案工作流,基于 PRD 更新 architecture、tech plan 和 `plan.draft.yaml`。
180
- - `/dev`:在 `SPRINTING` 创建或选择下一个最小 DEV task,执行一个 task,跑 gate,更新模块级 implementation doc,按两段 commit/push 闭环后停止。
180
+ - `/prd`:在 `REQUIREMENT_GATHERING` 创建或选择一个最小 `TASK-*` task,澄清需求或切片文档,并只更新当前 task 对应的 PRD、验收标准、open questions、`.docs/INDEX.md` 和 overview。
181
+ - `/design`:在 `ARCHITECTING` 创建或选择一个最小 `TASK-*` task,基于 PRD 生成或切分当前 task 对应的 architecture、tech plan 和 `plan.draft.yaml`。
182
+ - `/dev`:在 `SPRINTING` 创建或选择下一个最小 `TASK-*` development task,执行一个 task,跑 gate,更新模块级 implementation doc,按两段 commit/push 闭环后停止。
181
183
  - `/devloop`:在 `SPRINTING` 连续运行 `/dev` 循环,直到没有明确可创建/执行的任务,或遇到需求、架构、allowed_paths、gate、commit/push blocker。
182
184
  - `/syncdocs`:同步 `.docs/INDEX.md` 与当前文档事实源。
183
185
  - `/overview`:运行 `make docs-overview`,刷新 `.docs/<stage>/overview.md` 派生视图。
@@ -60,8 +60,9 @@ npx sdlc-harness init --adopt
60
60
  | 同步 managed workflow 文件 | `npx sdlc-harness sync` | 从包内 canonical assets 物化 `AGENTS.md` managed block、workflow skills、templates、policies、Makefile 片段和 GitHub workflow |
61
61
  | 升级已接入项目 | `npx sdlc-harness upgrade` | 执行迁移并自动 `sync`,保留 state、docs、业务代码和本地 override |
62
62
  | 接入诊断 | `npx sdlc-harness doctor` | 检查 harness root、版本、schema、关键文件和 managed paths |
63
- | 阶段 gate | `npx sdlc-harness validate-*`、`make validate-current`、`make validate-harness` | 校验需求、设计、开发计划、Harness 骨架、提示词语言契约和 overview freshness |
63
+ | 阶段 gate | `npx sdlc-harness validate-*`、`make validate-current`、`make validate-harness` | 校验需求、设计、开发、Review、测试、发布、RFC、Harness 骨架、提示词语言契约和 overview freshness |
64
64
  | 生命周期工作流 | `lifecycle.yaml`、`plan.yaml`、`.docs/**` | 固定 REQUIREMENT_GATHERING、ARCHITECTING、SPRINTING、REVIEWING、TESTING、RELEASING、RFC_RECALIBRATION 等阶段事实链 |
65
+ | 阶段小任务管控 | `plan.yaml`、`make validate-plan` | 每个阶段的 Agent 主任务都应拆成足够小的 `TASK-*` open task,并用 `phase` 标明所属阶段 |
65
66
  | 自然语言控制 | `AGENTS.md` + workflow skills | 用户可说“继续”“开始开发”“跑测试”“需求变了”等,由 Agent 映射到 `/next`、`/dev`、`/test`、RFC 等动作 |
66
67
  | 可选并行执行合同 | `plan.yaml#parallel_execution` | 用户明确要求多 agent/并行/多 worktree 时启用;支持 runtime-managed subagents 或 user-orchestrated worker prompts |
67
68
  | Workflow skills | `<harnessRoot>/skills/pjsdlc_*/SKILL.md` | 提供 PM、架构、开发、实现文档、Review、测试、发布、RFC 等阶段角色提示词 |
@@ -81,7 +82,9 @@ npx sdlc-harness init --adopt
81
82
  现在到哪一步了?
82
83
  继续推进。
83
84
  我提供这些信息,帮我完善产品方案。
85
+ 把这个长产品方案切成 slices。
84
86
  根据 PRD 做技术方案。
87
+ 把现有技术方案切片。
85
88
  根据技术方案拆 task。
86
89
  开始开发当前 task。
87
90
  继续开发下一个任务。
@@ -92,7 +95,7 @@ npx sdlc-harness init --adopt
92
95
  准备 review。
93
96
  ```
94
97
 
95
- Agent 会读取 `<harnessRoot>/state/lifecycle.yaml` 和 `<harnessRoot>/state/plan.yaml`,再按当前阶段选择对应 workflow skill、产物和 gate
98
+ Agent 会读取 `<harnessRoot>/state/lifecycle.yaml` 和 `<harnessRoot>/state/plan.yaml`,再按当前阶段选择对应 workflow skill、产物和 gate。任何阶段的 Agent 主任务都不是一次性长生成:产品方案、技术方案、文档切片、基于上一阶段事实源生成、Review、测试、发布和 RFC 处理,都应先落成一个最小 `TASK-*` open task,并设置对应 `phase`;当前轮只执行一个 task,写入 `result_docs` 或 `implementation_doc`、更新索引和 overview,运行 `make validate-plan`,任务完成后再从 `plan.yaml` 移除。
96
99
 
97
100
  ### Workflow skill 如何生效
98
101
 
@@ -144,9 +147,9 @@ Harness CLI v1 不承诺自动启动 Codex agent,也不要求 worker 之间通
144
147
  |---|---|---|
145
148
  | `/status` | 现在到哪一步了 | 读取 lifecycle/plan,报告当前阶段、任务、阻塞项和下一步 |
146
149
  | `/next` | 继续推进 | 按当前阶段的 `active_skill` 执行下一步 |
147
- | `/prd` | 完善产品方案 | 在需求阶段澄清用户目标、补齐 PRD、验收标准和 open questions |
148
- | `/design` | 设计技术方案 | 在架构阶段基于 PRD 生成或更新架构、技术方案和 `plan.draft.yaml` |
149
- | `/dev` | 做下一个任务 | 创建或选择下一个最小 DEV task,完成一个 task 闭环后停止 |
150
+ | `/prd` | 完善产品方案 | 在需求阶段创建或选择一个最小 `TASK-*` task,澄清目标、切片文档或补齐当前 PRD slice |
151
+ | `/design` | 设计技术方案 | 在架构阶段创建或选择一个最小 `TASK-*` task,生成或切分当前 architecture / tech plan / `plan.draft.yaml` 产物 |
152
+ | `/dev` | 做下一个任务 | 创建或选择下一个最小 `TASK-*` development task,完成一个 task 闭环后停止 |
150
153
  | `/devloop` | 开始循环:写任务,执行任务 | 连续运行 `/dev`,直到没有明确任务或遇到 blocker |
151
154
  | `/test` | 跑一下当前验证 | 运行当前 task 或阶段对应 gate |
152
155
  | `/review` | 准备 review | 进入只读 Review workflow |
@@ -179,6 +182,12 @@ npx sdlc-harness upgrade
179
182
  make validate-current
180
183
  ```
181
184
 
185
+ 校验当前 open task 合同:
186
+
187
+ ```sh
188
+ make validate-plan
189
+ ```
190
+
182
191
  校验整个 Harness:
183
192
 
184
193
  ```sh
@@ -1,6 +1,6 @@
1
1
  PYTHON ?= python3
2
2
 
3
- .PHONY: help status docs-overview validate-doc-overviews validate-harness validate-current validate-pm validate-design validate-dev validate-review validate-test validate-release validate-rfc lint test-current-domain test-all build
3
+ .PHONY: help status docs-overview validate-doc-overviews validate-harness validate-current validate-plan validate-pm validate-design validate-dev validate-review validate-test validate-release validate-rfc lint test-current-domain test-all build
4
4
 
5
5
  help:
6
6
  @echo "AI SDLC Harness commands"
@@ -9,6 +9,7 @@ help:
9
9
  @echo " make validate-doc-overviews 校验 .docs 各阶段 overview.md 是否最新"
10
10
  @echo " make validate-harness 校验 Harness 骨架、配置和提示词语言契约"
11
11
  @echo " make validate-current 运行当前 lifecycle phase 的 gate"
12
+ @echo " make validate-plan 校验 plan.yaml task 合同,允许当前 open task"
12
13
  @echo " make validate-pm 校验产品需求产物"
13
14
  @echo " make validate-design 校验架构设计、技术方案和任务草案"
14
15
  @echo " make validate-dev 校验 sprint 任务状态、路径、代码 gate 和实现文档"
@@ -34,6 +35,10 @@ validate-harness:
34
35
  validate-current:
35
36
  $(PYTHON) tools/run_current_gate.py
36
37
 
38
+ validate-plan:
39
+ $(PYTHON) tools/validate_plan.py --allow-open
40
+ $(PYTHON) tools/validate_allowed_paths.py
41
+
37
42
  validate-pm:
38
43
  test -f .docs/INDEX.md
39
44
  $(PYTHON) tools/validate_prd.py
@@ -31,17 +31,20 @@ phases:
31
31
  REVIEWING:
32
32
  read_only_source: true
33
33
  write:
34
+ - "<harnessRoot>/state/plan.yaml"
34
35
  - ".docs/06_review/**"
35
36
  - ".docs/INDEX.md"
36
37
 
37
38
  TESTING:
38
39
  write:
40
+ - "<harnessRoot>/state/plan.yaml"
39
41
  - ".docs/07_test/**"
40
42
  - "tests/**"
41
43
  - ".docs/INDEX.md"
42
44
 
43
45
  RELEASING:
44
46
  write:
47
+ - "<harnessRoot>/state/plan.yaml"
45
48
  - ".docs/08_release/**"
46
49
  - ".docs/INDEX.md"
47
50
 
@@ -5,6 +5,18 @@ gates:
5
5
  required_for:
6
6
  - "all"
7
7
 
8
+ validate-plan:
9
+ command: "make validate-plan"
10
+ purpose: "验证当前 open task 合同,允许任务执行中保留 open task"
11
+ required_for:
12
+ - "REQUIREMENT_GATHERING"
13
+ - "ARCHITECTING"
14
+ - "SPRINTING"
15
+ - "REVIEWING"
16
+ - "TESTING"
17
+ - "RELEASING"
18
+ - "RFC_RECALIBRATION"
19
+
8
20
  validate-pm:
9
21
  command: "make validate-pm"
10
22
  purpose: "验证 PRD、验收标准、Out of Scope 和 Open Questions"
@@ -15,8 +15,10 @@ phases:
15
15
  role: "pm"
16
16
  skill: "pjsdlc_pm_prd"
17
17
  inputs:
18
+ - "<harnessRoot>/state/plan.yaml"
18
19
  - ".docs/00_raw/"
19
20
  outputs:
21
+ - "<harnessRoot>/state/plan.yaml"
20
22
  - ".docs/01_product/"
21
23
  - ".docs/INDEX.md"
22
24
  gates:
@@ -28,9 +30,11 @@ phases:
28
30
  role: "architect"
29
31
  skill: "pjsdlc_architect_design"
30
32
  inputs:
33
+ - "<harnessRoot>/state/plan.yaml"
31
34
  - ".docs/01_product/"
32
35
  - ".docs/02_architecture/"
33
36
  outputs:
37
+ - "<harnessRoot>/state/plan.yaml"
34
38
  - ".docs/02_architecture/"
35
39
  - ".docs/03_tech_plan/"
36
40
  - "<harnessRoot>/state/plan.draft.yaml"
@@ -60,11 +64,13 @@ phases:
60
64
  role: "reviewer"
61
65
  skill: "pjsdlc_reviewer"
62
66
  inputs:
67
+ - "<harnessRoot>/state/plan.yaml"
63
68
  - ".docs/01_product/"
64
69
  - ".docs/03_tech_plan/"
65
70
  - ".docs/04_implementation/"
66
71
  - "git diff"
67
72
  outputs:
73
+ - "<harnessRoot>/state/plan.yaml"
68
74
  - ".docs/06_review/REVIEW_REPORT.md"
69
75
  gates:
70
76
  - "make validate-review"
@@ -75,11 +81,13 @@ phases:
75
81
  role: "tester"
76
82
  skill: "pjsdlc_tester"
77
83
  inputs:
84
+ - "<harnessRoot>/state/plan.yaml"
78
85
  - ".docs/01_product/"
79
86
  - ".docs/03_tech_plan/"
80
87
  - ".docs/04_implementation/"
81
88
  - ".docs/06_review/"
82
89
  outputs:
90
+ - "<harnessRoot>/state/plan.yaml"
83
91
  - ".docs/07_test/"
84
92
  - "tests/"
85
93
  gates:
@@ -91,9 +99,11 @@ phases:
91
99
  role: "release_manager"
92
100
  skill: "pjsdlc_release_manager"
93
101
  inputs:
102
+ - "<harnessRoot>/state/plan.yaml"
94
103
  - ".docs/07_test/"
95
104
  - "build artifacts"
96
105
  outputs:
106
+ - "<harnessRoot>/state/plan.yaml"
97
107
  - ".docs/08_release/"
98
108
  gates:
99
109
  - "make validate-release"
@@ -17,9 +17,12 @@ description: Use during ARCHITECTING to create architecture docs, technical plan
17
17
 
18
18
  架构产物应区分稳定边界和实现计划:architecture slice 记录领域边界、子系统、关键风险和长期约束;tech plan slice 记录接口契约、数据模型、模块方案、任务拆分和 gate。不要把重大架构变化藏在 task 描述里。
19
19
 
20
+ 架构和技术方案产出本身也是 workflow task,而不是一次性长文档生成。无论来源是对话式设计、既有完整技术方案切片,还是根据 PRD/architecture 事实源生成新方案,都要先在 `<harnessRoot>/state/plan.yaml` 创建或选择一个足够小的 `TASK-*` open task,并设置 `phase: "ARCHITECTING"`,只完成当前 `current_task_id` 对应的一片 architecture / tech plan / ADR / `plan.draft.yaml` 产物。不要在一个任务里连续创建大量设计文件;如果需要多个 slices,先拆出 pending tasks,当前轮只执行一个 task。
21
+
20
22
  ## 输入
21
23
 
22
24
  - `.docs/INDEX.md`
25
+ - `<harnessRoot>/state/plan.yaml`
23
26
  - 相关 `.docs/01_product/` PRD
24
27
  - 现有 `.docs/02_architecture/`
25
28
  - 当前代码结构概览
@@ -32,6 +35,7 @@ description: Use during ARCHITECTING to create architecture docs, technical plan
32
35
  - `.docs/03_tech_plan/` 下的技术方案
33
36
  - 需要长期保留的 ADR 写入 `.docs/05_decisions/`
34
37
  - `<harnessRoot>/state/plan.draft.yaml`
38
+ - 更新后的 `<harnessRoot>/state/plan.yaml`
35
39
  - 更新后的 `.docs/INDEX.md`
36
40
 
37
41
  ## 语义切片
@@ -41,21 +45,38 @@ description: Use during ARCHITECTING to create architecture docs, technical plan
41
45
  - `.docs/05_decisions/` 按单个架构决策切片,即一份 ADR 只记录一个 durable decision。
42
46
  - 如果一个技术方案跨越多个独立模块,应拆成多个 tech plan slice,并在 `plan.draft.yaml` 中分别引用。
43
47
  - 如果实现计划改变了已有模块边界,应更新相关 architecture slice,而不是只在 task 描述里补一句。
48
+ - 如果用户明确要求把既有完整技术方案文件切成多个 `.docs/03_tech_plan/` slices,先确认 replacement slices 覆盖原文件中仍有效的接口契约、数据模型、模块方案、任务组和 gate;切片完成并更新 `plan.draft.yaml` 引用、`.docs/INDEX.md`、刷新 `overview.md` 后,删除被替代的完整 tech plan file,避免同一事实由完整文件和 slices 双重保留。
44
49
  - 每次新增、拆分、合并或废弃 slice 后,都要更新 `.docs/INDEX.md`。
45
50
 
51
+ ## Plan Protocol
52
+
53
+ 架构和技术方案阶段的方案生成、既有文档切片和上一阶段事实源合成都受 `plan.yaml` 管控:
54
+
55
+ 1. 没有 open task 时,先创建一个最小 `TASK-*` task,设置 `phase: "ARCHITECTING"` 和 `current_task_id`。
56
+ 2. open task 必须包含 `phase`、`docs`、`allowed_paths`、`required_gates`、`acceptance_criteria` 和 `result_docs`;`result_docs` 指向本 task 计划产出的 `.docs/02_architecture/`、`.docs/03_tech_plan/`、`.docs/05_decisions/` 或 `<harnessRoot>/state/plan.draft.yaml`。
57
+ 3. 单个 task 的目标应足够小:一个子系统 architecture slice、一个 tech plan slice、一个接口契约、一组开发任务草案,或从完整技术方案中切出的一个语义 slice。
58
+ 4. 如果需要多个 architecture / tech plan slices,先生成多个 pending `TASK-*` tasks 或至少创建当前 task 并在 `working_notes` 写明剩余 slices;当前轮只执行一个 task。
59
+ 5. 执行当前 task 时只编辑 `allowed_paths` 中的文件,完成后更新 `.docs/INDEX.md`、运行 `make docs-overview`,并至少运行 `make validate-plan`;阶段出口前再运行 `make validate-design`。
60
+ 6. task 完成后,从 `plan.yaml.tasks` 移除该 task;如果仍有 pending `TASK-*` design task,下一轮 `/design` 或 `/next` 再继续。
61
+ 7. 如果网络或上下文中断,新会话先读取 `current_task_id` 和当前 open task,按 `working_notes` 恢复,而不是重新生成全量技术方案。
62
+
46
63
  ## 规则
47
64
 
48
65
  1. 技术方案必须引用 PRD 路径和 requirement IDs。
49
- 2. 每个 open task 必须包含 `id`、`title`、`status`、`summary`、`docs`、`allowed_paths`、`required_gates`、`acceptance_criteria` 和 `implementation_doc`。
66
+ 2. 每个 open task 必须包含 `id`、`phase`、`title`、`status`、`summary`、`docs`、`allowed_paths`、`required_gates`、`acceptance_criteria` 和 `result_docs`;开发阶段 task 继续使用 `implementation_doc`。
50
67
  3. `plan.draft.yaml` 不得自动覆盖 `plan.yaml`。
51
68
  4. 风险或不清晰的问题按 `<harnessRoot>/pjsdlc_managed/policies/risk_matrix.yaml` 标记。
52
- 5. 任务边界应足够小,能在一次开发执行内闭环;`implementation_doc` 应指向将被更新或新增的模块、子系统或核心数据流文档。
69
+ 5. 任务边界应足够小,能在一次设计执行内闭环;`result_docs` 应指向将被更新或新增的 architecture、tech plan、ADR 或 `plan.draft.yaml` 文件。
70
+ 6. `make validate-design` 是阶段出口 gate;如果还有 open `TASK-*` design task,不要请求进入 `SPRINTING`。
53
71
 
54
72
  ## 完成检查
55
73
 
56
74
  - [ ] 架构文档和技术方案已生成。
57
75
  - [ ] 相关接口契约和数据结构已明确。
76
+ - [ ] 当前设计产出或切片工作已绑定 `plan.yaml` 中一个最小 `TASK-*` task,并设置 `phase: "ARCHITECTING"`。
77
+ - [ ] 当前 task 已从 `plan.yaml` 移除,或因中断/blocker 保留为可恢复 open task。
58
78
  - [ ] 已判断 architecture / tech plan / ADR 的语义切片边界。
79
+ - [ ] 如果用户要求把完整技术方案切成 tech plan slices,已删除被替代的完整 tech plan file,并同步 `plan.draft.yaml` 引用。
59
80
  - [ ] task draft 字段完整且范围清晰。
60
81
  - [ ] `.docs/INDEX.md` 已链接新增产物。
61
82
  - [ ] 已运行 `make docs-overview` 刷新 `.docs/<stage>/overview.md`。
@@ -15,7 +15,7 @@ description: Use during SPRINTING to execute one task from plan.yaml, respecting
15
15
 
16
16
  开始编码前,先确认当前 open task 是否完整,修改范围是否覆盖必要文件,验收标准是否能被测试或 gate 验证。如果发现任务边界、产品行为或技术方案不清晰,要停下来说明 blocker、给出可能解释和推荐下一步,而不是扩大范围继续写。
17
17
 
18
- `/dev` 和 `/devloop` 是开发阶段的两个入口。`/dev` 创建或选择下一个最小 DEV task,并只完成一个 task 闭环后停止。`/devloop` 连续运行 `/dev`,直到技术方案中没有明确可创建/执行的任务,或遇到需求、架构、allowed_paths、gate、commit/push blocker。
18
+ `/dev` 和 `/devloop` 是开发阶段的两个入口。`/dev` 创建或选择下一个最小 `TASK-*` development task,设置 `phase: "SPRINTING"`,并只完成一个 task 闭环后停止。`/devloop` 连续运行 `/dev`,直到技术方案中没有明确可创建/执行的任务,或遇到需求、架构、allowed_paths、gate、commit/push blocker。
19
19
 
20
20
  实现时遵循小步闭环:先检查 `git status`,确认工作区没有未归属到当前 task 的脏变更;再定位相关代码和测试,做必要修改,运行 gate,修复失败,写入或更新相关 implementation doc 并刷新文档派生视图。此时先不要从 `plan.yaml` 移除当前 task,要在当前 task 仍位于 `plan.yaml` 时创建 task implementation commit;随后再移除 task,创建 task completion ledger commit,并 push 两个 commit。不要顺手重构、重排格式或处理无关问题;如果发现无关风险,只记录或报告。
21
21
 
@@ -44,14 +44,14 @@ description: Use during SPRINTING to execute one task from plan.yaml, respecting
44
44
 
45
45
  - `SPRINTING` 阶段的执行单元是 `current_task_id`,不要在开发中重新生成整个 Sprint 计划。
46
46
  - 当前 task 是开发阶段的执行单元、修改边界和提交边界;implementation doc 的长期语义切片是模块、子系统或核心数据流。
47
- - open task 在 `plan.yaml` 中直接保存 `docs`、`allowed_paths`、`required_gates`、`acceptance_criteria` 和必要的 `working_notes`。
47
+ - open task 在 `plan.yaml` 中直接保存 `phase: "SPRINTING"`、`docs`、`allowed_paths`、`required_gates`、`acceptance_criteria`、`implementation_doc` 和必要的 `working_notes`。
48
48
  - task implementation commit 必须发生在 task 移除之前,避免实现变更和计划短期化混在同一个提交里。
49
49
  - task completion ledger commit 发生在 implementation commit 之后,只负责将该 task 从当前 `plan.yaml` 移除。
50
- - 一个开发 task 默认对应一个主要 implementation commit 和一个轻量 completion ledger commit。implementation commit message 应包含 task id,例如 `DEV-003: implement login rate limit`;push 成功前,不进入下一个 task。
50
+ - 一个开发 task 默认对应一个主要 implementation commit 和一个轻量 completion ledger commit。implementation commit message 应包含 task id,例如 `TASK-003: implement login rate limit`;push 成功前,不进入下一个 task。
51
51
  - 本 Skill 不直接重切 PRD 或 tech plan;如果发现上游语义边界错误,进入 `BLOCKED`、创建 RFC,或请求回到 `ARCHITECTING`。
52
52
  - gate 通过后调用 `pjsdlc_implementation_doc`,由该 Skill 按真实实现更新或新增 `.docs/04_implementation/` 模块级 slice。
53
53
  - 如果一个任务实际变成多个独立实现边界,应停止扩大范围,拆分后续任务或回到任务规划。
54
- - `/dev` 是单任务执行入口:没有 open task 时,先根据 PRD、architecture、tech plan 和 `plan.draft.yaml` 创建一个最小 open task;已有 open task 时,直接执行该 task;完成后停止。
54
+ - `/dev` 是单任务执行入口:没有 open task 时,先根据 PRD、architecture、tech plan 和 `plan.draft.yaml` 创建一个最小 `TASK-*` open task;已有 open task 时,直接执行该 task;完成后停止。
55
55
  - `/devloop` 是连续执行入口:每完成一个 task 并 push 两段提交后,重新读取 lifecycle、plan、PRD、architecture 和 tech plan,再决定是否创建/执行下一个最小 task;没有明确任务或出现 blocker 时停止并报告。
56
56
  - Parallel Execution 是当前 task 的可选协作方式,不替代 task completion protocol;`SPRINTING` 并行必须用 `parallel_execution.linked_task_id` 绑定当前 `current_task_id`。
57
57
 
@@ -60,7 +60,7 @@ description: Use during SPRINTING to execute one task from plan.yaml, respecting
60
60
  每个 open task 都必须在 `plan.yaml` 中包含完整执行合同:
61
61
 
62
62
  1. `current_task_id` 指向正在执行的 open task。
63
- 2. open task 直接声明 `docs`、`allowed_paths`、`required_gates`、`acceptance_criteria`。
63
+ 2. open task 直接声明 `phase: "SPRINTING"`、`docs`、`allowed_paths`、`required_gates`、`acceptance_criteria` 和 `implementation_doc`。
64
64
  3. 任务执行中只保留恢复所需的简短 `working_notes`。
65
65
  4. gate、implementation doc、`.docs/INDEX.md` 和 `overview.md` 完成后,在当前 task 仍位于 `plan.yaml` 时创建 task implementation commit。
66
66
  5. implementation commit 完成后,再把该 task 从 `plan.yaml` 的 `tasks` 列表移除,并保留/递增 `next_task_sequence`。
@@ -45,21 +45,23 @@ Parallel Execution 是显式 opt-in:只有用户明确提出“并行”“多
45
45
  12. 用户自然语言要求继续、推进或下一步时,等价执行 `/next`。
46
46
  13. 用户自然语言要求进入下一阶段或检查是否可进入下一阶段时,等价执行 `/advance`。
47
47
  14. 用户自然语言表达需求或设计变化时,进入 RFC workflow。
48
- 15. 用户输入 `/prd`,或自然语言要求“完善产品方案”“写 PRD”“我提供信息,你帮我完善产品方案”时,如果 `current_phase` 是 `REQUIREMENT_GATHERING`,调用产品方案工作流并更新 PRD、验收标准和 open questions;否则说明当前阶段冲突和推荐路径。
49
- 16. 用户输入 `/design`,或自然语言要求“设计技术方案”“做架构方案”“根据 PRD 做技术方案”时,如果 `current_phase` 是 `ARCHITECTING`,调用架构和技术方案工作流并更新 architecturetech plan `plan.draft.yaml`;否则说明当前阶段冲突和推荐路径。
50
- 17. 用户输入 `/dev`,或自然语言要求“开始开发”“做当前任务”“做下一个任务”“继续开发下一个任务”时,如果 `current_phase` 是 `SPRINTING`,创建或选择一个最小 DEV task 并执行一个 task 闭环;否则说明当前阶段冲突和推荐路径。
48
+ 15. 用户输入 `/prd`,或自然语言要求“完善产品方案”“写 PRD”“文档切片”“我提供信息,你帮我完善产品方案”时,如果 `current_phase` 是 `REQUIREMENT_GATHERING`,调用产品方案工作流;该工作流必须先创建或选择一个最小 `TASK-*` open task,并设置 `phase: "REQUIREMENT_GATHERING"`,再执行一个 PRD 生成或切片 task;否则说明当前阶段冲突和推荐路径。
49
+ 16. 用户输入 `/design`,或自然语言要求“设计技术方案”“做架构方案”“根据 PRD 做技术方案”“切技术方案”时,如果 `current_phase` 是 `ARCHITECTING`,调用架构和技术方案工作流;该工作流必须先创建或选择一个最小 `TASK-*` open task,并设置 `phase: "ARCHITECTING"`,再执行一个 architecture / tech plan / `plan.draft.yaml` 生成或切片 task;否则说明当前阶段冲突和推荐路径。
50
+ 17. 用户输入 `/dev`,或自然语言要求“开始开发”“做当前任务”“做下一个任务”“继续开发下一个任务”时,如果 `current_phase` 是 `SPRINTING`,创建或选择一个最小 `TASK-*` development task 并执行一个 task 闭环;否则说明当前阶段冲突和推荐路径。
51
51
  18. 用户输入 `/devloop`,或自然语言要求“开始循环:写任务,执行任务”“把开发循环跑完”“连续开发”时,如果 `current_phase` 是 `SPRINTING`,连续运行 `/dev` 循环,直到没有明确可做任务或遇到 blocker;否则说明当前阶段冲突和推荐路径。
52
52
  19. 用户自然语言要求跑测试或验证时,运行当前 task 或当前阶段的对应 gate。
53
53
  20. 用户明确要求并行、多 agent 或多 worktree 时,先判断当前阶段是否是 `REQUIREMENT_GATHERING`、`SPRINTING` 或 `TESTING`;如果是,生成或使用 `parallel_execution.trigger: "user_requested"` 合同;否则说明当前阶段不支持并行合同。
54
54
  21. `runtime_managed` 模式只在当前 Agent runtime 真实具备 subagent 能力时使用;否则使用 `user_orchestrated` 并输出每个 worker 的可复制 prompt。
55
- 22. 用户自然语言要求 review 时,进入只读 Review workflow,不直接改源码。
55
+ 22. 用户自然语言要求 review 时,如果 `current_phase` 是 `REVIEWING`,创建或选择一个最小 `TASK-*` review task,并设置 `phase: "REVIEWING"`;reviewer 只读源码,不直接改源码。
56
56
  23. 用户自然语言要求刷新文档总览时,运行 `make docs-overview`。
57
57
  24. `/plan` 和 `/goal` 是客户端模式入口,不由 Harness 自动开启;如果用户手动组合 `/plan` 或 `/goal` 与自然语言或宏指令,应按对应 workflow action 继续执行。
58
58
  25. 如果动作会改变阶段、创建或删除 task、提交、push 或发布,先用一句话说明即将执行的动作和验证方式,再继续。
59
59
 
60
60
  ## Plan Protocol
61
61
 
62
- 每个 open task 都必须在 `plan.yaml` 中包含 `docs`、`allowed_paths`、`required_gates` 和 `acceptance_criteria`;done/cancelled task 不长期留在当前 `plan.yaml`。完成后的产物事实以模块、子系统或核心数据流级 implementation doc 为准,动作历史以 git/PR/CI/release 系统作为 cold archive,`next_task_sequence` 负责继续分配后续 task id。
62
+ 每个 open task 都必须在 `plan.yaml` 中包含 `id`、`phase`、`docs`、`allowed_paths`、`required_gates` 和 `acceptance_criteria`;新 task 统一使用 `TASK-*` id,历史 `DEV-*`、`PRD-*`、`DES-*` task 只作为兼容输入保留。文档和流程产物 task 使用 `result_docs` 指向本 task 产出的 PRD、architecture、tech plan、ADR、review、test、release、RFC 或 `plan.draft.yaml`,开发 task 使用 `implementation_doc` 指向模块级实现事实。done/cancelled task 不长期留在当前 `plan.yaml`。完成后的产物事实以对应 `.docs/**` slice、`plan.draft.yaml` 或模块级 implementation doc 为准,动作历史以 git/PR/CI/release 系统作为 cold archive,`next_task_sequence` 负责继续分配后续 task id。
63
+
64
+ `/prd`、`/design`、`/dev`、`/review`、`/test`、`/release` 和 `/rfc` 都是单 task 推进:默认只完成一个 `TASK-*`。`validate-plan` 用于检查当前 open task 合同是否完整;阶段出口 gate `validate-pm`、`validate-design`、`validate-dev`、`validate-review`、`validate-test`、`validate-release` 和 `validate-rfc` 都要求没有 open task 残留。
63
65
 
64
66
  `parallel_execution` 是可选顶层合同,缺省表示串行。启用后必须声明 `enabled`、`trigger`、`mode`、`phase`、`coordinator`、`workers` 和 `integration`;`SPRINTING` 并行还必须通过 `linked_task_id` 绑定当前 `current_task_id`。
65
67
 
@@ -17,10 +17,13 @@ description: Use during REQUIREMENT_GATHERING to turn raw input into PRD slices
17
17
 
18
18
  产出 PRD 时,优先让后续架构和测试能直接使用:每条需求应有清晰 requirement ID、验收条件、Out of Scope、风险或依赖。对话中出现新范围时,要判断是更新当前 slice、拆出新 slice,还是进入 RFC。
19
19
 
20
+ PRD 产出本身是 workflow task,而不是一次性长文档生成。无论来源是对话式需求澄清、既有完整文档切片,还是根据 `.docs/00_raw/` 等事实源合成产品方案,都要先在 `<harnessRoot>/state/plan.yaml` 创建或选择一个足够小的 `TASK-*` open task,并设置 `phase: "REQUIREMENT_GATHERING"`,只完成当前 `current_task_id` 对应的一片产物。不要在一个任务里连续创建大量 PRD 文件;如果需要多个 slices,先把后续 slices 拆成 pending tasks,当前轮只执行一个 task,方便网络中断后按 `plan.yaml` 恢复。
21
+
20
22
  如果用户在需求阶段明确要求并行、多 agent 或多 worktree,Parallel Execution 只能用于调研、草稿、场景拆解、风险列表或 open questions 收集。worker 不直接写最终 PRD;主 Agent 必须合成最终 `.docs/01_product/**`,并把假设、分歧和未决项写入 PRD。没有用户显式要求时,不要启用 `parallel_execution`。
21
23
 
22
24
  ## 输入
23
25
 
26
+ - `<harnessRoot>/state/plan.yaml`
24
27
  - 用户需求或原始记录
25
28
  - `.docs/00_raw/`
26
29
  - 现有 `.docs/01_product/`
@@ -31,6 +34,7 @@ description: Use during REQUIREMENT_GATHERING to turn raw input into PRD slices
31
34
 
32
35
  - `.docs/00_raw/` 下的原始需求记录
33
36
  - `.docs/01_product/` 下的一个或多个 PRD slice
37
+ - 更新后的 `<harnessRoot>/state/plan.yaml`
34
38
  - 更新后的 `.docs/INDEX.md`
35
39
 
36
40
  ## 语义切片
@@ -39,16 +43,31 @@ description: Use during REQUIREMENT_GATHERING to turn raw input into PRD slices
39
43
  - `.docs/01_product/` 按业务能力、用户场景、验收边界切片。
40
44
  - 如果新增内容仍属于同一业务能力,只更新原 PRD slice。
41
45
  - 如果新增内容形成独立用户场景、独立验收标准或独立 Out of Scope,应创建新的 PRD slice。
46
+ - 如果用户明确要求把既有完整 PRD/产品方案文件切成多个 `.docs/01_product/` slices,先确认 replacement slices 覆盖原文件中仍有效的全部需求事实;切片完成并更新 `.docs/INDEX.md`、刷新 `overview.md` 后,删除被替代的完整文件,避免同一事实由完整文件和 slices 双重保留。不要因此删除 `.docs/00_raw/` 原始记录,除非用户明确要求。
42
47
  - 每次新增、拆分、合并或废弃 slice 后,都要更新 `.docs/INDEX.md`。
43
48
 
49
+ ## Plan Protocol
50
+
51
+ 需求阶段的 PRD 生成、既有文档切片和事实源合成都受 `plan.yaml` 管控:
52
+
53
+ 1. 没有 open task 时,先创建一个最小 `TASK-*` task,设置 `phase: "REQUIREMENT_GATHERING"` 和 `current_task_id`。
54
+ 2. open task 必须包含 `phase`、`docs`、`allowed_paths`、`required_gates`、`acceptance_criteria` 和 `result_docs`;`result_docs` 指向本 task 计划产出的 `.docs/00_raw/` 或 `.docs/01_product/` 文件。
55
+ 3. 单个 task 的目标应足够小:一段原始需求归档、一个用户场景 PRD slice、一个验收边界、一个 open questions 集合,或从完整文档中切出的一个语义 slice。
56
+ 4. 如果用户要求切成多个 slices,先生成多个 pending `TASK-*` tasks 或至少创建当前 task 并在 `working_notes` 写明剩余 slices;当前轮只执行一个 task。
57
+ 5. 执行当前 task 时只编辑 `allowed_paths` 中的文件,完成后更新 `.docs/INDEX.md`、运行 `make docs-overview`,并至少运行 `make validate-plan`;阶段出口前再运行 `make validate-pm`。
58
+ 6. task 完成后,从 `plan.yaml.tasks` 移除该 task;如果仍有 pending `TASK-*` PRD task,下一轮 `/prd` 或 `/next` 再继续。
59
+ 7. 如果网络或上下文中断,新会话先读取 `current_task_id` 和当前 open task,按 `working_notes` 恢复,而不是重新生成全量 PRD。
60
+
44
61
  ## 规则
45
62
 
46
63
  1. 有价值的用户原始表述应保存在 `.docs/00_raw/`。
47
64
  2. 每个 PRD 必须包含目标、用户场景、功能需求、验收标准、Out of Scope 和 Open Questions。
48
65
  3. 不确定内容必须写入 `Open Questions`,不要静默假设。
49
66
  4. 如果需求与既有架构或已接受决策冲突,先写冲突说明,不要直接编写技术方案。
50
- 5. 需求阶段并行必须使用 `parallel_execution.trigger: "user_requested"`;`runtime_managed` 只在当前 runtime 支持 subagent 时使用,否则输出 `user_orchestrated` worker prompt
51
- 6. Skill 不直接进入开发;PRD 完成后请求 `manager` 运行阶段出口 gate。
67
+ 5. 需求阶段一次只执行一个 `TASK-*` task;不要在单次回复里创建或改写大量 PRD slices
68
+ 6. `make validate-pm` 是阶段出口 gate;如果还有 open `TASK-*` PRD task,不要请求进入 `ARCHITECTING`。
69
+ 7. 需求阶段并行必须使用 `parallel_execution.trigger: "user_requested"`;`runtime_managed` 只在当前 runtime 支持 subagent 时使用,否则输出 `user_orchestrated` worker prompt。
70
+ 8. 本 Skill 不直接进入开发;PRD 完成后请求 `manager` 运行阶段出口 gate。
52
71
 
53
72
  ## 完成检查
54
73
 
@@ -56,7 +75,10 @@ description: Use during REQUIREMENT_GATHERING to turn raw input into PRD slices
56
75
  - [ ] Acceptance Criteria 可测试。
57
76
  - [ ] Out of Scope 明确。
58
77
  - [ ] Open Questions 有 owner/status。
78
+ - [ ] 当前 PRD 产出或切片工作已绑定 `plan.yaml` 中一个最小 `TASK-*` task,并设置 `phase: "REQUIREMENT_GATHERING"`。
79
+ - [ ] 当前 task 已从 `plan.yaml` 移除,或因中断/blocker 保留为可恢复 open task。
59
80
  - [ ] 已判断是否需要新增、拆分、合并或废弃 PRD slice。
81
+ - [ ] 如果用户要求把完整 PRD/产品方案切成 slices,已删除被替代的完整文件,并保留必要的 `.docs/00_raw/` 原始记录。
60
82
  - [ ] 如果用户要求并行,worker output 已由主 Agent 合成,最终 PRD 不由 worker 直接写入。
61
83
  - [ ] `.docs/INDEX.md` 已链接新增产物。
62
84
  - [ ] 已运行 `make docs-overview` 刷新 `.docs/<stage>/overview.md`。
@@ -17,8 +17,11 @@ description: Use during RELEASING to prepare release notes, smoke evidence, depl
17
17
 
18
18
  Release note 面向人类读者,必须说明变更价值、影响范围和注意事项;rollback plan 面向执行者,必须具体到触发条件、操作入口、验证方式和负责人。
19
19
 
20
+ 发布准备本身也是 workflow task。开始 release 工作前,先在 `<harnessRoot>/state/plan.yaml` 创建或选择一个足够小的 `TASK-*` open task,并设置 `phase: "RELEASING"`;当前轮只产出一个 release slice、一次 smoke evidence 补充、一个部署检查或一个 rollback plan 单元。发布动作本身仍需用户明确授权。
21
+
20
22
  ## 输入
21
23
 
24
+ - `<harnessRoot>/state/plan.yaml`
22
25
  - `.docs/07_test/`
23
26
  - build artifacts
24
27
  - changelog 或 task list
@@ -27,6 +30,7 @@ Release note 面向人类读者,必须说明变更价值、影响范围和注
27
30
  ## 输出
28
31
 
29
32
  - `.docs/08_release/` 下的 release note
33
+ - 更新后的 `<harnessRoot>/state/plan.yaml`
30
34
  - smoke test result
31
35
  - deployment checklist
32
36
  - rollback plan
@@ -40,6 +44,17 @@ Release note 面向人类读者,必须说明变更价值、影响范围和注
40
44
  - 如果只是补充同一版本的 smoke evidence 或 rollback step,应更新原 release slice。
41
45
  - 发布 slice 完成后更新 `.docs/INDEX.md`;不再维护 Harness archive。
42
46
 
47
+ ## Plan Protocol
48
+
49
+ 发布阶段受 `plan.yaml` 管控:
50
+
51
+ 1. 没有 open task 时,先创建一个最小 `TASK-*` task,设置 `phase: "RELEASING"` 和 `current_task_id`。
52
+ 2. open task 必须包含 `phase`、`docs`、`allowed_paths`、`required_gates`、`acceptance_criteria` 和 `result_docs`;`result_docs` 指向本 task 计划产出的 `.docs/08_release/**` 文件。
53
+ 3. 单个 task 的目标应足够小:一个版本 release note、一个 smoke evidence 补充、一个 deployment checklist 或一个 rollback plan。
54
+ 4. 执行当前 task 时只编辑 `allowed_paths` 中的 release 产物、`.docs/INDEX.md`、overview 和 `plan.yaml`。
55
+ 5. 完成后运行 `make validate-plan` 和 task required gates;阶段出口前运行 `make validate-release`。
56
+ 6. task 完成后从 `plan.yaml.tasks` 移除;如果还有 pending release task,下一轮 `/release` 或 `/next` 再继续。
57
+
43
58
  ## 规则
44
59
 
45
60
  1. 除非用户明确要求,不自动部署。
@@ -47,10 +62,13 @@ Release note 面向人类读者,必须说明变更价值、影响范围和注
47
62
  3. Rollback plan 必须可执行。
48
63
  4. Smoke test evidence 必须链接或摘要记录。
49
64
  5. Human confirmation items 必须明确。
65
+ 6. 发布阶段一次只执行一个 `TASK-*` task。
50
66
 
51
67
  ## 完成检查
52
68
 
53
69
  - [ ] Release note 已生成。
70
+ - [ ] 当前发布工作已绑定 `plan.yaml` 中一个最小 `TASK-*` task,并设置 `phase: "RELEASING"`。
71
+ - [ ] 当前 task 已从 `plan.yaml` 移除,或因中断/blocker 保留为可恢复 open task。
54
72
  - [ ] Build artifacts 已记录。
55
73
  - [ ] Smoke test result 已记录。
56
74
  - [ ] 已判断 release slice 的版本或发布批次边界。
@@ -17,8 +17,11 @@ Review 时先建立证据链:PRD 说什么、技术方案承诺什么、implem
17
17
 
18
18
  不要把个人偏好包装成 blocker。区分 blocking issue、follow-up improvement 和 open question。如果没有发现问题,要明确说明,同时列出剩余测试缺口或残余风险。
19
19
 
20
+ Review 产出本身也是 workflow task。开始 review 前,先在 `<harnessRoot>/state/plan.yaml` 创建或选择一个足够小的 `TASK-*` open task,并设置 `phase: "REVIEWING"`;当前轮只产出一个 review batch、一个风险主题 slice 或一次 PR review 结论。不要在一个任务里覆盖多个互不相关的 review 主题。
21
+
20
22
  ## 输入
21
23
 
24
+ - `<harnessRoot>/state/plan.yaml`
22
25
  - `.docs/01_product/`
23
26
  - `.docs/03_tech_plan/`
24
27
  - `.docs/04_implementation/`
@@ -29,6 +32,7 @@ Review 时先建立证据链:PRD 说什么、技术方案承诺什么、implem
29
32
  ## 输出
30
33
 
31
34
  - `.docs/06_review/REVIEW_REPORT.md`
35
+ - 更新后的 `<harnessRoot>/state/plan.yaml`
32
36
  - 风险清单
33
37
  - 重构建议
34
38
  - 是否允许进入 `TESTING` 的结论
@@ -41,6 +45,17 @@ Review 时先建立证据链:PRD 说什么、技术方案承诺什么、implem
41
45
  - Review 不重切上游 PRD / tech plan;如果发现上游边界错误,记录 blocker 或建议 RFC。
42
46
  - 每次新增或拆分 review slice 后,都要更新 `.docs/INDEX.md`。
43
47
 
48
+ ## Plan Protocol
49
+
50
+ Review 阶段受 `plan.yaml` 管控:
51
+
52
+ 1. 没有 open task 时,先创建一个最小 `TASK-*` task,设置 `phase: "REVIEWING"` 和 `current_task_id`。
53
+ 2. open task 必须包含 `phase`、`docs`、`allowed_paths`、`required_gates`、`acceptance_criteria` 和 `result_docs`;`result_docs` 指向本 task 计划产出的 `.docs/06_review/**` 文件。
54
+ 3. 单个 task 的目标应足够小:一次 review batch、一个 PR、一个模块或一个风险主题。
55
+ 4. 执行当前 task 时只写 review 产物、`.docs/INDEX.md`、`overview.md` 和 `plan.yaml`,不修改源码。
56
+ 5. 完成后运行 `make validate-plan` 和 `make docs-overview`;阶段出口前运行 `make validate-review`。
57
+ 6. task 完成后从 `plan.yaml.tasks` 移除;如果还有 pending review task,下一轮 `/review` 或 `/next` 再继续。
58
+
44
59
  ## 规则
45
60
 
46
61
  1. 本 Skill 不修改源码。
@@ -48,10 +63,13 @@ Review 时先建立证据链:PRD 说什么、技术方案承诺什么、implem
48
63
  3. 每条 finding 尽量引用文件、需求、任务或文档路径。
49
64
  4. 区分 blocking issues 和 follow-up improvements。
50
65
  5. 如果未发现问题,明确说明,并列出剩余测试缺口或残余风险。
66
+ 6. Review 阶段一次只执行一个 `TASK-*` task。
51
67
 
52
68
  ## 完成检查
53
69
 
54
70
  - [ ] Review report 已生成。
71
+ - [ ] 当前 review 工作已绑定 `plan.yaml` 中一个最小 `TASK-*` task,并设置 `phase: "REVIEWING"`。
72
+ - [ ] 当前 task 已从 `plan.yaml` 移除,或因中断/blocker 保留为可恢复 open task。
55
73
  - [ ] 已评估需求一致性。
56
74
  - [ ] 已评估架构和可维护性风险。
57
75
  - [ ] 已判断 review slice 的范围和风险主题边界。
@@ -19,6 +19,8 @@ description: Use during RFC_RECALIBRATION to process requirement changes with im
19
19
 
20
20
  影响面分析必须先于补丁。至少检查 docs/state/skills/policies/templates/tools/package assets/tests/migrations/generated artifacts 是否受影响;如果某一类不受影响,也要显式说明不受影响或不需要修改。对于 Harness package 相关变更,还要检查 `sync`、`upgrade`、source mappings、package assets 和用户项目迁移行为。
21
21
 
22
+ RFC recalibration 本身也是 workflow task。开始处理变更前,先在 `<harnessRoot>/state/plan.yaml` 创建或选择一个足够小的 `TASK-*` open task,并设置 `phase: "RFC_RECALIBRATION"`;当前轮只处理一个 RFC 文件、一个 impact analysis 单元或一个局部补丁单元。
23
+
22
24
  ## 输入
23
25
 
24
26
  - `.docs/rfc/RFC_*.md`
@@ -33,6 +35,7 @@ description: Use during RFC_RECALIBRATION to process requirement changes with im
33
35
  - 局部更新后的 PRD 和技术方案
34
36
  - 被标记为 `pending_revision` 的受影响任务,或新增增量任务
35
37
  - Regression requirements
38
+ - 更新后的 `<harnessRoot>/state/plan.yaml`
36
39
  - 更新后的 `.docs/INDEX.md`
37
40
 
38
41
  ## 语义切片
@@ -43,6 +46,17 @@ description: Use during RFC_RECALIBRATION to process requirement changes with im
43
46
  - 对受影响产物做局部补丁,不重写无关稳定 slice。
44
47
  - 每次 RFC 影响了文档边界,都要更新 `.docs/INDEX.md` 并记录受影响任务状态。
45
48
 
49
+ ## Plan Protocol
50
+
51
+ RFC 阶段受 `plan.yaml` 管控:
52
+
53
+ 1. 没有 open task 时,先创建一个最小 `TASK-*` task,设置 `phase: "RFC_RECALIBRATION"` 和 `current_task_id`。
54
+ 2. open task 必须包含 `phase`、`docs`、`allowed_paths`、`required_gates`、`acceptance_criteria` 和 `result_docs`;`result_docs` 指向本 task 计划产出的 RFC、受影响 PRD/tech plan/test docs 或 plan update。
55
+ 3. 单个 task 的目标应足够小:一份 RFC 的 impact analysis、一个受影响 slice 的局部补丁、一组任务状态调整,或一个回归要求更新。
56
+ 4. 执行当前 task 时只编辑 `allowed_paths` 中的 RFC、受影响 facts、`.docs/INDEX.md`、overview 和 `plan.yaml`。
57
+ 5. 完成后运行 `make validate-plan` 和 task required gates;阶段出口前运行 `make validate-rfc`。
58
+ 6. task 完成后从 `plan.yaml.tasks` 移除;如果还有 pending RFC task,下一轮 `/rfc` 或 `/next` 再继续。
59
+
46
60
  ## 规则
47
61
 
48
62
  1. 影响已接受产物的需求变化,必须先进入本 Skill。
@@ -51,10 +65,13 @@ description: Use during RFC_RECALIBRATION to process requirement changes with im
51
65
  4. 受影响的 `pending` 或 `in_progress` 任务追加 revision notes。
52
66
  5. 不重写无关的稳定文档。
53
67
  6. 只有 `make validate-rfc` 通过后,才能恢复原阶段或进入 `SPRINTING`。
68
+ 7. RFC 阶段一次只执行一个 `TASK-*` task。
54
69
 
55
70
  ## 完成检查
56
71
 
57
72
  - [ ] RFC 包含有效 status 和 acceptance criteria。
73
+ - [ ] 当前 RFC 工作已绑定 `plan.yaml` 中一个最小 `TASK-*` task,并设置 `phase: "RFC_RECALIBRATION"`。
74
+ - [ ] 当前 task 已从 `plan.yaml` 移除,或因中断/blocker 保留为可恢复 open task。
58
75
  - [ ] Product impact 和 technical impact 已记录。
59
76
  - [ ] 已判断 RFC 是否需要拆分,以及是否影响其它阶段 slice。
60
77
  - [ ] 已列出 docs/state/skills/policies/templates/tools/package assets/tests/migrations/generated artifacts 的影响面。
@@ -17,10 +17,13 @@ description: Use during TESTING to produce a test matrix, run regression, and do
17
17
 
18
18
  执行回归时,优先选择能证明阶段出口的 gate。测试无法运行、环境缺失或数据不可得时,不要宣布通过,应记录 blocker、已完成检查和恢复条件。
19
19
 
20
+ 测试计划和回归证据产出本身也是 workflow task。开始测试前,先在 `<harnessRoot>/state/plan.yaml` 创建或选择一个足够小的 `TASK-*` open task,并设置 `phase: "TESTING"`;当前轮只产出一个测试矩阵 slice、一个回归批次、一个风险验证片区或一组 scoped test changes。
21
+
20
22
  如果用户明确要求并行、多 agent 或多 worktree,测试阶段可以启用 `parallel_execution`,让 worker 分别执行互不依赖的回归片区、smoke、兼容性或风险验证。worker 只提交证据和必要的 scoped test changes;最终 `.docs/07_test/**`、coverage gaps、PASS/BLOCKED 决策和阶段 gate 由主 Agent 汇总。没有用户显式要求时,测试 workflow 保持串行。
21
23
 
22
24
  ## 输入
23
25
 
26
+ - `<harnessRoot>/state/plan.yaml`
24
27
  - `.docs/01_product/`
25
28
  - `.docs/03_tech_plan/`
26
29
  - `.docs/04_implementation/`
@@ -32,6 +35,7 @@ description: Use during TESTING to produce a test matrix, run regression, and do
32
35
 
33
36
  - `.docs/07_test/TEST_PLAN.md`
34
37
  - 必要时在 `tests/` 下补充测试
38
+ - 更新后的 `<harnessRoot>/state/plan.yaml`
35
39
  - 回归测试记录
36
40
  - 覆盖缺口清单
37
41
 
@@ -43,6 +47,17 @@ description: Use during TESTING to produce a test matrix, run regression, and do
43
47
  - 如果新增测试只是覆盖同一验收标准,应更新原 test slice,不要创建重复测试计划。
44
48
  - 每次新增、拆分或合并 test slice 后,都要更新 `.docs/INDEX.md`。
45
49
 
50
+ ## Plan Protocol
51
+
52
+ 测试阶段受 `plan.yaml` 管控:
53
+
54
+ 1. 没有 open task 时,先创建一个最小 `TASK-*` task,设置 `phase: "TESTING"` 和 `current_task_id`。
55
+ 2. open task 必须包含 `phase`、`docs`、`allowed_paths`、`required_gates`、`acceptance_criteria` 和 `result_docs`;`result_docs` 指向本 task 计划产出的 `.docs/07_test/**` 文件,必要时也列出 scoped test files。
56
+ 3. 单个 task 的目标应足够小:一个测试矩阵 slice、一个回归批次、一个风险验证片区,或一组紧密相关的测试变更。
57
+ 4. 执行当前 task 时只编辑 `allowed_paths` 中的测试、测试文档、`.docs/INDEX.md`、overview 和 `plan.yaml`。
58
+ 5. 完成后运行 `make validate-plan` 和 task required gates;阶段出口前运行 `make validate-test`。
59
+ 6. task 完成后从 `plan.yaml.tasks` 移除;如果还有 pending testing task,下一轮 `/test` 或 `/next` 再继续。
60
+
46
61
  ## 规则
47
62
 
48
63
  1. 测试用例必须追溯到 PRD acceptance criteria 或 Review findings。
@@ -50,10 +65,13 @@ description: Use during TESTING to produce a test matrix, run regression, and do
50
65
  3. 如果有意延后覆盖,必须记录风险和 follow-up。
51
66
  4. 并行测试必须使用 `parallel_execution.trigger: "user_requested"`;`runtime_managed` 只在当前 runtime 支持 subagent 时使用,否则输出 `user_orchestrated` worker prompt。
52
67
  5. 宣布阶段完成前运行 `make test-all`。
68
+ 6. 测试阶段一次只执行一个 `TASK-*` task。
53
69
 
54
70
  ## 完成检查
55
71
 
56
72
  - [ ] Test matrix 已把需求映射到测试。
73
+ - [ ] 当前测试工作已绑定 `plan.yaml` 中一个最小 `TASK-*` task,并设置 `phase: "TESTING"`。
74
+ - [ ] 当前 task 已从 `plan.yaml` 移除,或因中断/blocker 保留为可恢复 open task。
57
75
  - [ ] Regression checklist 已完成。
58
76
  - [ ] 已判断 test plan / test matrix 的语义切片边界。
59
77
  - [ ] Coverage gaps 已明确。
@@ -1,5 +1,5 @@
1
- current_phase: "SPRINTING"
2
- current_task_id: "DEV-001"
1
+ current_phase: "REQUIREMENT_GATHERING"
2
+ current_task_id: "TASK-001"
3
3
  next_task_sequence: 2
4
4
  # Optional. Omit this block for normal serial workflow. Only add it when the
5
5
  # user explicitly asks for parallel / multi-agent / multi-worktree execution.
@@ -9,7 +9,7 @@ next_task_sequence: 2
9
9
  # mode: "user_orchestrated" # or "runtime_managed" when the current agent runtime can spawn subagents
10
10
  # phase: "SPRINTING"
11
11
  # coordinator: "main_agent"
12
- # linked_task_id: "DEV-001"
12
+ # linked_task_id: "TASK-001"
13
13
  # workers:
14
14
  # - id: "worker-feature"
15
15
  # writes_repo: true
@@ -33,24 +33,28 @@ next_task_sequence: 2
33
33
  # fact_source_updates:
34
34
  # - ".docs/04_implementation/"
35
35
  tasks:
36
- - id: "DEV-001"
37
- title: "实现示例任务"
36
+ - id: "TASK-001"
37
+ phase: "REQUIREMENT_GATHERING"
38
+ title: "生成示例 PRD slice"
38
39
  status: "pending"
39
40
  summary: "一句话描述任务目标和交付边界。"
40
41
  docs:
42
+ raw:
43
+ - ".docs/00_raw/example.md"
41
44
  product:
42
45
  - ".docs/01_product/example.md"
43
- tech_plan:
44
- - ".docs/03_tech_plan/example.md"
45
46
  rfc: []
46
47
  allowed_paths:
47
- - "src/**"
48
- - "tests/**"
48
+ - ".docs/00_raw/**"
49
+ - ".docs/01_product/**"
50
+ - ".docs/INDEX.md"
51
+ - "<harnessRoot>/state/plan.yaml"
49
52
  required_gates:
50
- - "make lint"
51
- - "make test-current-domain"
53
+ - "make validate-plan"
54
+ - "make docs-overview"
52
55
  acceptance_criteria:
53
56
  - "验收标准写在 open task 内,完成后从当前 plan 删除。"
54
57
  working_notes:
55
58
  - "执行现场备注只在 open task 保留。"
56
- implementation_doc: ".docs/04_implementation/example/module_or_flow.md"
59
+ result_docs:
60
+ - ".docs/01_product/example.md"
@@ -41,7 +41,7 @@ Input
41
41
 
42
42
  | Task ID | 标题(Title) | Allowed Paths | Required Gates | Implementation Doc |
43
43
  |---|---|---|---|---|
44
- | DEV-001 | | `src/**`, `tests/**` | `make lint`, `make test-current-domain` | `.docs/04_implementation/...` |
44
+ | TASK-001 | | `src/**`, `tests/**` | `make lint`, `make test-current-domain` | `.docs/04_implementation/...` |
45
45
 
46
46
  `Implementation Doc` 应指向模块、子系统或核心数据流级文档;多个 task 可以更新同一份文档,task id 和 commit 记录在该文档的 provenance / Change Log 中。
47
47
 
@@ -13,9 +13,14 @@ export const commands = {
13
13
  validate,
14
14
  "validate-harness": (args) => validate(["validate-harness", ...args]),
15
15
  "validate-current": (args) => validate(["validate-current", ...args]),
16
+ "validate-plan": (args) => validate(["validate-plan", ...args]),
16
17
  "validate-pm": (args) => validate(["validate-pm", ...args]),
17
18
  "validate-design": (args) => validate(["validate-design", ...args]),
18
19
  "validate-dev": (args) => validate(["validate-dev", ...args]),
20
+ "validate-review": (args) => validate(["validate-review", ...args]),
21
+ "validate-test": (args) => validate(["validate-test", ...args]),
22
+ "validate-release": (args) => validate(["validate-release", ...args]),
23
+ "validate-rfc": (args) => validate(["validate-rfc", ...args]),
19
24
  package: packageSource
20
25
  };
21
26
  export function help() {
@@ -26,5 +31,6 @@ export function help() {
26
31
  upgrade Run migrations and then sync
27
32
  doctor Diagnose project configuration and drift
28
33
  validate <gate> Run a Harness validation gate
34
+ validate-* Run a named gate directly, including validate-plan/review/test/release/rfc
29
35
  package <subcommand> Maintain package canonical source`);
30
36
  }
@@ -7,12 +7,18 @@ import { parseYaml } from "./yaml.js";
7
7
  const execFileAsync = promisify(execFile);
8
8
  const PARALLEL_MODES = new Set(["runtime_managed", "user_orchestrated"]);
9
9
  const PARALLEL_PHASES = new Set(["REQUIREMENT_GATHERING", "SPRINTING", "TESTING"]);
10
+ const TASK_PHASES = new Set(["REQUIREMENT_GATHERING", "ARCHITECTING", "SPRINTING", "REVIEWING", "TESTING", "RELEASING", "RFC_RECALIBRATION"]);
10
11
  const validators = {
11
12
  "validate-harness": validateHarness,
12
13
  "validate-current": validateCurrent,
14
+ "validate-plan": validatePlan,
13
15
  "validate-pm": validatePm,
14
16
  "validate-design": validateDesign,
15
- "validate-dev": validateDev
17
+ "validate-dev": validateDev,
18
+ "validate-review": validateReview,
19
+ "validate-test": validateTest,
20
+ "validate-release": validateRelease,
21
+ "validate-rfc": validateRfc
16
22
  };
17
23
  export async function runValidator(projectRoot, gate) {
18
24
  const normalized = normalizeGate(gate);
@@ -51,14 +57,19 @@ async function validateCurrent(projectRoot) {
51
57
  const gateByPhase = {
52
58
  REQUIREMENT_GATHERING: "validate-pm",
53
59
  ARCHITECTING: "validate-design",
54
- SPRINTING: "validate-dev"
60
+ SPRINTING: "validate-dev",
61
+ REVIEWING: "validate-review",
62
+ TESTING: "validate-test",
63
+ RELEASING: "validate-release",
64
+ RFC_RECALIBRATION: "validate-rfc"
55
65
  };
56
66
  return runValidator(projectRoot, gateByPhase[current] ?? "validate-harness");
57
67
  }
58
68
  async function validatePm(projectRoot) {
69
+ const plan = await validatePlanState(projectRoot, false);
59
70
  const docs = await markdownFiles(path.join(projectRoot, ".docs/01_product"));
60
71
  const text = await combinedText(docs);
61
- const errors = [];
72
+ const errors = [...plan.errors];
62
73
  if (docs.length === 0)
63
74
  errors.push("No PRD deliverables found");
64
75
  if (!containsAny(text, ["acceptance", "验收"]))
@@ -70,10 +81,11 @@ async function validatePm(projectRoot) {
70
81
  return { info: [`validate-pm checked ${docs.length} file(s)`], errors };
71
82
  }
72
83
  async function validateDesign(projectRoot) {
84
+ const plan = await validatePlanState(projectRoot, false);
73
85
  const architecture = await markdownFiles(path.join(projectRoot, ".docs/02_architecture"));
74
86
  const techPlan = await markdownFiles(path.join(projectRoot, ".docs/03_tech_plan"));
75
87
  const text = await combinedText([...architecture, ...techPlan]);
76
- const errors = [];
88
+ const errors = [...plan.errors];
77
89
  if (architecture.length === 0)
78
90
  errors.push("No architecture deliverables found");
79
91
  if (techPlan.length === 0)
@@ -86,7 +98,80 @@ async function validateDesign(projectRoot) {
86
98
  errors.push("Design must include task breakdown");
87
99
  return { info: [`validate-design checked ${architecture.length + techPlan.length} file(s)`], errors };
88
100
  }
101
+ async function validatePlan(projectRoot) {
102
+ const plan = await validatePlanState(projectRoot, true);
103
+ const pathErrors = await validateChangedPaths(projectRoot, plan.plan, true);
104
+ return { info: [`validate-plan checked ${plan.taskCount} task(s)`], errors: [...plan.errors, ...pathErrors] };
105
+ }
89
106
  async function validateDev(projectRoot) {
107
+ const plan = await validatePlanState(projectRoot, false);
108
+ return { info: [`validate-dev checked ${plan.taskCount} task(s)`], errors: plan.errors };
109
+ }
110
+ async function validateReview(projectRoot) {
111
+ const plan = await validatePlanState(projectRoot, false);
112
+ const text = (await readText(path.join(projectRoot, ".docs/06_review/REVIEW_REPORT.md"))).toLowerCase();
113
+ const errors = [...plan.errors];
114
+ if (!containsAny(text, ["finding", "发现", "风险"]))
115
+ errors.push("Review report must include findings or risks");
116
+ if (!containsAny(text, ["test gap", "测试缺口", "coverage"]))
117
+ errors.push("Review report must include test gaps or coverage notes");
118
+ if (!containsAny(text, ["pass", "blocked", "通过", "阻塞"]))
119
+ errors.push("Review report must include PASS/BLOCKED decision");
120
+ return { info: ["validate-review checked review report"], errors };
121
+ }
122
+ async function validateTest(projectRoot) {
123
+ const plan = await validatePlanState(projectRoot, false);
124
+ const text = (await readText(path.join(projectRoot, ".docs/07_test/TEST_PLAN.md"))).toLowerCase();
125
+ const errors = [...plan.errors];
126
+ if (!containsAny(text, ["matrix", "矩阵"]))
127
+ errors.push("Test plan must include a test matrix");
128
+ if (!containsAny(text, ["regression", "回归"]))
129
+ errors.push("Test plan must include regression coverage");
130
+ if (!containsAny(text, ["coverage gap", "覆盖缺口", "gap"]))
131
+ errors.push("Test plan must include coverage gaps");
132
+ if (!containsAny(text, ["pass", "blocked", "通过", "阻塞"]))
133
+ errors.push("Test plan must include PASS/BLOCKED decision");
134
+ return { info: ["validate-test checked test plan"], errors };
135
+ }
136
+ async function validateRelease(projectRoot) {
137
+ const plan = await validatePlanState(projectRoot, false);
138
+ const docs = await markdownFiles(path.join(projectRoot, ".docs/08_release"));
139
+ const text = await combinedText(docs);
140
+ const errors = [...plan.errors];
141
+ if (docs.length === 0)
142
+ errors.push("No release deliverables found");
143
+ if (!containsAny(text, ["release", "发布"]))
144
+ errors.push("Release docs must include release notes");
145
+ if (!containsAny(text, ["smoke", "冒烟"]))
146
+ errors.push("Release docs must include smoke test evidence");
147
+ if (!containsAny(text, ["rollback", "回滚"]))
148
+ errors.push("Release docs must include rollback plan");
149
+ return { info: [`validate-release checked ${docs.length} file(s)`], errors };
150
+ }
151
+ async function validateRfc(projectRoot) {
152
+ const plan = await validatePlanState(projectRoot, false);
153
+ const docs = await markdownFiles(path.join(projectRoot, ".docs/rfc"));
154
+ const text = await combinedText(docs);
155
+ const errors = [...plan.errors];
156
+ if (docs.length === 0)
157
+ errors.push("No RFC documents found");
158
+ if (!containsAny(text, ["background", "背景"]))
159
+ errors.push("RFC must include background");
160
+ if (!containsAny(text, ["product impact", "产品影响"]))
161
+ errors.push("RFC must include product impact");
162
+ if (!containsAny(text, ["technical impact", "技术影响"]))
163
+ errors.push("RFC must include technical impact candidates");
164
+ if (!containsAny(text, ["regression", "回归"]))
165
+ errors.push("RFC must include regression requirements");
166
+ const statuses = [...text.matchAll(/status:\s*([a-z_]+)/g)].map((match) => match[1].toUpperCase());
167
+ if (statuses.length === 0)
168
+ errors.push("RFC must include a Status line");
169
+ const invalidStatuses = statuses.filter((status) => !["DRAFT", "APPLIED", "VERIFIED", "ARCHIVED"].includes(status));
170
+ if (invalidStatuses.length > 0)
171
+ errors.push(`Invalid RFC status: ${invalidStatuses.join(", ")}`);
172
+ return { info: [`validate-rfc checked ${docs.length} file(s)`], errors };
173
+ }
174
+ async function validatePlanState(projectRoot, allowOpen) {
90
175
  const errors = [];
91
176
  const root = await harnessRoot(projectRoot);
92
177
  const tasksData = await readYamlObject(path.join(projectRoot, root, "state", "plan.yaml"));
@@ -97,16 +182,37 @@ async function validateDev(projectRoot) {
97
182
  errors.push("plan.yaml must define positive integer next_task_sequence");
98
183
  }
99
184
  const open = tasks.filter((task) => ["pending", "in_progress", "blocked", "pending_revision"].includes(String(task.status)));
100
- if (open.length > 0)
185
+ if (!allowOpen && open.length > 0)
101
186
  errors.push(`Open tasks remain: ${open.map((task) => task.id).join(", ")}`);
102
187
  let maxTaskSequence = 0;
103
- for (const task of tasks) {
104
- for (const field of ["id", "title", "status", "summary", "implementation_doc"]) {
188
+ tasks.forEach((task, index) => {
189
+ if (!isRecord(task)) {
190
+ errors.push(`Task #${index + 1} must be a mapping`);
191
+ return;
192
+ }
193
+ for (const field of ["id", "title", "status", "summary"]) {
105
194
  if (!task[field])
106
195
  errors.push(`Task missing ${field}: ${String(task.id ?? "unknown")}`);
107
196
  }
108
197
  const taskId = String(task.id ?? "");
109
- const match = taskId.match(/^DEV-(\d+)$/);
198
+ if (!/^[A-Z]+-\d+$/.test(taskId)) {
199
+ errors.push(`${taskId || `Task #${index + 1}`} id must match PREFIX-###`);
200
+ }
201
+ if (taskId.startsWith("TASK-") && !TASK_PHASES.has(String(task.phase ?? ""))) {
202
+ errors.push(`${taskId} must define valid phase`);
203
+ }
204
+ else if (task.phase !== undefined && !TASK_PHASES.has(String(task.phase))) {
205
+ errors.push(`${taskId} has invalid phase: ${String(task.phase)}`);
206
+ }
207
+ if (!["pending", "in_progress", "done", "blocked", "pending_revision", "cancelled"].includes(String(task.status))) {
208
+ errors.push(`${String(task.id ?? `Task #${index + 1}`)} has invalid status: ${String(task.status)}`);
209
+ }
210
+ const hasImplementationDoc = typeof task.implementation_doc === "string" && task.implementation_doc.trim().length > 0;
211
+ const hasResultDocs = Array.isArray(task.result_docs) && task.result_docs.length > 0;
212
+ if (!hasImplementationDoc && !hasResultDocs) {
213
+ errors.push(`${String(task.id ?? `Task #${index + 1}`)} must define implementation_doc or result_docs`);
214
+ }
215
+ const match = taskId.match(/^[A-Z]+-(\d+)$/);
110
216
  if (match) {
111
217
  maxTaskSequence = Math.max(maxTaskSequence, Number(match[1]));
112
218
  }
@@ -118,25 +224,35 @@ async function validateDev(projectRoot) {
118
224
  if (!task[field])
119
225
  errors.push(`Open task ${task.id} missing ${field}`);
120
226
  }
227
+ if (!isRecord(task.docs)) {
228
+ errors.push(`${task.id} docs must be a mapping`);
229
+ }
121
230
  if (!Array.isArray(task.allowed_paths) || task.allowed_paths.length === 0) {
122
231
  errors.push(`Open task ${task.id} must define allowed_paths`);
123
232
  }
124
233
  if (!Array.isArray(task.required_gates) || task.required_gates.length === 0) {
125
234
  errors.push(`Open task ${task.id} must define required_gates`);
126
235
  }
236
+ if (!Array.isArray(task.acceptance_criteria) || task.acceptance_criteria.length === 0) {
237
+ errors.push(`Open task ${task.id} must define acceptance_criteria`);
238
+ }
127
239
  }
128
240
  else {
129
241
  errors.push(`Completed task ${task.id} must not remain in plan.yaml`);
130
- for (const field of ["docs", "allowed_paths", "required_gates", "acceptance_criteria", "working_notes", "gate_result"]) {
131
- if (task[field])
242
+ for (const field of ["docs", "allowed_paths", "required_gates", "acceptance_criteria", "working_notes", "gate_result", "result_docs"]) {
243
+ if (field in task)
132
244
  errors.push(`Closed task ${task.id} must not retain ${field}`);
133
245
  }
134
246
  }
135
- }
247
+ });
136
248
  if (Number.isInteger(nextTaskSequence) && Number(nextTaskSequence) <= maxTaskSequence) {
137
249
  errors.push("next_task_sequence must be greater than task ids currently in plan.yaml");
138
250
  }
139
- return { info: [`validate-dev checked ${tasks.length} task(s)`], errors };
251
+ const currentTaskId = String(tasksData.current_task_id ?? "");
252
+ if (currentTaskId && !tasks.some((task) => isRecord(task) && task.id === currentTaskId)) {
253
+ errors.push(`current_task_id does not match a task: ${currentTaskId}`);
254
+ }
255
+ return { taskCount: tasks.length, errors, plan: tasksData };
140
256
  }
141
257
  function validateParallelExecutionContract(plan, errors) {
142
258
  const contract = plan.parallel_execution;
@@ -227,6 +343,36 @@ function validateParallelExecutionContract(plan, errors) {
227
343
  errors.push("parallel_execution.integration.fact_source_updates must be a non-empty list");
228
344
  }
229
345
  }
346
+ async function validateChangedPaths(projectRoot, plan, allowOpen) {
347
+ if (!allowOpen)
348
+ return [];
349
+ const currentTaskId = String(plan.current_task_id ?? "");
350
+ if (!currentTaskId)
351
+ return [];
352
+ const tasks = Array.isArray(plan.tasks) ? plan.tasks : [];
353
+ const task = tasks.find((candidate) => isRecord(candidate) && candidate.id === currentTaskId);
354
+ if (!isRecord(task))
355
+ return [`current_task_id does not match a task: ${currentTaskId}`];
356
+ if (!Array.isArray(task.allowed_paths))
357
+ return [`${currentTaskId} must define allowed_paths`];
358
+ const patterns = task.allowed_paths.map((pattern) => String(pattern).replace("<harnessRoot>", ".codex"));
359
+ const changed = await changedFiles(projectRoot);
360
+ const blocked = changed.filter((file) => !matchesAny(file, patterns));
361
+ return blocked.length > 0 ? [`Changed files outside current task allowed_paths: ${blocked.join(", ")}`] : [];
362
+ }
363
+ function matchesAny(file, patterns) {
364
+ return patterns.some((pattern) => matchesGlob(file, pattern));
365
+ }
366
+ function matchesGlob(file, pattern) {
367
+ const normalizedFile = file.replace(/\\/g, "/");
368
+ const normalizedPattern = pattern.replace(/\\/g, "/");
369
+ if (normalizedFile === normalizedPattern)
370
+ return true;
371
+ if (normalizedPattern.endsWith("/**") && normalizedFile.startsWith(normalizedPattern.slice(0, -3)))
372
+ return true;
373
+ const escaped = normalizedPattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*\*/g, ".*").replace(/\*/g, "[^/]*");
374
+ return new RegExp(`^${escaped}$`).test(normalizedFile);
375
+ }
230
376
  function isRecord(value) {
231
377
  return typeof value === "object" && value !== null && !Array.isArray(value);
232
378
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-project-sdlc",
3
- "version": "0.1.9",
3
+ "version": "0.1.10",
4
4
  "description": "CLI and canonical assets for the AI SDLC Harness workflow.",
5
5
  "type": "module",
6
6
  "bin": {