agent-project-sdlc 0.1.13 → 0.1.14
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 +8 -2
- package/assets/agents/AGENTS_CORE.md +2 -2
- package/assets/docs/README.md +7 -3
- package/assets/make/sdlc-harness.mk +4 -6
- package/assets/policies/allowed_paths.yaml +1 -0
- package/assets/policies/gates.yaml +2 -2
- package/assets/policies/phase_contracts.yaml +10 -7
- package/assets/skills/pjsdlc_dev_sprint/SKILL.md +16 -14
- package/assets/skills/pjsdlc_manager/SKILL.md +2 -2
- package/assets/skills/pjsdlc_release_manager/SKILL.md +16 -16
- package/assets/skills/pjsdlc_reviewer/SKILL.md +2 -1
- package/assets/skills/pjsdlc_rfc_recalibrate/SKILL.md +7 -2
- package/assets/skills/pjsdlc_tester/SKILL.md +23 -15
- package/assets/templates/RELEASE_TEMPLATE.md +8 -1
- package/assets/templates/RFC_TEMPLATE.md +8 -1
- package/assets/templates/TEST_CASES_TEMPLATE.md +24 -0
- package/assets/templates/TEST_REPORT_TEMPLATE.md +5 -1
- package/assets/templates/TEST_STRATEGY_TEMPLATE.md +27 -0
- package/dist/lib/validators.js +130 -16
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -76,6 +76,8 @@ The CLI does not promise to automatically launch Codex agents. Workers do not ne
|
|
|
76
76
|
|
|
77
77
|
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.
|
|
78
78
|
|
|
79
|
+
Release docs are current-state facts, not a version ledger. New release work should update `.docs/08_release/CURRENT_RELEASE.md`; `validate-release` accepts legacy versioned release docs only for existing projects that have not migrated yet.
|
|
80
|
+
|
|
79
81
|
The generic rule is that any workflow promoting a draft task into a formal `TASK-*` in `plan.yaml` must remove the source draft from its draft queue in the same state update. The formal task is then recovered only from `plan.yaml`; completed history lives in implementation docs, git, PR and CI records. The built-in Harness draft queue is currently `plan.draft.yaml.tasks[]`, which means unadopted development drafts only. `/devloop` treats the development queue as exhausted only when both `plan.yaml.tasks[]` and `plan.draft.yaml.tasks[]` have no executable task.
|
|
80
82
|
|
|
81
83
|
Before development starts, `ARCHITECTING` can return to `REQUIREMENT_GATHERING` for PRD edits. The manager uses `python3 tools/transition.py --to REQUIREMENT_GATHERING`, the PM workflow updates the PRD through one `TASK-*`, then `validate-pm` and `python3 tools/transition.py --to ARCHITECTING` return the project to design. Requirement changes after `SPRINTING` still use RFC recalibration.
|
|
@@ -84,7 +86,11 @@ Before development starts, `ARCHITECTING` can return to `REQUIREMENT_GATHERING`
|
|
|
84
86
|
|
|
85
87
|
SPRINTING Definition of Done includes runnable entry/exit boundaries. API, CLI, server route, adapter, worker, provider, config-contract and fixture/live boundaries promised by a technical plan or task must be implemented or marked `BLOCKED` during development. REVIEWING treats missing entry/exit as blocking, and TESTING only exercises existing entrypoints; it must not add product runtime, bootstrap, provider adapter, deploy code or package runtime scripts.
|
|
86
88
|
|
|
87
|
-
`validate-
|
|
89
|
+
`make validate-dev` and `npx sdlc-harness validate-dev` are in-development SPRINTING gates. They allow the current `current_task_id` open task to remain in `plan.yaml` while checking that it is a valid `phase: "SPRINTING"` task with `docs`, `allowed_paths`, `required_gates`, `acceptance_criteria`, `implementation_doc`, scoped dirty files, an empty `plan.draft.yaml` queue and linked runnable-entry implementation docs. `make validate-current` and `/advance` are phase-exit gates; before moving to REVIEWING, the implementation commit and completion ledger must be done and no open task may remain.
|
|
90
|
+
|
|
91
|
+
`validate-test` keeps its command name as the TESTING phase gate. `.docs/07_test/TEST_STRATEGY.md` describes scope, environment, priority and execution strategy; `.docs/07_test/TEST_CASES.md` describes cases bound to real runnable entry/exit; `.docs/07_test/TEST_REPORT.md` only records executed TESTING evidence, test matrix, regression evidence, runnable entry/exit coverage, coverage gaps and final decision. `validate-test` only accepts `TEST_REPORT.md`; it no longer treats `TEST_PLAN.md` as a report fallback.
|
|
92
|
+
|
|
93
|
+
Do not create formal `.docs/07_test/**` test cases or reports before development has delivered testable entry/exit. When an RFC changes the technical route, entry/exit or acceptance boundary, review `.docs/07_test/**` and remove superseded test results from current facts and `.docs/INDEX.md`.
|
|
88
94
|
|
|
89
95
|
## ADR And Memory Boundaries
|
|
90
96
|
|
|
@@ -112,4 +118,4 @@ make docs-overview
|
|
|
112
118
|
|
|
113
119
|
## More Information
|
|
114
120
|
|
|
115
|
-
The source repository keeps the full product and architecture specification in `PROJECT_SPEC.md`, with implementation and release
|
|
121
|
+
The source repository keeps the full product and architecture specification in `PROJECT_SPEC.md`, with implementation facts and the current release status under `.docs/**`.
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
- 实现文档:`.docs/04_implementation/`
|
|
16
16
|
- Review 文档:`.docs/06_review/`
|
|
17
17
|
- 测试文档:`.docs/07_test/`
|
|
18
|
-
- 发布文档:`.docs/08_release
|
|
18
|
+
- 发布文档:`.docs/08_release/CURRENT_RELEASE.md`(当前发布状态;历史发布动作由 git tag、release commit、registry 或外部发布系统追溯)
|
|
19
19
|
- RFC 文档:`.docs/rfc/`
|
|
20
20
|
- 全局文档索引:`.docs/INDEX.md`
|
|
21
21
|
- Harness authoring skills(如果存在):`.codex/skills/authoring/`,只在维护 Harness/workflow/npm package 源码或本仓库自举规则时读取,不作为用户项目默认分发内容
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
- `current_phase` 只保存在 `lifecycle.yaml`;不要在 `plan.yaml`、`plan.draft.yaml` 或 `parallel_execution` 中重复保存当前阶段。
|
|
35
35
|
- 新建任务统一使用 `TASK-*` id,并通过 `phase` 标明属于 `REQUIREMENT_GATHERING`、`ARCHITECTING`、`SPRINTING`、`REVIEWING`、`TESTING`、`RELEASING` 或 `RFC_RECALIBRATION`;历史 `PRD-*`、`DES-*`、`DEV-*` 只作为兼容旧记录和旧提交的 provenance。
|
|
36
36
|
- `next_task_sequence` 记录下一个可分配的 `TASK-*` 序号,避免删除历史 task 后发生 id 冲突。
|
|
37
|
-
- 文档、Review、测试、发布和 RFC 类 task 使用 `result_docs` 指向本 task 产出的 PRD、architecture、tech plan、ADR、review report、test report、release
|
|
37
|
+
- 文档、Review、测试、发布和 RFC 类 task 使用 `result_docs` 指向本 task 产出的 PRD、architecture、tech plan、ADR、review report、test report、current release status、RFC 或 `plan.draft.yaml`;开发 task 使用 `implementation_doc` 指向模块级实现事实。
|
|
38
38
|
- task 完成并写入或更新相关事实源后,从 `plan.yaml` 的 `tasks` 列表移除该 task;不要长期保留 done/cancelled task 摘要。
|
|
39
39
|
- `plan.draft.yaml` 是架构阶段生成的计划草案,不自动覆盖 `plan.yaml`。
|
|
40
40
|
- `plan.draft.yaml` 不保存 `current_phase` 或 `current_task_id`,只保存待采用的 task 草案和必要的 `next_task_sequence`。
|
package/assets/docs/README.md
CHANGED
|
@@ -107,7 +107,11 @@ Agent 会读取 `<harnessRoot>/state/lifecycle.yaml` 和 `<harnessRoot>/state/pl
|
|
|
107
107
|
|
|
108
108
|
SPRINTING 的 Definition of Done 包含可运行入口/出口:技术方案或 task 承诺的 API、CLI、server route、adapter、worker、provider、配置契约和 fixture/live 边界必须在开发阶段实现或明确 `BLOCKED`。REVIEWING 会把缺少入口/出口作为阻断项;TESTING 只调用既有入口做输入输出验证,不能新增 product runtime、bootstrap、provider adapter、deploy 或 package runtime script。
|
|
109
109
|
|
|
110
|
-
`validate-
|
|
110
|
+
`make validate-dev` / `npx sdlc-harness validate-dev` 是 SPRINTING 开发中 gate:当前 `current_task_id` 指向的 open task 可以继续留在 `plan.yaml`,validator 会检查它是否是合法 `phase: "SPRINTING"` task、是否具备 `docs`、`allowed_paths`、`required_gates`、`acceptance_criteria`、`implementation_doc`,并校验 dirty files、`plan.draft.yaml` 和 implementation doc。`make validate-current` / `/advance` 是阶段出口 gate;进入 REVIEWING 前仍必须先完成 implementation commit 和 completion ledger,把 open task 从 `plan.yaml` 移除。
|
|
111
|
+
|
|
112
|
+
`validate-test` 仍然是 TESTING 阶段 gate 名称。`.docs/07_test/TEST_STRATEGY.md` 描述测试范围、环境、优先级和执行策略;`.docs/07_test/TEST_CASES.md` 描述绑定真实 runnable entry/exit 的测试用例;`.docs/07_test/TEST_REPORT.md` 只记录 TESTING 阶段实际执行后的 test matrix、regression evidence、runnable entry/exit coverage、coverage gaps 和 final decision。`validate-test` 只接受 `TEST_REPORT.md`,不会把 `TEST_PLAN.md` 当作 report fallback。
|
|
113
|
+
|
|
114
|
+
开发尚未完成可测试 entry/exit 前,不要在 `.docs/07_test/**` 生成正式测试用例或测试报告;验收思路应保留在 PRD acceptance criteria、tech plan verification strategy 或非执行性草稿里。RFC 改变技术路线、entry/exit 或验收边界时,必须审查 `.docs/07_test/**`,把被新方案 supersede 的旧测试结果从当前事实源和 `.docs/INDEX.md` 中移除。
|
|
111
115
|
|
|
112
116
|
### ADR 与 Memory 的边界
|
|
113
117
|
|
|
@@ -233,8 +237,8 @@ make docs-overview
|
|
|
233
237
|
| `.docs/04_implementation/` | 模块、子系统和核心数据流的真实实现事实 |
|
|
234
238
|
| `.docs/05_decisions/` | ADR,长期关键决策及其背景、备选方案、理由和后果 |
|
|
235
239
|
| `.docs/06_review/` | Review 报告 |
|
|
236
|
-
| `.docs/07_test/` |
|
|
237
|
-
| `.docs/08_release/` |
|
|
240
|
+
| `.docs/07_test/` | 测试策略、测试用例、执行后测试报告、回归证据和覆盖缺口 |
|
|
241
|
+
| `.docs/08_release/` | 当前发布状态、smoke evidence、回滚方案和已知限制 |
|
|
238
242
|
| `.docs/rfc/` | 需求变更和影响分析 |
|
|
239
243
|
|
|
240
244
|
`overview.md` 是生成物,用于浏览和阶段交接;Markdown slices 和 `.docs/INDEX.md` 才是事实源。
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
PYTHON ?= python3
|
|
2
|
+
SDLC_HARNESS ?= npx sdlc-harness
|
|
2
3
|
|
|
3
4
|
.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
5
|
|
|
@@ -14,8 +15,8 @@ help:
|
|
|
14
15
|
@echo " make validate-design 校验架构设计、技术方案和任务草案"
|
|
15
16
|
@echo " make validate-dev 校验 sprint 任务状态、draft 消费、路径、代码 gate 和实现文档"
|
|
16
17
|
@echo " make validate-review 校验 Review report"
|
|
17
|
-
@echo " make validate-test 校验
|
|
18
|
-
@echo " make validate-release 校验 release
|
|
18
|
+
@echo " make validate-test 校验 TEST_REPORT 执行证据和测试阶段边界"
|
|
19
|
+
@echo " make validate-release 校验 current release status、smoke result 和 rollback plan"
|
|
19
20
|
@echo " make validate-rfc 校验 RFC 产物并运行完整回归入口"
|
|
20
21
|
|
|
21
22
|
status:
|
|
@@ -48,12 +49,9 @@ validate-design:
|
|
|
48
49
|
$(PYTHON) tools/validate_plan_draft.py
|
|
49
50
|
|
|
50
51
|
validate-dev:
|
|
51
|
-
$(
|
|
52
|
-
$(PYTHON) tools/validate_dev_state.py
|
|
53
|
-
$(PYTHON) tools/validate_allowed_paths.py
|
|
52
|
+
$(SDLC_HARNESS) validate-dev
|
|
54
53
|
$(MAKE) lint
|
|
55
54
|
$(MAKE) test-current-domain
|
|
56
|
-
$(PYTHON) tools/validate_task_docs.py
|
|
57
55
|
|
|
58
56
|
validate-review:
|
|
59
57
|
test -f .docs/06_review/REVIEW_REPORT.md
|
|
@@ -43,13 +43,13 @@ gates:
|
|
|
43
43
|
|
|
44
44
|
validate-test:
|
|
45
45
|
command: "make validate-test"
|
|
46
|
-
purpose: "
|
|
46
|
+
purpose: "验证 TEST_REPORT.md 执行证据、测试矩阵、回归记录、覆盖缺口和阶段边界"
|
|
47
47
|
required_for:
|
|
48
48
|
- "TESTING"
|
|
49
49
|
|
|
50
50
|
validate-release:
|
|
51
51
|
command: "make validate-release"
|
|
52
|
-
purpose: "
|
|
52
|
+
purpose: "验证当前发布状态、Smoke Test、部署检查和回滚方案"
|
|
53
53
|
required_for:
|
|
54
54
|
- "RELEASING"
|
|
55
55
|
|
|
@@ -45,7 +45,7 @@ phases:
|
|
|
45
45
|
- "REQUIREMENT_GATHERING"
|
|
46
46
|
|
|
47
47
|
SPRINTING:
|
|
48
|
-
goal: "
|
|
48
|
+
goal: "按任务状态执行开发、消费已采用草案、开发验证和实现文档沉淀"
|
|
49
49
|
role: "developer"
|
|
50
50
|
skill: "pjsdlc_dev_sprint"
|
|
51
51
|
inputs:
|
|
@@ -81,7 +81,7 @@ phases:
|
|
|
81
81
|
next: "TESTING"
|
|
82
82
|
|
|
83
83
|
TESTING:
|
|
84
|
-
goal: "
|
|
84
|
+
goal: "基于已交付 entry/exit 形成测试策略、测试用例、执行报告、回归证据和覆盖缺口结论"
|
|
85
85
|
role: "tester"
|
|
86
86
|
skill: "pjsdlc_tester"
|
|
87
87
|
inputs:
|
|
@@ -92,14 +92,16 @@ phases:
|
|
|
92
92
|
- ".docs/06_review/"
|
|
93
93
|
outputs:
|
|
94
94
|
- "<harnessRoot>/state/plan.yaml"
|
|
95
|
-
- ".docs/07_test/"
|
|
95
|
+
- ".docs/07_test/TEST_STRATEGY.md"
|
|
96
|
+
- ".docs/07_test/TEST_CASES.md"
|
|
97
|
+
- ".docs/07_test/TEST_REPORT.md"
|
|
96
98
|
- "tests/"
|
|
97
99
|
gates:
|
|
98
100
|
- "make validate-test"
|
|
99
101
|
next: "RELEASING"
|
|
100
102
|
|
|
101
103
|
RELEASING:
|
|
102
|
-
goal: "
|
|
104
|
+
goal: "当前发布状态、发布检查和回滚方案"
|
|
103
105
|
role: "release_manager"
|
|
104
106
|
skill: "pjsdlc_release_manager"
|
|
105
107
|
inputs:
|
|
@@ -108,7 +110,7 @@ phases:
|
|
|
108
110
|
- "build artifacts"
|
|
109
111
|
outputs:
|
|
110
112
|
- "<harnessRoot>/state/plan.yaml"
|
|
111
|
-
- ".docs/08_release/"
|
|
113
|
+
- ".docs/08_release/CURRENT_RELEASE.md"
|
|
112
114
|
gates:
|
|
113
115
|
- "make validate-release"
|
|
114
116
|
next: "COMPLETED"
|
|
@@ -118,14 +120,14 @@ phases:
|
|
|
118
120
|
role: "manager"
|
|
119
121
|
skill: "pjsdlc_manager"
|
|
120
122
|
inputs:
|
|
121
|
-
- ".docs/08_release/"
|
|
123
|
+
- ".docs/08_release/CURRENT_RELEASE.md"
|
|
122
124
|
outputs:
|
|
123
125
|
- "<harnessRoot>/state/lifecycle.yaml"
|
|
124
126
|
gates: []
|
|
125
127
|
next: "IDLE"
|
|
126
128
|
|
|
127
129
|
RFC_RECALIBRATION:
|
|
128
|
-
goal: "
|
|
130
|
+
goal: "处理需求变更、影响分析、测试事实源清理、局部补丁和任务回退"
|
|
129
131
|
role: "rfc_owner"
|
|
130
132
|
skill: "pjsdlc_rfc_recalibrate"
|
|
131
133
|
inputs:
|
|
@@ -135,6 +137,7 @@ phases:
|
|
|
135
137
|
- "<harnessRoot>/state/plan.yaml"
|
|
136
138
|
outputs:
|
|
137
139
|
- ".docs/rfc/"
|
|
140
|
+
- ".docs/07_test/"
|
|
138
141
|
- "<harnessRoot>/state/plan.yaml"
|
|
139
142
|
- ".docs/INDEX.md"
|
|
140
143
|
gates:
|
|
@@ -19,7 +19,7 @@ description: Use during SPRINTING to execute one task from plan.yaml, respecting
|
|
|
19
19
|
|
|
20
20
|
`/dev` 和 `/devloop` 是开发阶段的两个入口。`/dev` 创建或选择下一个最小 `TASK-*` development task,设置 `phase: "SPRINTING"`,并只完成一个 task 闭环后停止。通用规则是从任何 draft queue promote 正式 `TASK-*` 时都必须同次消费源 draft;当前开发阶段的内置 draft queue 是 `plan.draft.yaml.tasks[]`,因此如果这个 task 来自 `plan.draft.yaml.tasks[]`,promote 时必须同次删除源 draft,避免已采用草案继续显示为 `pending`。`/devloop` 连续运行 `/dev`,直到 `plan.yaml.tasks[]` 和 `plan.draft.yaml.tasks[]` 都没有明确可创建/执行的任务,或遇到需求、架构、allowed_paths、gate、commit/push blocker。
|
|
21
21
|
|
|
22
|
-
实现时遵循小步闭环:先检查 `git status`,确认工作区没有未归属到当前 task 的脏变更;再定位相关代码和测试,做必要修改,运行 gate,修复失败,写入或更新相关 implementation doc
|
|
22
|
+
实现时遵循小步闭环:先检查 `git status`,确认工作区没有未归属到当前 task 的脏变更;再定位相关代码和测试,做必要修改,运行 gate,修复失败,写入或更新相关 implementation doc 并刷新文档派生视图。直接运行 `make validate-dev` 或 `npx sdlc-harness validate-dev` 是开发中 gate,允许当前 `SPRINTING` task 仍然 open,并校验 `current_task_id`、task 合同、dirty files、draft queue 和 implementation doc。此时先不要从 `plan.yaml` 移除当前 task,要在当前 task 仍位于 `plan.yaml` 时创建 task implementation commit;随后再移除 task,创建 task completion ledger commit,并 push 两个 commit。`make validate-current` / `/advance` 是阶段出口 gate,必须在 open task 已移除后才通过。不要顺手重构、重排格式或处理无关问题;如果发现无关风险,只记录或报告。
|
|
23
23
|
|
|
24
24
|
如果用户明确要求并行、多 agent 或多 worktree,开发阶段可以启用可选 `parallel_execution`。主 Agent 先创建合同,声明每个 worker 的 `branch`、`worktree`、`owned_paths`、`forbidden_paths`、`expected_output` 和 `required_gates`。worker 可以在各自 owned paths 内实现,但不得直接修改 `plan.yaml`、`lifecycle.yaml`、`.docs/INDEX.md`、overview 或最终 implementation doc。主 Agent 负责 review、merge/cherry-pick、运行总 gate、更新事实源和完成两段提交。没有用户显式要求时,继续使用串行 `/dev` 或 `/devloop`。
|
|
25
25
|
|
|
@@ -55,6 +55,7 @@ description: Use during SPRINTING to execute one task from plan.yaml, respecting
|
|
|
55
55
|
- 一个开发 task 默认对应一个主要 implementation commit 和一个轻量 completion ledger commit。implementation commit message 应包含 task id,例如 `TASK-003: implement login rate limit`;push 成功前,不进入下一个 task。
|
|
56
56
|
- 本 Skill 不直接重切 PRD 或 tech plan;如果发现上游语义边界错误,进入 `BLOCKED`、创建 RFC,或请求回到 `ARCHITECTING`。
|
|
57
57
|
- gate 通过后调用 `pjsdlc_implementation_doc`,由该 Skill 按真实实现更新或新增 `.docs/04_implementation/` 模块级 slice。
|
|
58
|
+
- direct dev gate 与 phase-exit gate 语义不同:`validate-dev` 支持当前 open `SPRINTING` task;`validate-current` 在 `SPRINTING` 下仍会拒绝 open task,提示先完成 implementation commit 和 completion ledger。
|
|
58
59
|
- 如果一个任务实际变成多个独立实现边界,应停止扩大范围,拆分后续任务或回到任务规划。
|
|
59
60
|
- `/dev` 是单任务执行入口:没有 open task 时,先根据 PRD、architecture、tech plan 和 `plan.draft.yaml` 创建一个最小 `TASK-*` open task;如果从 `plan.draft.yaml.tasks[]` 采用 draft,必须同次从 draft 列表删除源项;已有 open task 时,直接执行该 task;完成后停止。
|
|
60
61
|
- `/devloop` 是连续执行入口:每完成一个 task 并 push 两段提交后,重新读取 lifecycle、`plan.yaml`、`plan.draft.yaml`、PRD、architecture 和 tech plan,再决定是否创建/执行下一个最小 task;没有 open task 且没有未采用 draft,或出现 blocker 时停止并报告。
|
|
@@ -80,19 +81,20 @@ done task 的执行流水不在当前 `plan.yaml` 长期保留,也不是默认
|
|
|
80
81
|
2. 开始修改前检查 `git status`;如果存在不属于当前 task 的未提交变更,先完成对应 task 的 commit/push,或报告 blocker,不要混入当前 task。
|
|
81
82
|
3. 只编辑当前 task 的 `allowed_paths` 允许的文件,以及 `SPRINTING` 阶段允许的 Harness 记账文件;如果本轮 promote draft,允许同步编辑 `<harnessRoot>/state/plan.draft.yaml` 来消费源 draft。
|
|
82
83
|
4. 必须运行当前 task 的 `required_gates`。
|
|
83
|
-
5.
|
|
84
|
-
6. 如果 gate
|
|
85
|
-
7. gate
|
|
86
|
-
8.
|
|
87
|
-
9.
|
|
88
|
-
10.
|
|
89
|
-
11. implementation commit
|
|
90
|
-
12.
|
|
91
|
-
13.
|
|
92
|
-
14.
|
|
93
|
-
15.
|
|
94
|
-
16.
|
|
95
|
-
17.
|
|
84
|
+
5. 开发中可运行 `make validate-dev` 验证当前 open task;它通过不表示可以进入 `REVIEWING`。
|
|
85
|
+
6. 如果 gate 因代码或测试逻辑失败,在任务范围内修复。
|
|
86
|
+
7. 如果 gate 因基础设施、凭证缺失、产品行为不清或高风险架构变化失败,进入 `BLOCKED`。
|
|
87
|
+
8. gate 通过后调用 `pjsdlc_implementation_doc`。
|
|
88
|
+
9. 只有 gate 通过、承诺的 runnable entry/exit 已实现或明确 `BLOCKED`,且 implementation doc 校验通过后,才能把任务标记为 `done`。
|
|
89
|
+
10. 任务完成并写入或更新相关 implementation doc、刷新 `overview.md`、记录 gate 后,先创建 task implementation commit;此时不要移除该 task。
|
|
90
|
+
11. task implementation commit 必须发生在 task 移除前;后续默认不要读取其中的执行期字段,历史查询以模块级 implementation doc、RFC、PRD、tech plan 和代码为主。
|
|
91
|
+
12. implementation commit 完成后,从当前 `plan.yaml` 移除该 task,并创建 task completion ledger commit。
|
|
92
|
+
13. 默认不追溯已完成 task 的执行流水;只有显式 forensic/audit/regression 任务才临时查询 git、PR 或 CI 记录。
|
|
93
|
+
14. 两个 commit 后必须 `git push` 到当前 upstream branch;如果没有 remote/upstream、权限或凭证导致无法 push,停止推进并报告 blocker。
|
|
94
|
+
15. `/devloop` 每轮都必须重新读取当前状态,不得在一次上下文中假设 plan、draft、代码或远端状态未变化。
|
|
95
|
+
16. 只有用户明确要求并行、多 agent 或多 worktree 时,才允许创建 `parallel_execution`;否则不得默认并行。
|
|
96
|
+
17. `runtime_managed` 只在当前 runtime 支持 subagent 时使用;没有该能力时,输出 `user_orchestrated` worker prompt,由用户手动打开对话或 worktree 后粘贴。
|
|
97
|
+
18. worker 不更新主事实源;主 Agent 才能更新 `plan.yaml`、`.docs/INDEX.md`、overview、implementation doc 和最终 gate 证据。
|
|
96
98
|
|
|
97
99
|
## 完成检查
|
|
98
100
|
|
|
@@ -38,7 +38,7 @@ Parallel Execution 是显式 opt-in:只有用户明确提出“并行”“多
|
|
|
38
38
|
5. gate 失败时保持当前阶段不变,并报告 blocker。
|
|
39
39
|
6. 用户输入 `/status` 时,运行 `make status`。
|
|
40
40
|
7. 用户输入 `/next` 时,调用 `active_skill` 映射的 Skill。
|
|
41
|
-
8. 用户输入 `/advance` 时,运行 `make validate-current`,通过后流转到配置的 `next`
|
|
41
|
+
8. 用户输入 `/advance` 时,运行 `make validate-current`,通过后流转到配置的 `next` 阶段;在 `SPRINTING` 下这会执行 phase-exit no-open 检查,不能用 direct `make validate-dev` 的通过结果替代。
|
|
42
42
|
9. 用户输入 `/rfc <file>` 时,流转到 `RFC_RECALIBRATION` 并调用 `pjsdlc_rfc_recalibrate`。
|
|
43
43
|
10. 如果当前 task 处于 `blocked` 或缺少 open task 必需的 plan 字段,不要推进阶段,先要求 `plan.yaml` 完整。
|
|
44
44
|
11. 用户自然语言询问状态时,等价执行 `/status`。
|
|
@@ -61,7 +61,7 @@ Parallel Execution 是显式 opt-in:只有用户明确提出“并行”“多
|
|
|
61
61
|
|
|
62
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` 指向模块级实现事实。任何阶段如果从 draft queue promote 正式 `TASK-*`,必须同次消费并删除源 draft;当前内置 draft queue 是 `plan.draft.yaml.tasks[]`,用于保存尚未采用的开发草案。done/cancelled task 不长期留在当前 `plan.yaml`。完成后的产物事实以对应 `.docs/**` slice 或模块级 implementation doc 为准,动作历史以 git/PR/CI/release 系统作为 cold archive,`next_task_sequence` 负责继续分配后续 task id。
|
|
63
63
|
|
|
64
|
-
`/prd`、`/design`、`/dev`、`/review`、`/test`、`/release` 和 `/rfc` 都是单 task 推进:默认只完成一个 `TASK-*`。`validate-plan` 用于检查当前 open task
|
|
64
|
+
`/prd`、`/design`、`/dev`、`/review`、`/test`、`/release` 和 `/rfc` 都是单 task 推进:默认只完成一个 `TASK-*`。`validate-plan` 用于检查当前 open task 合同是否完整。direct `validate-dev` / `make validate-dev` 是 `SPRINTING` 开发中 gate,允许一个合法当前 open task 存在;`validate-current` / `/advance` 在 `SPRINTING` 下仍是阶段出口 gate,要求没有 open task 残留。其它阶段出口 gate `validate-pm`、`validate-design`、`validate-review`、`validate-test`、`validate-release` 和 `validate-rfc` 也要求没有 open task 残留。
|
|
65
65
|
|
|
66
66
|
`parallel_execution` 是可选顶层合同,缺省表示串行。启用后必须声明 `enabled`、`trigger`、`mode`、`coordinator`、`workers` 和 `integration`;不要在合同内重复保存 `phase` 或 `linked_task_id`,当前阶段来自 lifecycle 的 `current_phase`,当前任务来自 plan 的 `current_task_id`。
|
|
67
67
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: pjsdlc_release_manager
|
|
3
|
-
description: Use during RELEASING to prepare release
|
|
3
|
+
description: Use during RELEASING to prepare the current release status, smoke evidence, deployment checks, and rollback plan.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Release Manager Skill
|
|
@@ -11,13 +11,13 @@ description: Use during RELEASING to prepare release notes, smoke evidence, depl
|
|
|
11
11
|
|
|
12
12
|
## 角色提示词
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
你是发布负责人,目标是判断当前版本是否具备可发布性,并把当前发布状态、发布说明、smoke evidence、部署检查和回滚路径组织成可执行交付物。你不默认部署,除非用户明确授权。
|
|
15
15
|
|
|
16
16
|
准备发布时,先确认测试结论、build artifacts、included changes、known limitations、人工确认项和环境依赖。对风险要说清楚:哪些风险已通过测试降低,哪些风险只能通过 smoke、监控或回滚缓解。
|
|
17
17
|
|
|
18
|
-
|
|
18
|
+
Current release status 面向当前发布决策,必须说明版本、变更价值、影响范围、smoke 证据、已知限制和注意事项;rollback plan 面向执行者,必须具体到触发条件、操作入口、验证方式和负责人。
|
|
19
19
|
|
|
20
|
-
发布准备本身也是 workflow task。开始 release 工作前,先在 `<harnessRoot>/state/plan.yaml` 创建或选择一个足够小的 `TASK-*` open task,并设置 `phase: "RELEASING"
|
|
20
|
+
发布准备本身也是 workflow task。开始 release 工作前,先在 `<harnessRoot>/state/plan.yaml` 创建或选择一个足够小的 `TASK-*` open task,并设置 `phase: "RELEASING"`;当前轮只更新 `.docs/08_release/CURRENT_RELEASE.md` 中的当前发布状态、一次 smoke evidence 补充、一个部署检查或一个 rollback plan 单元。发布动作本身仍需用户明确授权。
|
|
21
21
|
|
|
22
22
|
## 输入
|
|
23
23
|
|
|
@@ -29,28 +29,28 @@ Release note 面向人类读者,必须说明变更价值、影响范围和注
|
|
|
29
29
|
|
|
30
30
|
## 输出
|
|
31
31
|
|
|
32
|
-
- `.docs/08_release
|
|
32
|
+
- `.docs/08_release/CURRENT_RELEASE.md` 当前发布状态
|
|
33
33
|
- 更新后的 `<harnessRoot>/state/plan.yaml`
|
|
34
34
|
- smoke test result
|
|
35
35
|
- deployment checklist
|
|
36
36
|
- rollback plan
|
|
37
|
-
- 发布完成后由 git tag、release commit 或外部发布系统记录动作历史
|
|
37
|
+
- 发布完成后由 git tag、release commit、registry、CI 或外部发布系统记录动作历史
|
|
38
38
|
|
|
39
39
|
## 语义切片
|
|
40
40
|
|
|
41
|
-
- `.docs/08_release
|
|
42
|
-
-
|
|
43
|
-
-
|
|
44
|
-
-
|
|
45
|
-
-
|
|
41
|
+
- `.docs/08_release/CURRENT_RELEASE.md` 是 canonical release fact source,只表达当前发布状态、release notes、build artifacts、smoke test result、deployment checklist、rollback plan 和 known issues。
|
|
42
|
+
- `.docs/08_release/` 不保存长期版本历史;过去 release 通过 git tag、npm registry、CI、release commit 或外部发布系统追溯。
|
|
43
|
+
- 如果当前发布包含多个独立发布单元,应在 `CURRENT_RELEASE.md` 中分区说明依赖关系,不新增版本化 release ledger。
|
|
44
|
+
- 如果只是补充当前版本的 smoke evidence 或 rollback step,应更新 `CURRENT_RELEASE.md`。
|
|
45
|
+
- 发布状态完成后更新 `.docs/INDEX.md`;不再维护 Harness archive。
|
|
46
46
|
|
|
47
47
|
## Plan Protocol
|
|
48
48
|
|
|
49
49
|
发布阶段受 `plan.yaml` 管控:
|
|
50
50
|
|
|
51
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`
|
|
53
|
-
3. 单个 task
|
|
52
|
+
2. open task 必须包含 `phase`、`docs`、`allowed_paths`、`required_gates`、`acceptance_criteria` 和 `result_docs`;`result_docs` 指向 `.docs/08_release/CURRENT_RELEASE.md`。
|
|
53
|
+
3. 单个 task 的目标应足够小:一次当前发布状态更新、一个 smoke evidence 补充、一个 deployment checklist 或一个 rollback plan。
|
|
54
54
|
4. 执行当前 task 时只编辑 `allowed_paths` 中的 release 产物、`.docs/INDEX.md`、overview 和 `plan.yaml`。
|
|
55
55
|
5. 完成后运行 `make validate-plan` 和 task required gates;阶段出口前运行 `make validate-release`。
|
|
56
56
|
6. task 完成后从 `plan.yaml.tasks` 移除;如果还有 pending release task,下一轮 `/release` 或 `/next` 再继续。
|
|
@@ -58,7 +58,7 @@ Release note 面向人类读者,必须说明变更价值、影响范围和注
|
|
|
58
58
|
## 规则
|
|
59
59
|
|
|
60
60
|
1. 除非用户明确要求,不自动部署。
|
|
61
|
-
2.
|
|
61
|
+
2. Current release status 必须说明 included changes 和 known limitations。
|
|
62
62
|
3. Rollback plan 必须可执行。
|
|
63
63
|
4. Smoke test evidence 必须链接或摘要记录。
|
|
64
64
|
5. Human confirmation items 必须明确。
|
|
@@ -66,12 +66,12 @@ Release note 面向人类读者,必须说明变更价值、影响范围和注
|
|
|
66
66
|
|
|
67
67
|
## 完成检查
|
|
68
68
|
|
|
69
|
-
- [ ]
|
|
69
|
+
- [ ] `.docs/08_release/CURRENT_RELEASE.md` 已更新。
|
|
70
70
|
- [ ] 当前发布工作已绑定 `plan.yaml` 中一个最小 `TASK-*` task,并设置 `phase: "RELEASING"`。
|
|
71
71
|
- [ ] 当前 task 已从 `plan.yaml` 移除,或因中断/blocker 保留为可恢复 open task。
|
|
72
72
|
- [ ] Build artifacts 已记录。
|
|
73
73
|
- [ ] Smoke test result 已记录。
|
|
74
|
-
- [ ]
|
|
74
|
+
- [ ] 已判断当前发布状态中的版本或发布批次边界。
|
|
75
75
|
- [ ] Rollback plan 已生成。
|
|
76
76
|
- [ ] 已运行 `make docs-overview` 刷新 `.docs/<stage>/overview.md`。
|
|
77
77
|
- [ ] `make validate-release` 准备通过。
|
|
@@ -17,7 +17,7 @@ Review 时先建立证据链:PRD 说什么、技术方案承诺什么、implem
|
|
|
17
17
|
|
|
18
18
|
不要把个人偏好包装成 blocker。区分 blocking issue、follow-up improvement 和 open question。如果没有发现问题,要明确说明,同时列出剩余测试缺口或残余风险。
|
|
19
19
|
|
|
20
|
-
Review 必须把“当前模块没有可运行入口/出口”视为阻断项,而不是普通测试缺口。凡 PRD、技术方案或 implementation doc 承诺 API、CLI、server route、adapter、worker、provider、外部发送/写入执行器、配置契约或 live/fixture 双模式边界,Review 都要核对真实代码和实现文档是否提供可调用入口、输出/副作用边界和验证方式;缺失时 gate decision 应为 `BLOCKED`,并要求回到 SPRINTING/RFC,而不是允许进入 TESTING 后补 runtime。
|
|
20
|
+
Review 必须把“当前模块没有可运行入口/出口”视为阻断项,而不是普通测试缺口。凡 PRD、技术方案或 implementation doc 承诺 API、CLI、server route、adapter、worker、provider、外部发送/写入执行器、配置契约或 live/fixture 双模式边界,Review 都要核对真实代码和实现文档是否提供可调用入口、输出/副作用边界和验证方式;缺失时 gate decision 应为 `BLOCKED`,并要求回到 SPRINTING/RFC,而不是允许进入 TESTING 后补 runtime。Review 不创建 `.docs/07_test/**` 正式测试产物;如果发现现有测试事实源仍链接已被 RFC supersede 的旧路线证据,应将其列为进入 TESTING 前的 blocker,并要求 RFC 清理或更新索引。
|
|
21
21
|
|
|
22
22
|
Review 产出本身也是 workflow task。开始 review 前,先在 `<harnessRoot>/state/plan.yaml` 创建或选择一个足够小的 `TASK-*` open task,并设置 `phase: "REVIEWING"`;当前轮只产出一个 review batch、一个风险主题 slice 或一次 PR review 结论。不要在一个任务里覆盖多个互不相关的 review 主题。
|
|
23
23
|
|
|
@@ -27,6 +27,7 @@ Review 产出本身也是 workflow task。开始 review 前,先在 `<harnessRo
|
|
|
27
27
|
- `.docs/01_product/`
|
|
28
28
|
- `.docs/03_tech_plan/`
|
|
29
29
|
- `.docs/04_implementation/`
|
|
30
|
+
- `.docs/07_test/`(只读,用于发现 stale test facts)
|
|
30
31
|
- `git diff`
|
|
31
32
|
- gate/test 结果
|
|
32
33
|
- `<harnessRoot>/pjsdlc_managed/templates/REVIEW_TEMPLATE.md`
|
|
@@ -15,10 +15,12 @@ description: Use during RFC_RECALIBRATION to process requirement changes with im
|
|
|
15
15
|
|
|
16
16
|
处理 RFC 时,先确认变化来源、动机、验收标准、紧急程度和影响范围。必须区分产品语义变化、技术实现偏移、任务边界调整和单纯文档澄清。对不确定的影响,先记录假设和待验证项,再决定是否回到 PM、ARCHITECTING 或 SPRINTING。
|
|
17
17
|
|
|
18
|
-
输出应包含 impact analysis
|
|
18
|
+
输出应包含 impact analysis、受影响产物、任务状态调整、测试事实源影响、回归要求和恢复路径。只修改受影响 slice;如果变化跨越多个独立能力,应拆分 RFC 或生成增量任务。
|
|
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 替换模块技术路线、entry/exit、环境依赖或验收边界,必须同步审查 `.docs/07_test/**`。被新方案 supersede 的测试环境、测试进度、测试用例、测试报告和 partial evidence 要从当前测试事实源删除或迁出,并从 `.docs/INDEX.md` 和 generated overview 中移除链接;历史证据只保留在 RFC provenance、git history、CI/release 系统或明确 archive 语义中,不能继续放在当前 `.docs/07_test/**` 冒充现行测试依据。RFC 必须写明 `Test Fact Source Impact`:reviewed test docs、superseded test docs、retained test docs 和原因;如果只是文案澄清且不影响测试事实源,可写 `none`。
|
|
23
|
+
|
|
22
24
|
RFC recalibration 本身也是 workflow task。开始处理变更前,先在 `<harnessRoot>/state/plan.yaml` 创建或选择一个足够小的 `TASK-*` open task,并设置 `phase: "RFC_RECALIBRATION"`;当前轮只处理一个 RFC 文件、一个 impact analysis 单元或一个局部补丁单元。
|
|
23
25
|
|
|
24
26
|
## 输入
|
|
@@ -35,6 +37,7 @@ RFC recalibration 本身也是 workflow task。开始处理变更前,先在 `<
|
|
|
35
37
|
- 局部更新后的 PRD 和技术方案
|
|
36
38
|
- 被标记为 `pending_revision` 的受影响任务,或新增增量任务
|
|
37
39
|
- Regression requirements
|
|
40
|
+
- Test fact source impact
|
|
38
41
|
- 更新后的 `<harnessRoot>/state/plan.yaml`
|
|
39
42
|
- 更新后的 `.docs/INDEX.md`
|
|
40
43
|
|
|
@@ -42,7 +45,7 @@ RFC recalibration 本身也是 workflow task。开始处理变更前,先在 `<
|
|
|
42
45
|
|
|
43
46
|
- `.docs/rfc/` 按一次需求变更切片,一份 RFC 只描述一个可独立评估、实现和回归的变更。
|
|
44
47
|
- 如果用户一次提出多个互不依赖的变更,应拆成多份 RFC。
|
|
45
|
-
- RFC 的 impact analysis 负责判断是否需要重切 PRD、tech plan、implementation doc 或 test report,并覆盖 state、tools、package assets、tests、migration 和 generated overview。
|
|
48
|
+
- RFC 的 impact analysis 负责判断是否需要重切 PRD、tech plan、implementation doc、test strategy、test cases 或 test report,并覆盖 state、tools、package assets、tests、migration 和 generated overview。
|
|
46
49
|
- 对受影响产物做局部补丁,不重写无关稳定 slice。
|
|
47
50
|
- 每次 RFC 影响了文档边界,都要更新 `.docs/INDEX.md` 并记录受影响任务状态。
|
|
48
51
|
|
|
@@ -66,6 +69,7 @@ RFC 阶段受 `plan.yaml` 管控:
|
|
|
66
69
|
5. 不重写无关的稳定文档。
|
|
67
70
|
6. 只有 `make validate-rfc` 通过后,才能恢复原阶段或进入 `SPRINTING`。
|
|
68
71
|
7. RFC 阶段一次只执行一个 `TASK-*` task。
|
|
72
|
+
8. RFC 列为 superseded 的 `.docs/07_test/**` 文件必须在当前测试事实源中不存在,并且不得继续出现在 `.docs/INDEX.md`。
|
|
69
73
|
|
|
70
74
|
## 完成检查
|
|
71
75
|
|
|
@@ -75,6 +79,7 @@ RFC 阶段受 `plan.yaml` 管控:
|
|
|
75
79
|
- [ ] Product impact 和 technical impact 已记录。
|
|
76
80
|
- [ ] 已判断 RFC 是否需要拆分,以及是否影响其它阶段 slice。
|
|
77
81
|
- [ ] 已列出 docs/state/skills/policies/templates/tools/package assets/tests/migrations/generated artifacts 的影响面。
|
|
82
|
+
- [ ] 已记录 `Test Fact Source Impact`,并清理被 supersede 的 `.docs/07_test/**` 当前事实链接。
|
|
78
83
|
- [ ] 受影响任务已标记或新增。
|
|
79
84
|
- [ ] Regression requirements 已明确。
|
|
80
85
|
- [ ] `.docs/INDEX.md` 已链接 RFC 和受影响产物。
|
|
@@ -7,19 +7,19 @@ description: Use during TESTING to produce a test matrix, run regression, and do
|
|
|
7
7
|
|
|
8
8
|
## 目的
|
|
9
9
|
|
|
10
|
-
把 PRD、技术方案、实现事实和 Review findings
|
|
10
|
+
把 PRD、技术方案、实现事实和 Review findings 转成可执行测试设计与执行后的回归证据。
|
|
11
11
|
|
|
12
12
|
## 角色提示词
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
你是测试负责人,目标是把需求、风险和实现变化转成可执行、可追踪、可复用的测试产物。必须严格区分三类文档:`TEST_STRATEGY.md` 描述范围、环境、优先级和执行策略;`TEST_CASES.md` 描述要测什么、前置条件、步骤和预期结果;`TEST_REPORT.md` 只记录 TESTING 阶段实际执行后的结果、证据、覆盖缺口和最终结论。不要把测试用例、测试计划或待填报告命名为 `TEST_REPORT.md`。
|
|
15
15
|
|
|
16
16
|
开始测试规划前,先建立映射关系:PRD acceptance criteria、技术方案关键接口/数据模型、implementation doc 的真实改动、Review findings 和现有测试。对每个测试项说明它覆盖的需求或风险;对暂不覆盖的内容说明原因、残余风险和 follow-up。
|
|
17
17
|
|
|
18
|
-
执行回归时,优先选择能证明阶段出口的 gate
|
|
18
|
+
执行回归时,优先选择能证明阶段出口的 gate。测试无法运行、环境缺失或数据不可得时,不要宣布通过;如果已经进入 TESTING,应在 `TEST_REPORT.md` 中记录 `BLOCKED`、已完成检查和恢复条件。
|
|
19
19
|
|
|
20
|
-
TESTING 只能调用 SPRINTING 已经交付的入口做输入/输出验证。可以补充测试、fixture、mock、assertion helper 和测试文档,但不能在 TESTING 中新增或长期维护 product runtime、server/API/CLI/adapter、direct poller、cloud bootstrap、systemd unit、真实 provider adapter、package runtime script 或部署脚本。如果发现真实入口/出口不存在、live 模式不可调用、配置契约缺失或用户目标与已实现通道不一致,应记录 `BLOCKED`、生成 RFC 或后续 dev task
|
|
20
|
+
TESTING 只能调用 SPRINTING 已经交付的入口做输入/输出验证。可以补充测试、fixture、mock、assertion helper 和测试文档,但不能在 TESTING 中新增或长期维护 product runtime、server/API/CLI/adapter、direct poller、cloud bootstrap、systemd unit、真实 provider adapter、package runtime script 或部署脚本。如果发现真实入口/出口不存在、live 模式不可调用、配置契约缺失或用户目标与已实现通道不一致,应记录 `BLOCKED`、生成 RFC 或后续 dev task 建议,并停止把测试阶段扩大成开发/集成搭建。开发尚未交付可测试 entry/exit 时,不要在 `.docs/07_test/**` 提前生成正式测试用例或正式报告;验收思路应留在 PRD acceptance criteria、tech plan verification strategy 或非 `.docs/07_test/**` 的草稿说明里。
|
|
21
21
|
|
|
22
|
-
|
|
22
|
+
测试设计和回归证据产出本身也是 workflow task。开始测试前,先在 `<harnessRoot>/state/plan.yaml` 创建或选择一个足够小的 `TASK-*` open task,并设置 `phase: "TESTING"`;当前轮只产出一个测试策略 slice、测试用例 slice、回归批次、风险验证片区或一组 scoped test changes。`plan.yaml` 仍是唯一执行计划事实源,`.docs/07_test/**` 只记录当前方案的 test strategy、test cases、executed regression evidence、coverage gaps 和 final decision,不表达“下一步如何开发”,也不保留已被 RFC supersede 的旧测试结果。
|
|
23
23
|
|
|
24
24
|
如果用户明确要求并行、多 agent 或多 worktree,测试阶段可以启用 `parallel_execution`,让 worker 分别执行互不依赖的回归片区、smoke、兼容性或风险验证。worker 只提交证据和必要的 scoped test changes;最终 `.docs/07_test/**`、coverage gaps、PASS/BLOCKED 决策和阶段 gate 由主 Agent 汇总。没有用户显式要求时,测试 workflow 保持串行。
|
|
25
25
|
|
|
@@ -31,11 +31,15 @@ TESTING 只能调用 SPRINTING 已经交付的入口做输入/输出验证。可
|
|
|
31
31
|
- `.docs/04_implementation/`
|
|
32
32
|
- `.docs/06_review/REVIEW_REPORT.md`
|
|
33
33
|
- 现有测试
|
|
34
|
+
- `<harnessRoot>/pjsdlc_managed/templates/TEST_STRATEGY_TEMPLATE.md`
|
|
35
|
+
- `<harnessRoot>/pjsdlc_managed/templates/TEST_CASES_TEMPLATE.md`
|
|
34
36
|
- `<harnessRoot>/pjsdlc_managed/templates/TEST_REPORT_TEMPLATE.md`
|
|
35
37
|
|
|
36
38
|
## 输出
|
|
37
39
|
|
|
38
|
-
- `.docs/07_test/
|
|
40
|
+
- `.docs/07_test/TEST_STRATEGY.md`(可选,仅在 TESTING 中生成)
|
|
41
|
+
- `.docs/07_test/TEST_CASES.md`(可选,仅在 TESTING 中绑定真实 entry/exit 后生成)
|
|
42
|
+
- `.docs/07_test/TEST_REPORT.md`(执行后必备)
|
|
39
43
|
- 必要时在 `tests/` 下补充测试
|
|
40
44
|
- 更新后的 `<harnessRoot>/state/plan.yaml`
|
|
41
45
|
- 回归证据记录
|
|
@@ -44,10 +48,10 @@ TESTING 只能调用 SPRINTING 已经交付的入口做输入/输出验证。可
|
|
|
44
48
|
|
|
45
49
|
## 语义切片
|
|
46
50
|
|
|
47
|
-
- `.docs/07_test/`
|
|
51
|
+
- `.docs/07_test/` 默认按测试策略、测试用例、执行报告、回归批次或领域测试范围切片。
|
|
48
52
|
- Test matrix 的语义原子是 PRD acceptance criteria、Review findings 和关键风险路径。
|
|
49
|
-
- 如果多个领域的测试范围互不依赖,应拆成多个
|
|
50
|
-
- 如果新增测试只是覆盖同一验收标准,应更新原 test slice
|
|
53
|
+
- 如果多个领域的测试范围互不依赖,应拆成多个 strategy/cases/evidence slices,并在主 `TEST_REPORT.md` 汇总实际执行结论。
|
|
54
|
+
- 如果新增测试只是覆盖同一验收标准,应更新原 test slice,不要创建重复测试报告;测试报告只能在有实际执行证据后更新。
|
|
51
55
|
- 每次新增、拆分或合并 test slice 后,都要更新 `.docs/INDEX.md`。
|
|
52
56
|
|
|
53
57
|
## Plan Protocol
|
|
@@ -56,22 +60,24 @@ TESTING 只能调用 SPRINTING 已经交付的入口做输入/输出验证。可
|
|
|
56
60
|
|
|
57
61
|
1. 没有 open task 时,先创建一个最小 `TASK-*` task,设置 `phase: "TESTING"` 和 `current_task_id`。
|
|
58
62
|
2. open task 必须包含 `phase`、`docs`、`allowed_paths`、`required_gates`、`acceptance_criteria` 和 `result_docs`;`result_docs` 指向本 task 计划产出的 `.docs/07_test/**` 文件,必要时也列出 scoped test files。
|
|
59
|
-
3. 单个 task
|
|
63
|
+
3. 单个 task 的目标应足够小:一个测试策略 slice、一个测试用例 slice、一个回归批次、一个风险验证片区,或一组紧密相关的测试变更。
|
|
60
64
|
4. 执行当前 task 时只编辑 `allowed_paths` 中的测试、测试文档、`.docs/INDEX.md`、overview 和 `plan.yaml`。
|
|
61
65
|
5. 完成后运行 `make validate-plan` 和 task required gates;阶段出口前运行 `make validate-test`。
|
|
62
66
|
6. task 完成后从 `plan.yaml.tasks` 移除;如果还有 pending testing task,下一轮 `/test` 或 `/next` 再继续。
|
|
63
67
|
|
|
64
68
|
## 规则
|
|
65
69
|
|
|
66
|
-
1. 测试用例必须追溯到 PRD acceptance criteria 或 Review findings。
|
|
70
|
+
1. 测试用例必须追溯到 PRD acceptance criteria 或 Review findings,并绑定 SPRINTING/REVIEWING 已确认的 runnable entry/exit。
|
|
67
71
|
2. 根据风险补充边界、负向、回归和集成测试。
|
|
68
72
|
3. 如果有意延后覆盖,必须记录风险和 follow-up。
|
|
69
73
|
4. 不得新增 product runtime、server/API/CLI/adapter、poller、cloud bootstrap、systemd unit、真实 provider adapter、package runtime script 或部署脚本;这些属于 SPRINTING/RFC。
|
|
70
74
|
5. 测试发现入口/出口缺失时,Final decision 必须为 `BLOCKED`,并指出回到 SPRINTING/RFC 的具体条件。
|
|
71
|
-
6.
|
|
72
|
-
7.
|
|
73
|
-
8.
|
|
74
|
-
9.
|
|
75
|
+
6. 新测试策略使用 `.docs/07_test/TEST_STRATEGY.md`,新测试用例使用 `.docs/07_test/TEST_CASES.md`,执行报告使用 `.docs/07_test/TEST_REPORT.md`;不要新建或继续依赖 `.docs/07_test/TEST_PLAN.md`。
|
|
76
|
+
7. `TEST_REPORT.md` 不得包含 `pending`、`TBD`、`待填`、`TODO` 或占位结论;未执行或不可执行时 Final decision 必须为 `BLOCKED` 并给出恢复条件。
|
|
77
|
+
8. RFC 改变技术路线、entry/exit 或验收边界后,必须确认 `.docs/07_test/**` 中旧路线测试证据已删除或不再从 `.docs/INDEX.md` 暴露。
|
|
78
|
+
9. 并行测试必须使用 `parallel_execution.trigger: "user_requested"`;`runtime_managed` 只在当前 runtime 支持 subagent 时使用,否则输出 `user_orchestrated` worker prompt。
|
|
79
|
+
10. 宣布阶段完成前运行 `make test-all`。
|
|
80
|
+
11. 测试阶段一次只执行一个 `TASK-*` task。
|
|
75
81
|
|
|
76
82
|
## 完成检查
|
|
77
83
|
|
|
@@ -81,6 +87,8 @@ TESTING 只能调用 SPRINTING 已经交付的入口做输入/输出验证。可
|
|
|
81
87
|
- [ ] Regression checklist 已完成。
|
|
82
88
|
- [ ] 测试只调用既有 runnable entry/exit;未在 TESTING 中新增 product runtime、bootstrap、provider adapter、deploy 或 package runtime script。
|
|
83
89
|
- [ ] 已判断 test report / test matrix 的语义切片边界。
|
|
90
|
+
- [ ] 未把测试计划、测试用例或待填内容写成 `TEST_REPORT.md`。
|
|
91
|
+
- [ ] 已确认 `.docs/07_test/**` 只包含当前方案仍有效的测试事实。
|
|
84
92
|
- [ ] Coverage gaps 已明确。
|
|
85
93
|
- [ ] 如果启用了并行测试,worker evidence 已由主 Agent 汇总到测试产物。
|
|
86
94
|
- [ ] 已运行 `make docs-overview` 刷新 `.docs/<stage>/overview.md`。
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
# Release
|
|
1
|
+
# Current Release Status(当前发布状态)
|
|
2
|
+
|
|
3
|
+
本文件是 `.docs/08_release/CURRENT_RELEASE.md` 的模板。每次发布更新当前状态;历史发布证据通过 git tag、npm registry、CI、release commit 或外部发布系统追溯。
|
|
2
4
|
|
|
3
5
|
## 1. Release Summary(发布摘要)
|
|
4
6
|
|
|
@@ -6,6 +8,7 @@
|
|
|
6
8
|
- Milestone:
|
|
7
9
|
- Date:
|
|
8
10
|
- Owner:
|
|
11
|
+
- Status:
|
|
9
12
|
|
|
10
13
|
## 2. Included Changes(包含变更)
|
|
11
14
|
|
|
@@ -32,3 +35,7 @@
|
|
|
32
35
|
- 步骤(Steps):
|
|
33
36
|
- 数据注意事项(Data considerations):
|
|
34
37
|
- 负责人(Owner):
|
|
38
|
+
|
|
39
|
+
## 7. Known Issues(已知限制)
|
|
40
|
+
|
|
41
|
+
-
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# Test Cases(测试用例)
|
|
2
|
+
|
|
3
|
+
## 1. Scope(范围)
|
|
4
|
+
|
|
5
|
+
- PRD:
|
|
6
|
+
- Technical design:
|
|
7
|
+
- Runnable entry/exit under test:
|
|
8
|
+
|
|
9
|
+
## 2. Cases(用例)
|
|
10
|
+
|
|
11
|
+
| ID | Requirement | Preconditions | Steps | Expected Result |
|
|
12
|
+
|---|---|---|---|---|
|
|
13
|
+
| TC-001 | | | | |
|
|
14
|
+
|
|
15
|
+
## 3. Traceability(追溯)
|
|
16
|
+
|
|
17
|
+
- PRD acceptance criteria:
|
|
18
|
+
- Review findings:
|
|
19
|
+
- Risk paths:
|
|
20
|
+
|
|
21
|
+
## 4. Notes(备注)
|
|
22
|
+
|
|
23
|
+
- Fixture/live boundary:
|
|
24
|
+
- Data setup:
|
|
@@ -1,17 +1,20 @@
|
|
|
1
1
|
# Test Report(测试报告)
|
|
2
2
|
|
|
3
|
+
> `TEST_REPORT.md` 只记录 TESTING 阶段已经执行过的验证。测试计划、测试策略或待填内容请使用 `TEST_STRATEGY.md` / `TEST_CASES.md`,不要放在本文件。
|
|
4
|
+
|
|
3
5
|
## 1. Scope(范围)
|
|
4
6
|
|
|
5
7
|
- PRD:
|
|
6
8
|
- Technical design:
|
|
7
9
|
- Implementation docs:
|
|
8
10
|
- Review report:
|
|
11
|
+
- Executed at:
|
|
9
12
|
|
|
10
13
|
## 2. Test Matrix(测试矩阵)
|
|
11
14
|
|
|
12
15
|
| 需求(Requirement) | 场景(Scenario) | 测试类型(Test Type) | 测试用例(Test Case) | 结果(Result) |
|
|
13
16
|
|---|---|---|---|---|
|
|
14
|
-
| | | unit/integration/e2e/regression | |
|
|
17
|
+
| | | unit/integration/e2e/regression | | PASS/BLOCKED |
|
|
15
18
|
|
|
16
19
|
## 3. Regression Evidence(回归证据)
|
|
17
20
|
|
|
@@ -35,3 +38,4 @@
|
|
|
35
38
|
|
|
36
39
|
- Decision: `PASS` / `BLOCKED`
|
|
37
40
|
- Evidence:
|
|
41
|
+
- Recovery condition when `BLOCKED`:
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# Test Strategy(测试策略)
|
|
2
|
+
|
|
3
|
+
## 1. Scope(范围)
|
|
4
|
+
|
|
5
|
+
- In scope:
|
|
6
|
+
- Out of scope:
|
|
7
|
+
- Target release/module:
|
|
8
|
+
|
|
9
|
+
## 2. Environment(环境)
|
|
10
|
+
|
|
11
|
+
- Runnable entry points:
|
|
12
|
+
- Expected exits / side effects:
|
|
13
|
+
- Config contract:
|
|
14
|
+
- Fixture/live boundary:
|
|
15
|
+
|
|
16
|
+
## 3. Priority(优先级)
|
|
17
|
+
|
|
18
|
+
| Area | Priority | Reason |
|
|
19
|
+
|---|---|---|
|
|
20
|
+
| | P0/P1/P2 | |
|
|
21
|
+
|
|
22
|
+
## 4. Execution Strategy(执行策略)
|
|
23
|
+
|
|
24
|
+
- Automated checks:
|
|
25
|
+
- Manual checks:
|
|
26
|
+
- Regression gates:
|
|
27
|
+
- Blocker handling:
|
package/dist/lib/validators.js
CHANGED
|
@@ -69,7 +69,11 @@ const TESTING_DISALLOWED_CHANGED_PATHS = [...TESTING_DISALLOWED_ALLOWED_PATHS, "
|
|
|
69
69
|
const TESTING_RUNTIME_FILE_TERMS = ["bootstrap", "cloud", "daemon", "poller", "provider", "runtime", "service", "systemd"];
|
|
70
70
|
const TESTING_ALLOWED_TEST_FILE_TERMS = ["assertion", "fixture", "mock", "smoke"];
|
|
71
71
|
const TEST_REPORT_PATH = ".docs/07_test/TEST_REPORT.md";
|
|
72
|
-
const
|
|
72
|
+
const CURRENT_RELEASE_REPORT_PATH = ".docs/08_release/CURRENT_RELEASE.md";
|
|
73
|
+
const TEST_REPORT_PLACEHOLDER_TERMS = ["pending", "tbd", "todo", "待填", "待补", "placeholder"];
|
|
74
|
+
const TEST_FACT_SOURCE_PHASES = new Set(["TESTING", "RFC_RECALIBRATION"]);
|
|
75
|
+
const TEST_FACT_SOURCE_PATTERNS = [".docs/07_test/**", ".docs/07_test/"];
|
|
76
|
+
const TEST_FACT_SOURCE_REF = /\.docs\/07_test\/[^\s`,)]+/g;
|
|
73
77
|
const RUNNABLE_ENTRY_EXIT_TERMS = [
|
|
74
78
|
"runnable entry/exit",
|
|
75
79
|
"entry/exit",
|
|
@@ -125,6 +129,9 @@ async function validateCurrent(projectRoot) {
|
|
|
125
129
|
const root = await harnessRoot(projectRoot);
|
|
126
130
|
const lifecycle = await readYamlObject(path.join(projectRoot, root, "state", "lifecycle.yaml"));
|
|
127
131
|
const current = String(lifecycle.current_phase ?? "");
|
|
132
|
+
if (current === "SPRINTING") {
|
|
133
|
+
return validateDevInternal(projectRoot, { phaseExit: true });
|
|
134
|
+
}
|
|
128
135
|
const gateByPhase = {
|
|
129
136
|
REQUIREMENT_GATHERING: "validate-pm",
|
|
130
137
|
ARCHITECTING: "validate-design",
|
|
@@ -265,6 +272,7 @@ function validateDraftTaskShape(task, index, errors) {
|
|
|
265
272
|
if (!hasImplementationDoc && !hasResultDocs) {
|
|
266
273
|
errors.push(`${String(task.id ?? prefix)} must define implementation_doc or result_docs`);
|
|
267
274
|
}
|
|
275
|
+
errors.push(...testFactSourceErrorsForTask(task));
|
|
268
276
|
if (OPEN_TASK_STATUSES.has(String(task.status))) {
|
|
269
277
|
if ("gate_result" in task)
|
|
270
278
|
errors.push(`${String(task.id ?? prefix)} open task must not define gate_result`);
|
|
@@ -314,11 +322,49 @@ async function validateCrossCuttingArchitecture(projectRoot, productFiles, techP
|
|
|
314
322
|
return errors;
|
|
315
323
|
}
|
|
316
324
|
async function validateDev(projectRoot) {
|
|
325
|
+
return validateDevInternal(projectRoot, { phaseExit: false });
|
|
326
|
+
}
|
|
327
|
+
async function validateDevInternal(projectRoot, options) {
|
|
317
328
|
const root = await harnessRoot(projectRoot);
|
|
318
|
-
const
|
|
329
|
+
const lifecycle = await readYamlObject(path.join(projectRoot, root, "state", "lifecycle.yaml"));
|
|
330
|
+
const plan = await validatePlanState(projectRoot, !options.phaseExit);
|
|
331
|
+
const phaseErrors = String(lifecycle.current_phase ?? "") === "SPRINTING" ? [] : ["validate-dev requires lifecycle current_phase SPRINTING"];
|
|
332
|
+
const openTaskErrors = options.phaseExit ? [] : validateDevOpenTaskState(plan.plan);
|
|
333
|
+
const pathErrors = options.phaseExit ? [] : await validateChangedPaths(projectRoot, plan.plan, true);
|
|
319
334
|
const draftErrors = await validateDevDraftConsumed(projectRoot, root);
|
|
320
335
|
const implementationDocErrors = await validateImplementationDocRunnableEntryExit(projectRoot);
|
|
321
|
-
return {
|
|
336
|
+
return {
|
|
337
|
+
info: [`validate-dev checked ${plan.taskCount} task(s)${options.phaseExit ? " for phase exit" : ""}`],
|
|
338
|
+
errors: [...phaseErrors, ...plan.errors, ...openTaskErrors, ...pathErrors, ...draftErrors, ...implementationDocErrors]
|
|
339
|
+
};
|
|
340
|
+
}
|
|
341
|
+
function validateDevOpenTaskState(plan) {
|
|
342
|
+
const errors = [];
|
|
343
|
+
const tasks = Array.isArray(plan.tasks) ? plan.tasks.filter(isRecord) : [];
|
|
344
|
+
const open = tasks.filter((task) => OPEN_TASK_STATUSES.has(String(task.status)));
|
|
345
|
+
if (open.length === 0)
|
|
346
|
+
return errors;
|
|
347
|
+
const currentTaskId = String(plan.current_task_id ?? "");
|
|
348
|
+
if (!currentTaskId) {
|
|
349
|
+
errors.push("validate-dev requires current_task_id when SPRINTING has an open task");
|
|
350
|
+
return errors;
|
|
351
|
+
}
|
|
352
|
+
const currentTask = open.find((task) => String(task.id ?? "") === currentTaskId);
|
|
353
|
+
if (!currentTask) {
|
|
354
|
+
errors.push(`current_task_id does not match an open SPRINTING task: ${currentTaskId}`);
|
|
355
|
+
return errors;
|
|
356
|
+
}
|
|
357
|
+
const otherOpen = open.filter((task) => String(task.id ?? "") !== currentTaskId);
|
|
358
|
+
if (otherOpen.length > 0) {
|
|
359
|
+
errors.push(`validate-dev supports only the current open task during SPRINTING: ${open.map((task) => task.id).join(", ")}`);
|
|
360
|
+
}
|
|
361
|
+
if (String(currentTask.phase ?? "") !== "SPRINTING") {
|
|
362
|
+
errors.push(`${currentTaskId} must have phase SPRINTING for validate-dev`);
|
|
363
|
+
}
|
|
364
|
+
if (typeof currentTask.implementation_doc !== "string" || !currentTask.implementation_doc.trim()) {
|
|
365
|
+
errors.push(`${currentTaskId} must define implementation_doc for validate-dev`);
|
|
366
|
+
}
|
|
367
|
+
return errors;
|
|
322
368
|
}
|
|
323
369
|
async function validateDevDraftConsumed(projectRoot, root) {
|
|
324
370
|
const errors = [];
|
|
@@ -359,7 +405,10 @@ async function validateTest(projectRoot) {
|
|
|
359
405
|
const report = await readTestReport(projectRoot);
|
|
360
406
|
const text = report ? report.text.toLowerCase() : "";
|
|
361
407
|
if (!report)
|
|
362
|
-
errors.push(`Missing test report: expected
|
|
408
|
+
errors.push(`Missing test report: expected executed evidence at ${TEST_REPORT_PATH}`);
|
|
409
|
+
if (containsAny(text, TEST_REPORT_PLACEHOLDER_TERMS)) {
|
|
410
|
+
errors.push("Test report must contain executed evidence, not pending/TBD/TODO/placeholder content");
|
|
411
|
+
}
|
|
363
412
|
if (!containsAny(text, ["matrix", "矩阵"]))
|
|
364
413
|
errors.push("Test report must include a test matrix");
|
|
365
414
|
if (!containsAny(text, ["regression", "回归"]))
|
|
@@ -378,18 +427,18 @@ async function validateTest(projectRoot) {
|
|
|
378
427
|
}
|
|
379
428
|
async function validateRelease(projectRoot) {
|
|
380
429
|
const plan = await validatePlanState(projectRoot, false);
|
|
381
|
-
const
|
|
382
|
-
const text =
|
|
430
|
+
const report = await readReleaseReport(projectRoot);
|
|
431
|
+
const text = report?.text ?? "";
|
|
383
432
|
const errors = [...plan.errors];
|
|
384
|
-
if (
|
|
385
|
-
errors.push(
|
|
433
|
+
if (!report)
|
|
434
|
+
errors.push(`Missing current release report: expected ${CURRENT_RELEASE_REPORT_PATH} or legacy .docs/08_release/*.md`);
|
|
386
435
|
if (!containsAny(text, ["release", "发布"]))
|
|
387
|
-
errors.push("
|
|
436
|
+
errors.push("Current release report must include release notes");
|
|
388
437
|
if (!containsAny(text, ["smoke", "冒烟"]))
|
|
389
|
-
errors.push("
|
|
438
|
+
errors.push("Current release report must include smoke test evidence");
|
|
390
439
|
if (!containsAny(text, ["rollback", "回滚"]))
|
|
391
|
-
errors.push("
|
|
392
|
-
return { info: [`validate-release checked ${
|
|
440
|
+
errors.push("Current release report must include rollback plan");
|
|
441
|
+
return { info: [`validate-release checked ${report?.source ?? "missing current release report"}`], errors };
|
|
393
442
|
}
|
|
394
443
|
async function validateRfc(projectRoot) {
|
|
395
444
|
const plan = await validatePlanState(projectRoot, false);
|
|
@@ -406,6 +455,21 @@ async function validateRfc(projectRoot) {
|
|
|
406
455
|
errors.push("RFC must include technical impact candidates");
|
|
407
456
|
if (!containsAny(text, ["regression", "回归"]))
|
|
408
457
|
errors.push("RFC must include regression requirements");
|
|
458
|
+
if (!containsAny(text, ["test fact source impact", "测试事实源影响"])) {
|
|
459
|
+
errors.push("RFC must include Test Fact Source Impact");
|
|
460
|
+
}
|
|
461
|
+
const indexPath = path.join(projectRoot, ".docs/INDEX.md");
|
|
462
|
+
const indexText = (await pathExists(indexPath)) ? await readText(indexPath) : "";
|
|
463
|
+
if (!indexText)
|
|
464
|
+
errors.push("Missing .docs/INDEX.md for RFC test fact source validation");
|
|
465
|
+
for (const superseded of await supersededTestDocs(docs)) {
|
|
466
|
+
if (await pathExists(path.join(projectRoot, superseded))) {
|
|
467
|
+
errors.push(`Superseded test doc still exists in current facts: ${superseded}`);
|
|
468
|
+
}
|
|
469
|
+
if (indexText.includes(superseded)) {
|
|
470
|
+
errors.push(`Superseded test doc still linked from .docs/INDEX.md: ${superseded}`);
|
|
471
|
+
}
|
|
472
|
+
}
|
|
409
473
|
const statuses = [...text.matchAll(/status:\s*([a-z_]+)/g)].map((match) => match[1].toUpperCase());
|
|
410
474
|
if (statuses.length === 0)
|
|
411
475
|
errors.push("RFC must include a Status line");
|
|
@@ -464,6 +528,7 @@ async function validatePlanState(projectRoot, allowOpen) {
|
|
|
464
528
|
if (match) {
|
|
465
529
|
maxTaskSequence = Math.max(maxTaskSequence, Number(match[1]));
|
|
466
530
|
}
|
|
531
|
+
errors.push(...testFactSourceErrorsForTask(task));
|
|
467
532
|
if (["pending", "in_progress", "blocked", "pending_revision"].includes(String(task.status))) {
|
|
468
533
|
if ("gate_result" in task) {
|
|
469
534
|
errors.push(`Open task ${task.id} must not define gate_result`);
|
|
@@ -637,19 +702,37 @@ async function readTestReport(projectRoot) {
|
|
|
637
702
|
if (await pathExists(canonical)) {
|
|
638
703
|
return { text: await readText(canonical), source: TEST_REPORT_PATH };
|
|
639
704
|
}
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
705
|
+
return undefined;
|
|
706
|
+
}
|
|
707
|
+
async function readReleaseReport(projectRoot) {
|
|
708
|
+
const canonical = path.join(projectRoot, CURRENT_RELEASE_REPORT_PATH);
|
|
709
|
+
if (await pathExists(canonical)) {
|
|
710
|
+
return { text: await readText(canonical), source: CURRENT_RELEASE_REPORT_PATH };
|
|
711
|
+
}
|
|
712
|
+
const legacyDocs = await markdownFiles(path.join(projectRoot, ".docs/08_release"));
|
|
713
|
+
if (legacyDocs.length > 0) {
|
|
714
|
+
return { text: await combinedText(legacyDocs), source: `legacy .docs/08_release/*.md (${legacyDocs.length} file(s))` };
|
|
643
715
|
}
|
|
644
716
|
return undefined;
|
|
645
717
|
}
|
|
646
718
|
async function validateImplementationDocRunnableEntryExit(projectRoot) {
|
|
647
719
|
const docs = await markdownFiles(path.join(projectRoot, ".docs/04_implementation"));
|
|
648
720
|
const errors = [];
|
|
721
|
+
const indexPath = path.join(projectRoot, ".docs/INDEX.md");
|
|
722
|
+
const indexText = (await pathExists(indexPath)) ? await readText(indexPath) : "";
|
|
723
|
+
if (docs.length > 0 && !indexText) {
|
|
724
|
+
errors.push("Missing .docs/INDEX.md for implementation doc validation");
|
|
725
|
+
}
|
|
649
726
|
for (const doc of docs) {
|
|
727
|
+
const relative = repoRelative(projectRoot, doc);
|
|
728
|
+
const dotted = relative.startsWith(".") ? relative : `.${relative}`;
|
|
729
|
+
const withoutDocsPrefix = dotted.replace(/^\.docs\//, "");
|
|
730
|
+
if (indexText && !indexText.includes(dotted) && !indexText.includes(withoutDocsPrefix)) {
|
|
731
|
+
errors.push(`.docs/INDEX.md does not link implementation doc: ${dotted}`);
|
|
732
|
+
}
|
|
650
733
|
const text = await readText(doc);
|
|
651
734
|
if (!containsAny(text, RUNNABLE_ENTRY_EXIT_TERMS)) {
|
|
652
|
-
errors.push(`Implementation doc must include Runnable Entry/Exit facts or explicit Not applicable: ${
|
|
735
|
+
errors.push(`Implementation doc must include Runnable Entry/Exit facts or explicit Not applicable: ${dotted}`);
|
|
653
736
|
}
|
|
654
737
|
}
|
|
655
738
|
return errors;
|
|
@@ -688,6 +771,37 @@ function testingBoundaryErrorsForChangedFiles(files) {
|
|
|
688
771
|
`TESTING changes must use existing product entrypoints only; move runtime, bootstrap, provider, deploy, or package script changes to SPRINTING/RFC: ${blocked.join(", ")}`
|
|
689
772
|
];
|
|
690
773
|
}
|
|
774
|
+
function testFactSourceErrorsForTask(task) {
|
|
775
|
+
const phase = String(task.phase ?? "");
|
|
776
|
+
if (TEST_FACT_SOURCE_PHASES.has(phase))
|
|
777
|
+
return [];
|
|
778
|
+
const candidates = [...asStringList(task.allowed_paths), ...asStringList(task.result_docs)];
|
|
779
|
+
const blocked = candidates.filter((candidate) => {
|
|
780
|
+
const normalized = candidate.replace(/\\/g, "/");
|
|
781
|
+
return normalized.startsWith(".docs/07_test/") || matchesAny(normalized, TEST_FACT_SOURCE_PATTERNS);
|
|
782
|
+
});
|
|
783
|
+
if (blocked.length === 0)
|
|
784
|
+
return [];
|
|
785
|
+
return [
|
|
786
|
+
`Only TESTING or RFC_RECALIBRATION tasks may target current test fact sources under .docs/07_test/**: ${blocked.join(", ")}`
|
|
787
|
+
];
|
|
788
|
+
}
|
|
789
|
+
async function supersededTestDocs(docs) {
|
|
790
|
+
const refs = new Set();
|
|
791
|
+
for (const doc of docs) {
|
|
792
|
+
const text = await readText(doc);
|
|
793
|
+
for (const line of text.split("\n")) {
|
|
794
|
+
const lowered = line.toLowerCase();
|
|
795
|
+
if (!lowered.includes("superseded") && !lowered.includes("被替代") && !lowered.includes("失效")) {
|
|
796
|
+
continue;
|
|
797
|
+
}
|
|
798
|
+
for (const match of line.matchAll(TEST_FACT_SOURCE_REF)) {
|
|
799
|
+
refs.add(normalizeDocRef(match[0]).replace(/[.,;:]$/, ""));
|
|
800
|
+
}
|
|
801
|
+
}
|
|
802
|
+
}
|
|
803
|
+
return [...refs].filter((ref) => ref.startsWith(".docs/07_test/"));
|
|
804
|
+
}
|
|
691
805
|
function isTestingBoundaryAllowedPath(file) {
|
|
692
806
|
const lowered = file.replace(/\\/g, "/").toLowerCase();
|
|
693
807
|
if (["package.json", "package-lock.json", "npm-shrinkwrap.json", "pnpm-lock.yaml", "yarn.lock", "bun.lock", "bun.lockb"].includes(lowered)) {
|