@cyning/harness 2.12.1 → 2.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +19 -1
- package/README.md +22 -4
- package/docs/ONBOARDING.md +10 -2
- package/docs/README.md +14 -4
- package/docs/ROADMAP_TO_AGENT_GOVERNANCE.md +7 -7
- package/docs/USER_GUIDE_v1.0_zh.md +8 -1
- package/docs/spec/SPEC-lifecycle-dry-run-close_v1.md +63 -0
- package/harness/discipline-coverage.yaml +1 -1
- package/harness/lifecycle.yaml +2 -1
- package/lib/cli.js +14 -3
- package/lib/lifecycle.js +35 -2
- package/lib/task-close.js +196 -97
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,24 @@
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
## [2.13.0] - 2026-07-26
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- **`lifecycle dry-run` 接线 `close_*`**:`--task` + `close` + `from=done` 时求值 `close_invoke` / `close_self_check` / `close_acceptance` / `close_slug` / `close_status` / `close_review`(与 `task close` 同语义 · 旁路 · 不 mv)。
|
|
12
|
+
- `evaluateCloseChecks`(`lib/task-close.js`):close 与 dry-run 共用检查;CLI 透传 `--allow-invoke-gap` / `--allow-unchecked`。
|
|
13
|
+
|
|
14
|
+
### Changed
|
|
15
|
+
|
|
16
|
+
- README / USER_GUIDE / ONBOARDING / docs 索引:对齐 **2.12–2.13**(invoke hats · dry-run close)。
|
|
17
|
+
- `discipline-coverage.yaml` `as_of` → **2.13.0**。
|
|
18
|
+
|
|
19
|
+
### Notes
|
|
20
|
+
|
|
21
|
+
- SPEC:`docs/spec/SPEC-lifecycle-dry-run-close_v1.md`
|
|
22
|
+
- minor · 仍无 `--apply` / G7;`to_00` 仍可 unevaluated
|
|
23
|
+
- dry-run **不替代** `task close` 硬闸
|
|
24
|
+
|
|
7
25
|
## [2.12.1] - 2026-07-26
|
|
8
26
|
|
|
9
27
|
### Fixed
|
|
@@ -21,7 +39,7 @@
|
|
|
21
39
|
|
|
22
40
|
- patch · 对规范命名无影响;依赖 slug 误计的畸形文件名 close 可能新 BLOCK
|
|
23
41
|
- 迁移指引:[`docs/USER_GUIDE_v1.0_zh.md`](docs/USER_GUIDE_v1.0_zh.md) §6.0(不另造 RUNBOOK)
|
|
24
|
-
-
|
|
42
|
+
- **已发布**:`@cyning/harness@2.12.1`(npm `latest` · 2026-07-26)· tag `v2.12.1` 已推送
|
|
25
43
|
|
|
26
44
|
## [2.12.0] - 2026-07-26
|
|
27
45
|
|
package/README.md
CHANGED
|
@@ -97,6 +97,22 @@ Inform-YAML:`npx @cyning/harness graph yaml compile|check --input docs/_tech_g
|
|
|
97
97
|
HGM 过程轨:`npx @cyning/harness graph ingest|snapshot|axioms check --target /path/to/your-repo`(v2.0+)
|
|
98
98
|
Invoke 索引:`npx @cyning/harness sync index --target /path/to/your-repo`(不覆盖 S2 域)
|
|
99
99
|
|
|
100
|
+
### 近期纪律闸(v2.5–2.13)
|
|
101
|
+
|
|
102
|
+
| 能力 | 版本 | 命令 / 要点 |
|
|
103
|
+
|------|------|-------------|
|
|
104
|
+
| reviews 留档 | 2.5+ / 全量 2.9 | `verify` · `task close` 查 R\<n\> 审查文 |
|
|
105
|
+
| `task lint` | 2.3+ · W6=2.12 | 结构闸;缺 `required_invoke_hats` / profile 提醒 |
|
|
106
|
+
| `lifecycle show` / `dry-run` | 2.7 / 2.10+ | 登记真值;旁路资格报告(**不**替代 verify/close) |
|
|
107
|
+
| dry-run `to_30` 守卫 | 2.11 | HG · reviews · D5 · lint |
|
|
108
|
+
| 多帽 invoke 留档 | **2.12+** | close 硬闸缺省 `10,30,40`;`--allow-invoke-gap`;见 USER_GUIDE §6.0 |
|
|
109
|
+
| dry-run `close_*` | **2.13** | 与 `task close` 同语义求值;仍无 `--apply` |
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
npx @cyning/harness lifecycle dry-run --transition close --from done --task docs/tasks/active/task_xxx.md
|
|
113
|
+
npx @cyning/harness task close --file docs/tasks/active/task_xxx.md # 硬闸归档
|
|
114
|
+
```
|
|
115
|
+
|
|
100
116
|
Node 仓可选:`npx @cyning/harness init --with-scripts` → `pnpm harness:verify`。
|
|
101
117
|
详见 [`docs/USER_GUIDE_v1.0_zh.md`](docs/USER_GUIDE_v1.0_zh.md) · [`docs/ONBOARDING.md`](docs/ONBOARDING.md) §2.2。
|
|
102
118
|
|
|
@@ -171,9 +187,11 @@ cyning-harness/
|
|
|
171
187
|
|
|
172
188
|
| 项 | 值 |
|
|
173
189
|
|----|-----|
|
|
174
|
-
| 当前 |
|
|
175
|
-
|
|
|
176
|
-
| 许可 | **MIT** · [`LICENSE`](LICENSE) ·
|
|
190
|
+
| 当前 npm | **`@cyning/harness@2.13.0`** — dry-run `close_*` · invoke hats 硬闸(2.12)· lifecycle / verify 扩面(2.9–2.11) |
|
|
191
|
+
| 里程碑 | v2.0 — HGM G1 · Inform-YAML · ICVO audit · invoke_index |
|
|
192
|
+
| 许可 | **MIT** · [`LICENSE`](LICENSE) · 变更 [`CHANGELOG.md`](CHANGELOG.md) |
|
|
193
|
+
|
|
194
|
+
> README 历史「v2.0.0」表述为 HGM 里程碑标签;**以 `package.json` / npm `latest` / CHANGELOG 为准**。
|
|
177
195
|
|
|
178
196
|
---
|
|
179
197
|
|
|
@@ -183,7 +201,7 @@ cyning-harness/
|
|
|
183
201
|
| --- | --- |
|
|
184
202
|
| **总指引** | [`methodology/README.md`](docs/methodology/README.md) · [关系图](docs/methodology/DOCUMENT_MAP_v1_zh.md) · [路线 ROADMAP](docs/methodology/ROADMAP_v1_zh.md) |
|
|
185
203
|
| **本体** | [DESIGN_ONTOLOGY v1.2](docs/methodology/product/DESIGN_ONTOLOGY_v1_zh.md) |
|
|
186
|
-
| **使用** | [**USER_GUIDE v1.0**](docs/USER_GUIDE_v1.0_zh.md) ·
|
|
204
|
+
| **使用** | [**USER_GUIDE v1.0**](docs/USER_GUIDE_v1.0_zh.md)(§6.0 invoke 留档 · lifecycle dry-run)· [金样验收](examples/demo_checkout/ACCEPTANCE.md) |
|
|
187
205
|
| **远期** | [HGM 草案](docs/methodology/graph/HARNESS_GRAPH_MODEL_design_v0_zh.md) |
|
|
188
206
|
| **接入** | [ONBOARDING](docs/ONBOARDING.md) · [ARCHITECTURE](docs/ARCHITECTURE.md) |
|
|
189
207
|
|
package/docs/ONBOARDING.md
CHANGED
|
@@ -66,13 +66,20 @@ npx @cyning/harness verify --spec docs/spec/SPEC-xxx_v1.md \
|
|
|
66
66
|
# 只读生命周期登记(方向二 · harness/lifecycle.yaml · 非引擎)
|
|
67
67
|
npx @cyning/harness lifecycle show
|
|
68
68
|
npx @cyning/harness lifecycle show --json
|
|
69
|
-
# 转移资格 dry-run(v2.10+ · 旁路报告 · 不替代 verify)
|
|
70
|
-
# v2.11+ to_30 已接线:HG-* · reviews · audit_D5 · task_lint
|
|
69
|
+
# 转移资格 dry-run(v2.10+ · 旁路报告 · 不替代 verify / task close)
|
|
70
|
+
# v2.11+ to_30 已接线:HG-* · reviews · audit_D5 · task_lint
|
|
71
|
+
# v2.13+ close_* 已接线(与 task close 同语义 · 仍无 --apply)
|
|
71
72
|
npx @cyning/harness lifecycle dry-run --transition to_30 --from draft \
|
|
72
73
|
--task docs/tasks/active/task_xxx.md
|
|
74
|
+
npx @cyning/harness lifecycle dry-run --transition close --from done \
|
|
75
|
+
--task docs/tasks/active/task_xxx.md
|
|
73
76
|
# 无 --task:仅结构 + 守卫清单 unevaluated
|
|
74
77
|
npx @cyning/harness lifecycle dry-run --transition to_30 --from draft
|
|
75
78
|
|
|
79
|
+
# 受闸归档(v2.12+ 多帽 invoke 集合 · 缺省 10,30,40 · 见 USER_GUIDE §6.0)
|
|
80
|
+
npx @cyning/harness task close --file docs/tasks/active/task_xxx.md
|
|
81
|
+
# 豁免示例:--allow-invoke-gap · --allow-unchecked · --allow-no-review
|
|
82
|
+
|
|
76
83
|
# 机械化率资产只读(v2.11+ · SoT=discipline-coverage.yaml)
|
|
77
84
|
npx @cyning/harness discipline show
|
|
78
85
|
npx @cyning/harness discipline show --json
|
|
@@ -94,6 +101,7 @@ npx @cyning/harness sync index --target /path/to/your-repo
|
|
|
94
101
|
| **D3** | 30 前置人闸 | 复用 `gate-check.sh`,HG-AUDIT-R1 非 approved 时 verify 非 0 |
|
|
95
102
|
| **D5** | 改码任务测试声明 | 仅 `--task`:`test_strategy=required` 但无测试/CI 引用时 verify 非 0 |
|
|
96
103
|
| **reviews** | R<n> 审查文存在 | `--task` 与全量(v2.9+)· 缺文 BLOCKED · `--allow-no-review` |
|
|
104
|
+
| **invoke hats** | 多帽 invoke 集合 | `task close` 硬闸(v2.12+)· `verify --task` 仅 WARN · `lifecycle dry-run close` 旁路(v2.13) |
|
|
97
105
|
| **active** | 任务发现 | 双路径 `docs/tasks/active` ∪ `docs/harness/tasks/active`(v2.9+) |
|
|
98
106
|
| **S5** | Git 工作区干净 | dirty 时 warn(不直接 fail verify,但 apply 须 `--force`) |
|
|
99
107
|
| **lint** | task 结构(仅 `--task`) | v2.7+ E 级 → `WARN: task lint`(不改 exit / `may_start_30`) |
|
package/docs/README.md
CHANGED
|
@@ -3,8 +3,9 @@
|
|
|
3
3
|
| 项 | 内容 |
|
|
4
4
|
| --- | --- |
|
|
5
5
|
| **入口** | 本页 → **[方法论总指引](./methodology/README.md)** |
|
|
6
|
-
| **接入** | [**USER_GUIDE v1.0**](./USER_GUIDE_v1.0_zh.md) · [`ONBOARDING.md`](./ONBOARDING.md) · [`../wizard/README.md`](../wizard/README.md) |
|
|
6
|
+
| **接入** | [**USER_GUIDE v1.0**](./USER_GUIDE_v1.0_zh.md)(§6.0 invoke 留档 · lifecycle)· [`ONBOARDING.md`](./ONBOARDING.md) · [`../wizard/README.md`](../wizard/README.md) |
|
|
7
7
|
| **架构** | [`ARCHITECTURE.md`](./ARCHITECTURE.md) |
|
|
8
|
+
| **变更** | [`../CHANGELOG.md`](../CHANGELOG.md) · 当前包 **2.13.0** |
|
|
8
9
|
|
|
9
10
|
---
|
|
10
11
|
|
|
@@ -13,9 +14,16 @@
|
|
|
13
14
|
| 读者 | 先读 |
|
|
14
15
|
| --- | --- |
|
|
15
16
|
| **新接入** | **USER_GUIDE v1.0** → ONBOARDING → [`methodology/execution/P0_V0.2_GAP.md`](./methodology/execution/P0_V0.2_GAP.md) |
|
|
16
|
-
| **实现 / 扩展** | [`methodology/product/DESIGN_ONTOLOGY_v1_zh.md`](./methodology/product/DESIGN_ONTOLOGY_v1_zh.md) |
|
|
17
|
+
| **实现 / 扩展** | [`methodology/product/DESIGN_ONTOLOGY_v1_zh.md`](./methodology/product/DESIGN_ONTOLOGY_v1_zh.md) · [`spec/`](./spec/)(闸 SPEC) |
|
|
17
18
|
| **战略 / 开源** | [`methodology/pointers/`](./methodology/pointers/README.md) → 工作区 STRATEGY_* |
|
|
18
|
-
| **HGM 远期** | [`methodology/graph/`](./methodology/graph/README.md) |
|
|
19
|
+
| **HGM 远期** | [`methodology/graph/`](./methodology/graph/README.md) · 愿景 [`ROADMAP_TO_AGENT_GOVERNANCE.md`](./ROADMAP_TO_AGENT_GOVERNANCE.md) |
|
|
20
|
+
|
|
21
|
+
### 近期产品说明(2.12–2.13)
|
|
22
|
+
|
|
23
|
+
| 主题 | 文档 |
|
|
24
|
+
|------|------|
|
|
25
|
+
| 多帽 invoke 留档硬闸 | USER_GUIDE §6.0 · [`SPEC-invoke-hats-retention-gate_v1.md`](./spec/SPEC-invoke-hats-retention-gate_v1.md) |
|
|
26
|
+
| dry-run 接线 close | [`SPEC-lifecycle-dry-run-close_v1.md`](./spec/SPEC-lifecycle-dry-run-close_v1.md) · ONBOARDING §2.2 |
|
|
19
27
|
|
|
20
28
|
---
|
|
21
29
|
|
|
@@ -26,6 +34,8 @@ docs/
|
|
|
26
34
|
├── README.md # 本页
|
|
27
35
|
├── ARCHITECTURE.md # 五轨 · 与业务仓关系
|
|
28
36
|
├── ONBOARDING.md # 接入指南
|
|
37
|
+
├── USER_GUIDE_v1.0_zh.md # 陌生人手册
|
|
38
|
+
├── spec/ # 产品闸 SPEC(signed)
|
|
29
39
|
├── DESIGN_ONTOLOGY_v1_zh.md # POINTER → methodology/product/
|
|
30
40
|
├── P0_V0.2_GAP.md # POINTER → methodology/execution/
|
|
31
41
|
├── HARNESS_GRAPH_MODEL_*.md # POINTER → methodology/graph/
|
|
@@ -35,4 +45,4 @@ docs/
|
|
|
35
45
|
└── prompts/ # Agent 写作 / 讨论 Prompt
|
|
36
46
|
```
|
|
37
47
|
|
|
38
|
-
**修订**:2026-
|
|
48
|
+
**修订**:2026-07-26 · 对齐 2.12–2.13 · 索引 SPEC
|
|
@@ -31,23 +31,23 @@ cyning-harness 已经通过 HumanGate、AuditReview、InvokeSnapshot 等实体
|
|
|
31
31
|
|
|
32
32
|
## 2. 三阶段演进路径
|
|
33
33
|
|
|
34
|
-
### 阶段 0
|
|
34
|
+
### 阶段 0:当前状态(纪律包 · npm 2.x)
|
|
35
35
|
|
|
36
36
|
| 项 | 内容 |
|
|
37
37
|
| --- | --- |
|
|
38
38
|
| **形态** | 项目纪律包,需手动绑定到仓库,要求开发者遵循 SDD 帽链 |
|
|
39
|
+
| **当前包** | `@cyning/harness@2.13+`(以 npm / CHANGELOG 为准;本文档为愿景,非 semver 承诺) |
|
|
39
40
|
|
|
40
|
-
|
|
41
|
+
**能力**(摘要):
|
|
41
42
|
|
|
42
|
-
- ProcessTrack + StarterHat(10/
|
|
43
|
-
-
|
|
44
|
-
-
|
|
45
|
-
- 基础 ICVO 映射
|
|
43
|
+
- ProcessTrack + StarterHat(10/20/30/40)+ HumanGate / AuditReview / InvokeSnapshot
|
|
44
|
+
- Sync(S1–S6)· ICVO verify / audit · lifecycle dry-run(to_30 · close_*)
|
|
45
|
+
- 多帽 invoke 留档硬闸(close · v2.12+)
|
|
46
46
|
|
|
47
47
|
**限制**:
|
|
48
48
|
|
|
49
49
|
- 必须有人参与闸门审批(HumanGate)
|
|
50
|
-
- 未与 Agent
|
|
50
|
+
- 未与 Agent 运行时集成;dry-run **无** `--apply` / G7 runner
|
|
51
51
|
|
|
52
52
|
---
|
|
53
53
|
|
|
@@ -190,7 +190,7 @@ Schema:[`schema/verify_result.v1.schema.json`](../schema/verify_result.v1.sche
|
|
|
190
190
|
| `npx @cyning/harness verify` | 全量:双路径 + reviews(v2.9+);`--task`:30 前聚合 + invoke hats WARN(v2.12+);`--spec`:SPEC→00(v2.8+ · 互斥) |
|
|
191
191
|
| `npx @cyning/harness task close` | 受闸归档;v2.12+ 按 `required_invoke_hats` / `invoke_retention_profile` 校验多帽 invoke(缺省 `10,30,40`;`--allow-invoke-gap`) |
|
|
192
192
|
| `npx @cyning/harness lifecycle show` | 只读展示 `harness/lifecycle.yaml`(登记 · v2.7+) |
|
|
193
|
-
| `npx @cyning/harness lifecycle dry-run` |
|
|
193
|
+
| `npx @cyning/harness lifecycle dry-run` | 转移资格判定(v2.10+ · `to_30` v2.11 · **`close_*` v2.13** · 旁路 · 非 G7) |
|
|
194
194
|
| `npx @cyning/harness discipline show` | 只读展示 `harness/discipline-coverage.yaml`(v2.11+ · 非 audit UI) |
|
|
195
195
|
| `npx @cyning/harness gate-check` | 仅人工闸(`--graph` / `--json`) |
|
|
196
196
|
| `npx @cyning/harness audit` | ICVO 机械审计(D3/D5/S5) |
|
|
@@ -221,6 +221,13 @@ Audit **不替代** 维护者判断;22 内容质量仍须人读 review。详
|
|
|
221
221
|
|
|
222
222
|
`verify --task` 对缺口仅 **WARN**,不挡 `may_start_30`;硬闸在 close。
|
|
223
223
|
|
|
224
|
+
归档前可用旁路资格报告(**不** mv、**不**替代 `task close`):
|
|
225
|
+
|
|
226
|
+
```bash
|
|
227
|
+
npx @cyning/harness lifecycle dry-run --transition close --from done --task docs/tasks/active/task_xxx.md
|
|
228
|
+
# 可选:--allow-invoke-gap · --allow-unchecked · --allow-no-review
|
|
229
|
+
```
|
|
230
|
+
|
|
224
231
|
### 6.1 SDD-Compliance bench(维护者 · 可选)
|
|
225
232
|
|
|
226
233
|
> **业务仓日常不必跑**;用于验证 `gate-check` / `sync` 公理行为,或对照 README「试点证据」中的 bench 数字。
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# SPEC:lifecycle dry-run 接线 close_*(v1)
|
|
2
|
+
|
|
3
|
+
> **状态**:`signed`
|
|
4
|
+
> **track**:`feature`
|
|
5
|
+
> **前置**:`@cyning/harness@2.12.1`(invoke hats 硬闸)· dry-run 骨架 `2.10` · to_30 扩面 `2.11`
|
|
6
|
+
> **下游**:30 实现 → 测试 → docs → **2.13.0**
|
|
7
|
+
> **关联**:[`SPEC-lifecycle-guard-expand_v1.md`](./SPEC-lifecycle-guard-expand_v1.md) residual · [`SPEC-invoke-hats-retention-gate_v1.md`](./SPEC-invoke-hats-retention-gate_v1.md)
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Harness 元信息
|
|
12
|
+
|
|
13
|
+
| 字段 | 值 |
|
|
14
|
+
|------|-----|
|
|
15
|
+
| **spec_slug** | `lifecycle-dry-run-close` |
|
|
16
|
+
| **test_strategy** | `required` |
|
|
17
|
+
| **test_strategy_note** | close 转移各 close_* 守卫 pass/fail/warn;豁免旗;无 --apply |
|
|
18
|
+
| **skip_spec_audit** | `true` |
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## 1. 背景与目标
|
|
23
|
+
|
|
24
|
+
`lifecycle dry-run` 对 `close` 转移的 `close_*` 仍全部 `unevaluated`,与 `task close` 硬闸脱节。
|
|
25
|
+
**一句话**:`--task` + `close` + `from=done` 时,把 yaml 登记的 close 守卫接到与 `task close` **同语义**的薄 adapter(旁路报告 · 不 mv · 无 `--apply`)。
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## 2. 范围
|
|
30
|
+
|
|
31
|
+
| guard id | 语义 | allow |
|
|
32
|
+
|----------|------|-------|
|
|
33
|
+
| `close_invoke` | invoke hats 集合覆盖(v2.12+) | `--allow-invoke-gap` |
|
|
34
|
+
| `close_self_check` | 自检结论已回填 | — |
|
|
35
|
+
| `close_acceptance` | 验收勾选 | `--allow-unchecked` |
|
|
36
|
+
| `close_slug` | task_slug 与文件名一致 | — |
|
|
37
|
+
| `close_status` | 状态 done/completed | — |
|
|
38
|
+
| `close_review` | R\<n\> 审查文存在 | `--allow-no-review` |
|
|
39
|
+
|
|
40
|
+
- 复用 `task-close` 检查逻辑(抽 `evaluateCloseChecks`,禁止复制分叉)
|
|
41
|
+
- CLI `lifecycle dry-run` 透传上述 allow 旗
|
|
42
|
+
- CHANGELOG **2.13.0** · README / USER_GUIDE / ONBOARDING / docs 索引同步
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
## 3. 非范围
|
|
47
|
+
|
|
48
|
+
- `--apply` / 写盘 / G7 runner
|
|
49
|
+
- 改 `task close` / `verify` 硬闸语义
|
|
50
|
+
- N2-C · `to_00` 接线 · `audit --discipline` UI
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
## 4. 验收
|
|
55
|
+
|
|
56
|
+
- [x] `close` + 齐全 fixture → 全部 close_* 非 `unevaluated` · `unevaluated_count=0` · exit 0
|
|
57
|
+
- [x] 仅缺 invoke hats → `close_invoke=fail` · blocked
|
|
58
|
+
- [x] `--allow-invoke-gap` → `close_invoke=warn` · 不 block
|
|
59
|
+
- [x] 状态非 done → `close_status=fail`
|
|
60
|
+
- [x] 无 `--task`:仍 unevaluated
|
|
61
|
+
- [x] `npm test` 全绿 · README 版本表与 CLI 说明已更新
|
|
62
|
+
|
|
63
|
+
> **实现**:`@cyning/harness@2.13.0` · `evaluateCloseChecks` + lifecycle adapters。
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
# 禁止:把本文件当「每周全量重盘」流程;不做 audit --discipline UI(本波)
|
|
7
7
|
|
|
8
8
|
version: "1"
|
|
9
|
-
as_of_package_version: "2.
|
|
9
|
+
as_of_package_version: "2.13.0"
|
|
10
10
|
scope: "Starter · harness/prompts (10/22/30/40 + FRAGMENT)+ TEMPLATE_invoke"
|
|
11
11
|
source_audit: "rethink/2026-07-mechanization-rate · 02 转录 + 03 聚合 · 状态已按 G1–G4/N1–N4 刷新(非第二轮通读盘点)"
|
|
12
12
|
|
package/harness/lifecycle.yaml
CHANGED
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
# 登记真值:状态 / 转移 / 守卫。yaml 本身不是引擎。
|
|
4
4
|
# 由 `lifecycle dry-run` 引擎消费(v2.10+ · 资格判定旁路报告 · 不写盘)。
|
|
5
5
|
# v2.11+ to_30 已接线:HG-AUDIT-R1 · HG-TASK-DRAFT · reviews_retention · audit_D5 · task_lint
|
|
6
|
-
#
|
|
6
|
+
# v2.13+ close 已接线:close_invoke · close_self_check · close_acceptance · close_slug · close_status · close_review
|
|
7
|
+
# (to_00 / spec_reviews_retention 仍可 unevaluated)
|
|
7
8
|
# 只读展示:npx @cyning/harness lifecycle show [--json]
|
|
8
9
|
# Schema:schema/lifecycle.v1.schema.json
|
|
9
10
|
|
package/lib/cli.js
CHANGED
|
@@ -531,9 +531,10 @@ async function cmdLifecycle(args) {
|
|
|
531
531
|
npx @cyning/harness lifecycle show [--json]
|
|
532
532
|
npx @cyning/harness lifecycle dry-run --transition ID --from STATE [--task PATH] [--json]
|
|
533
533
|
[--allow-no-review] [--allow-lint-fail] [--allow-no-spec-review]
|
|
534
|
+
[--allow-invoke-gap] [--allow-unchecked]
|
|
534
535
|
|
|
535
536
|
show · 只读展示 harness/lifecycle.yaml(登记真值 · v2.7+)
|
|
536
|
-
dry-run · 转移资格判定(引擎消费 yaml · v2.10+ ·
|
|
537
|
+
dry-run · 转移资格判定(引擎消费 yaml · v2.10+ · to_30 v2.11+ · close_* v2.13+ · 旁路 · 非 G7)
|
|
537
538
|
`);
|
|
538
539
|
return;
|
|
539
540
|
}
|
|
@@ -567,12 +568,16 @@ dry-run · 转移资格判定(引擎消费 yaml · v2.10+ · 旁路报告 ·
|
|
|
567
568
|
const allowNoReview = remaining.includes('--allow-no-review');
|
|
568
569
|
const allowLintFail = remaining.includes('--allow-lint-fail');
|
|
569
570
|
const allowNoSpecReview = remaining.includes('--allow-no-spec-review');
|
|
571
|
+
const allowInvokeGap = remaining.includes('--allow-invoke-gap');
|
|
572
|
+
const allowUnchecked = remaining.includes('--allow-unchecked');
|
|
570
573
|
remaining = remaining.filter(
|
|
571
574
|
(a) =>
|
|
572
575
|
a !== '--json' &&
|
|
573
576
|
a !== '--allow-no-review' &&
|
|
574
577
|
a !== '--allow-lint-fail' &&
|
|
575
|
-
a !== '--allow-no-spec-review'
|
|
578
|
+
a !== '--allow-no-spec-review' &&
|
|
579
|
+
a !== '--allow-invoke-gap' &&
|
|
580
|
+
a !== '--allow-unchecked',
|
|
576
581
|
);
|
|
577
582
|
if (remaining.length > 0) {
|
|
578
583
|
const err = new Error(`lifecycle dry-run 未知参数: ${remaining.join(' ')}`);
|
|
@@ -591,7 +596,13 @@ dry-run · 转移资格判定(引擎消费 yaml · v2.10+ · 旁路报告 ·
|
|
|
591
596
|
transitionId,
|
|
592
597
|
fromState,
|
|
593
598
|
taskPath,
|
|
594
|
-
flags: {
|
|
599
|
+
flags: {
|
|
600
|
+
allowNoReview,
|
|
601
|
+
allowLintFail,
|
|
602
|
+
allowNoSpecReview,
|
|
603
|
+
allowInvokeGap,
|
|
604
|
+
allowUnchecked,
|
|
605
|
+
},
|
|
595
606
|
cwd: process.cwd(),
|
|
596
607
|
});
|
|
597
608
|
|
package/lib/lifecycle.js
CHANGED
|
@@ -5,6 +5,7 @@ import { runTestCheck } from './audit.js';
|
|
|
5
5
|
import { resolveHarnessRoot } from './paths.js';
|
|
6
6
|
import { findReview, parseHumanGates } from './task-meta.js';
|
|
7
7
|
import { lintTaskFile } from './task-lint.js';
|
|
8
|
+
import { evaluateCloseChecks } from './task-close.js';
|
|
8
9
|
|
|
9
10
|
const SEVERITIES = new Set(['block', 'warn']);
|
|
10
11
|
|
|
@@ -123,13 +124,19 @@ export function formatLifecycleShow(data) {
|
|
|
123
124
|
return lines.join('\n').trimEnd();
|
|
124
125
|
}
|
|
125
126
|
|
|
126
|
-
/** 已接线的守卫(未登记者 → unevaluated
|
|
127
|
+
/** 已接线的守卫(未登记者 → unevaluated) */
|
|
127
128
|
const GUARD_ADAPTERS = {
|
|
128
129
|
'HG-AUDIT-R1': evaluateHgGate('HG-AUDIT-R1'),
|
|
129
130
|
'HG-TASK-DRAFT': evaluateHgGate('HG-TASK-DRAFT'),
|
|
130
131
|
reviews_retention: evaluateReviewsRetention,
|
|
131
132
|
audit_D5: evaluateAuditD5,
|
|
132
133
|
task_lint: evaluateTaskLint,
|
|
134
|
+
close_invoke: evaluateCloseGuard('close_invoke'),
|
|
135
|
+
close_self_check: evaluateCloseGuard('close_self_check'),
|
|
136
|
+
close_acceptance: evaluateCloseGuard('close_acceptance'),
|
|
137
|
+
close_slug: evaluateCloseGuard('close_slug'),
|
|
138
|
+
close_status: evaluateCloseGuard('close_status'),
|
|
139
|
+
close_review: evaluateCloseGuard('close_review'),
|
|
133
140
|
};
|
|
134
141
|
|
|
135
142
|
function findRepoRootFromTask(absTask) {
|
|
@@ -146,6 +153,8 @@ function flagsAllow(flags, allowFlag) {
|
|
|
146
153
|
if (allowFlag === '--allow-no-review') return Boolean(flags.allowNoReview);
|
|
147
154
|
if (allowFlag === '--allow-lint-fail') return Boolean(flags.allowLintFail);
|
|
148
155
|
if (allowFlag === '--allow-no-spec-review') return Boolean(flags.allowNoSpecReview);
|
|
156
|
+
if (allowFlag === '--allow-invoke-gap') return Boolean(flags.allowInvokeGap);
|
|
157
|
+
if (allowFlag === '--allow-unchecked') return Boolean(flags.allowUnchecked);
|
|
149
158
|
return false;
|
|
150
159
|
}
|
|
151
160
|
|
|
@@ -207,6 +216,19 @@ function evaluateTaskLint({ taskPath, target }) {
|
|
|
207
216
|
}
|
|
208
217
|
}
|
|
209
218
|
|
|
219
|
+
/** close_* · 复用 evaluateCloseChecks(与 task close 同语义) */
|
|
220
|
+
function evaluateCloseGuard(checkId) {
|
|
221
|
+
return ({ taskPath, closeEval, target, cwd }) => {
|
|
222
|
+
const ev =
|
|
223
|
+
closeEval ?? evaluateCloseChecks(taskPath, { cwd: target ?? cwd });
|
|
224
|
+
const c = ev.checks[checkId];
|
|
225
|
+
if (!c) {
|
|
226
|
+
return { status: 'fail', detail: `未知 close 检查: ${checkId}` };
|
|
227
|
+
}
|
|
228
|
+
return { status: c.status, detail: c.detail };
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
|
|
210
232
|
/**
|
|
211
233
|
* 转移 dry-run(引擎最小骨架 · v2.10+)。
|
|
212
234
|
* 默认不写盘;无 --apply。
|
|
@@ -216,7 +238,13 @@ function evaluateTaskLint({ taskPath, target }) {
|
|
|
216
238
|
* fromState: string,
|
|
217
239
|
* taskPath?: string,
|
|
218
240
|
* harnessRoot?: string,
|
|
219
|
-
* flags?: {
|
|
241
|
+
* flags?: {
|
|
242
|
+
* allowNoReview?: boolean,
|
|
243
|
+
* allowLintFail?: boolean,
|
|
244
|
+
* allowNoSpecReview?: boolean,
|
|
245
|
+
* allowInvokeGap?: boolean,
|
|
246
|
+
* allowUnchecked?: boolean,
|
|
247
|
+
* },
|
|
220
248
|
* cwd?: string,
|
|
221
249
|
* }} options
|
|
222
250
|
*/
|
|
@@ -282,6 +310,10 @@ export function dryRunTransition(options = {}) {
|
|
|
282
310
|
|
|
283
311
|
const canEval = Boolean(absTask);
|
|
284
312
|
const target = absTask ? findRepoRootFromTask(absTask) ?? cwd : cwd;
|
|
313
|
+
const closeEval =
|
|
314
|
+
canEval && transition.id === 'close'
|
|
315
|
+
? evaluateCloseChecks(absTask, { cwd: target })
|
|
316
|
+
: null;
|
|
285
317
|
const guards = [];
|
|
286
318
|
|
|
287
319
|
for (const g of transition.guards) {
|
|
@@ -303,6 +335,7 @@ export function dryRunTransition(options = {}) {
|
|
|
303
335
|
flags,
|
|
304
336
|
guard: g,
|
|
305
337
|
cwd,
|
|
338
|
+
closeEval,
|
|
306
339
|
});
|
|
307
340
|
|
|
308
341
|
let status = evaluated.status;
|
package/lib/task-close.js
CHANGED
|
@@ -14,95 +14,108 @@ import {
|
|
|
14
14
|
} from './task-meta.js';
|
|
15
15
|
|
|
16
16
|
/**
|
|
17
|
-
* task close
|
|
18
|
-
*
|
|
19
|
-
*
|
|
17
|
+
* close 机械校验(结构化 · 供 task close 与 lifecycle dry-run 共用)。
|
|
18
|
+
* 不应用 --allow-*(豁免由调用方处理);不 mv。
|
|
19
|
+
*
|
|
20
|
+
* @returns {{
|
|
21
|
+
* file: string,
|
|
22
|
+
* slug: string|null,
|
|
23
|
+
* fileSlug: string,
|
|
24
|
+
* repoRoot: string|null,
|
|
25
|
+
* inActive: boolean,
|
|
26
|
+
* invoke_hats: object|null,
|
|
27
|
+
* checks: Record<string, { status: 'pass'|'fail', detail: string }>,
|
|
28
|
+
* }}
|
|
20
29
|
*/
|
|
21
|
-
export function
|
|
22
|
-
const {
|
|
23
|
-
yes = false,
|
|
24
|
-
target: targetArg,
|
|
25
|
-
allowUnchecked = false,
|
|
26
|
-
allowNoReview = false,
|
|
27
|
-
allowInvokeGap = false,
|
|
28
|
-
cwd = process.cwd(),
|
|
29
|
-
} = options;
|
|
30
|
-
|
|
30
|
+
export function evaluateCloseChecks(filePath, options = {}) {
|
|
31
|
+
const { cwd = process.cwd() } = options;
|
|
31
32
|
const abs = path.resolve(cwd, filePath);
|
|
32
|
-
|
|
33
|
-
const
|
|
33
|
+
|
|
34
|
+
const emptyFail = (detail) => ({
|
|
35
|
+
file: abs,
|
|
36
|
+
slug: null,
|
|
37
|
+
fileSlug: extractTaskSlug(abs),
|
|
38
|
+
repoRoot: null,
|
|
39
|
+
inActive: false,
|
|
40
|
+
invoke_hats: null,
|
|
41
|
+
checks: {
|
|
42
|
+
close_invoke: { status: 'fail', detail },
|
|
43
|
+
close_self_check: { status: 'fail', detail },
|
|
44
|
+
close_acceptance: { status: 'fail', detail },
|
|
45
|
+
close_slug: { status: 'fail', detail },
|
|
46
|
+
close_status: { status: 'fail', detail },
|
|
47
|
+
close_review: { status: 'fail', detail },
|
|
48
|
+
},
|
|
49
|
+
});
|
|
34
50
|
|
|
35
51
|
if (!fs.existsSync(abs)) {
|
|
36
|
-
return {
|
|
37
|
-
ok: false,
|
|
38
|
-
blockers: [`task 文件不存在: ${filePath}`],
|
|
39
|
-
warnings,
|
|
40
|
-
file: abs,
|
|
41
|
-
slug: extractTaskSlug(abs),
|
|
42
|
-
dest: null,
|
|
43
|
-
dryRun: !yes,
|
|
44
|
-
moved: false,
|
|
45
|
-
};
|
|
52
|
+
return emptyFail(`task 文件不存在: ${filePath}`);
|
|
46
53
|
}
|
|
47
54
|
|
|
48
55
|
const content = fs.readFileSync(abs, 'utf8');
|
|
49
56
|
const meta = parseHarnessMeta(content);
|
|
50
57
|
const fileSlug = extractTaskSlug(abs);
|
|
51
58
|
const slug = meta.task_slug ?? null;
|
|
59
|
+
const activeDir = path.dirname(abs);
|
|
60
|
+
const inActive = path.basename(activeDir) === 'active';
|
|
61
|
+
const repoRoot = findRepoRoot(activeDir);
|
|
62
|
+
|
|
63
|
+
const checks = {};
|
|
52
64
|
|
|
53
|
-
//
|
|
65
|
+
// close_slug
|
|
54
66
|
if (!slug) {
|
|
55
|
-
|
|
67
|
+
checks.close_slug = { status: 'fail', detail: 'Harness 元信息表缺 task_slug' };
|
|
56
68
|
} else if (normalizeSlug(slug) !== normalizeSlug(fileSlug)) {
|
|
57
|
-
|
|
69
|
+
checks.close_slug = {
|
|
70
|
+
status: 'fail',
|
|
71
|
+
detail: `slug 不一致: 文件名 ${fileSlug} ≠ 元信息 task_slug ${slug}`,
|
|
72
|
+
};
|
|
73
|
+
} else {
|
|
74
|
+
checks.close_slug = { status: 'pass', detail: `slug=${slug}` };
|
|
58
75
|
}
|
|
59
76
|
|
|
60
|
-
//
|
|
61
|
-
const activeDir = path.dirname(abs);
|
|
62
|
-
const inActive = path.basename(activeDir) === 'active';
|
|
63
|
-
const repoRoot = findRepoRoot(activeDir);
|
|
64
|
-
|
|
65
|
-
// 检查 1 · invoke 留档(v2.12+:按 required_invoke_hats / profile 集合覆盖)
|
|
77
|
+
// close_invoke
|
|
66
78
|
let invokeHatsEval = null;
|
|
67
79
|
if (!repoRoot) {
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
80
|
+
checks.close_invoke = {
|
|
81
|
+
status: 'fail',
|
|
82
|
+
detail: `无法定位 repo root(${activeDir} 无 docs 祖先)`,
|
|
83
|
+
};
|
|
84
|
+
} else if (!slug) {
|
|
85
|
+
checks.close_invoke = { status: 'fail', detail: '无 task_slug · 无法解析 invoke 目录' };
|
|
86
|
+
} else {
|
|
87
|
+
const invokeDir = path.join(repoRoot, 'docs/harness/invokes/by-task', slug);
|
|
75
88
|
invokeHatsEval = evaluateInvokeHatsRetention(meta, invokeDir);
|
|
76
89
|
const relDir = path.relative(repoRoot, invokeDir).replace(/\\/g, '/');
|
|
77
90
|
if (invokeHatsEval.missingDir || invokeHatsEval.files.length === 0) {
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
}
|
|
82
|
-
blockers.push(msg);
|
|
83
|
-
}
|
|
91
|
+
checks.close_invoke = {
|
|
92
|
+
status: 'fail',
|
|
93
|
+
detail: `missing invoke snapshots: ${relDir} 不存在或无 .md(要求 hats: ${invokeHatsEval.required.join(',')})`,
|
|
94
|
+
};
|
|
84
95
|
} else if (invokeHatsEval.missing.length > 0) {
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
96
|
+
checks.close_invoke = {
|
|
97
|
+
status: 'fail',
|
|
98
|
+
detail:
|
|
99
|
+
`missing invoke hats: ${invokeHatsEval.missing.join(',')} ` +
|
|
100
|
+
`(要求 ${invokeHatsEval.required.join(',')} · 已有 ${invokeHatsEval.covered.join(',') || '∅'} · ` +
|
|
101
|
+
`source=${invokeHatsEval.resolution.source}` +
|
|
102
|
+
`${invokeHatsEval.resolution.unknown_profile ? ` · unknown_profile=${invokeHatsEval.resolution.unknown_profile}` : ''})`,
|
|
103
|
+
};
|
|
104
|
+
} else {
|
|
105
|
+
const note = invokeHatsEval.resolution.unknown_profile
|
|
106
|
+
? ` · unknown_profile=${invokeHatsEval.resolution.unknown_profile} 已回退 default`
|
|
107
|
+
: '';
|
|
108
|
+
checks.close_invoke = {
|
|
109
|
+
status: 'pass',
|
|
110
|
+
detail: `hats ok · ${invokeHatsEval.required.join(',')}${note}`,
|
|
111
|
+
};
|
|
99
112
|
}
|
|
100
113
|
}
|
|
101
114
|
|
|
102
|
-
//
|
|
115
|
+
// close_self_check
|
|
103
116
|
const selfCheck = extractSection(content, '### 自检结论', '\n##');
|
|
104
117
|
if (!selfCheck) {
|
|
105
|
-
|
|
118
|
+
checks.close_self_check = { status: 'fail', detail: '缺 ### 自检结论 节' };
|
|
106
119
|
} else {
|
|
107
120
|
const bodyLines = selfCheck
|
|
108
121
|
.split('\n')
|
|
@@ -111,53 +124,146 @@ export function closeTaskFile(filePath, options = {}) {
|
|
|
111
124
|
.filter(Boolean);
|
|
112
125
|
const substantive = bodyLines.filter((l) => !PLACEHOLDER_RE.test(l));
|
|
113
126
|
if (substantive.length === 0) {
|
|
114
|
-
|
|
127
|
+
checks.close_self_check = {
|
|
128
|
+
status: 'fail',
|
|
129
|
+
detail: '自检结论未回填(空或纯占位符)',
|
|
130
|
+
};
|
|
131
|
+
} else {
|
|
132
|
+
checks.close_self_check = { status: 'pass', detail: '自检结论已回填' };
|
|
115
133
|
}
|
|
116
134
|
}
|
|
117
135
|
|
|
118
|
-
//
|
|
136
|
+
// close_acceptance
|
|
119
137
|
const acceptance = extractSection(content, '## 验收标准', '\n##');
|
|
120
138
|
if (!acceptance) {
|
|
121
|
-
|
|
139
|
+
checks.close_acceptance = { status: 'fail', detail: '缺 ## 验收标准 节' };
|
|
122
140
|
} else {
|
|
123
|
-
const unchecked = acceptance
|
|
124
|
-
.split('\n')
|
|
125
|
-
.filter((l) => UNCHECKED_RE.test(l));
|
|
141
|
+
const unchecked = acceptance.split('\n').filter((l) => UNCHECKED_RE.test(l));
|
|
126
142
|
if (unchecked.length > 0) {
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
}
|
|
143
|
+
checks.close_acceptance = {
|
|
144
|
+
status: 'fail',
|
|
145
|
+
detail: `验收标准 ${unchecked.length} 项未勾选`,
|
|
146
|
+
};
|
|
147
|
+
} else {
|
|
148
|
+
checks.close_acceptance = { status: 'pass', detail: '验收标准已勾选' };
|
|
134
149
|
}
|
|
135
150
|
}
|
|
136
151
|
|
|
137
|
-
//
|
|
152
|
+
// close_status
|
|
138
153
|
const statusLine = content.split('\n').find((l) => STATUS_RE.test(l));
|
|
139
154
|
const status = statusLine ? statusLine.match(STATUS_RE)[1].toLowerCase() : null;
|
|
140
155
|
if (!status) {
|
|
141
|
-
|
|
156
|
+
checks.close_status = { status: 'fail', detail: '未找到 > **状态** 行' };
|
|
142
157
|
} else if (!CLOSE_STATUSES.has(status)) {
|
|
143
|
-
|
|
158
|
+
checks.close_status = {
|
|
159
|
+
status: 'fail',
|
|
160
|
+
detail: `状态非 done/completed(当前: ${status})`,
|
|
161
|
+
};
|
|
162
|
+
} else {
|
|
163
|
+
checks.close_status = { status: 'pass', detail: `status=${status}` };
|
|
144
164
|
}
|
|
145
165
|
|
|
146
|
-
//
|
|
147
|
-
if (repoRoot) {
|
|
166
|
+
// close_review
|
|
167
|
+
if (!repoRoot) {
|
|
168
|
+
checks.close_review = {
|
|
169
|
+
status: 'fail',
|
|
170
|
+
detail: '无法定位 repo root · 无法查 reviews',
|
|
171
|
+
};
|
|
172
|
+
} else {
|
|
148
173
|
const review = findReview(repoRoot, abs);
|
|
149
|
-
if (
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
174
|
+
if (review.found) {
|
|
175
|
+
checks.close_review = { status: 'pass', detail: review.latest };
|
|
176
|
+
} else {
|
|
177
|
+
checks.close_review = { status: 'fail', detail: 'missing R<n> review' };
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
return {
|
|
182
|
+
file: abs,
|
|
183
|
+
slug: slug ?? fileSlug,
|
|
184
|
+
fileSlug,
|
|
185
|
+
repoRoot,
|
|
186
|
+
inActive,
|
|
187
|
+
invoke_hats: invokeHatsEval
|
|
188
|
+
? {
|
|
189
|
+
required: invokeHatsEval.required,
|
|
190
|
+
covered: invokeHatsEval.covered,
|
|
191
|
+
missing: invokeHatsEval.missing,
|
|
192
|
+
source: invokeHatsEval.resolution.source,
|
|
193
|
+
}
|
|
194
|
+
: null,
|
|
195
|
+
checks,
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* task close 机械校验 + 归档执行。
|
|
201
|
+
* 任一校验失败 → ok:false + blockers[],绝不 mv。
|
|
202
|
+
* 默认 dry-run(只检不 mv);yes=true 才执行归档。
|
|
203
|
+
*/
|
|
204
|
+
export function closeTaskFile(filePath, options = {}) {
|
|
205
|
+
const {
|
|
206
|
+
yes = false,
|
|
207
|
+
target: targetArg,
|
|
208
|
+
allowUnchecked = false,
|
|
209
|
+
allowNoReview = false,
|
|
210
|
+
allowInvokeGap = false,
|
|
211
|
+
cwd = process.cwd(),
|
|
212
|
+
} = options;
|
|
213
|
+
|
|
214
|
+
const abs = path.resolve(cwd, filePath);
|
|
215
|
+
const blockers = [];
|
|
216
|
+
const warnings = [];
|
|
217
|
+
|
|
218
|
+
if (!fs.existsSync(abs)) {
|
|
219
|
+
return {
|
|
220
|
+
ok: false,
|
|
221
|
+
blockers: [`task 文件不存在: ${filePath}`],
|
|
222
|
+
warnings,
|
|
223
|
+
file: abs,
|
|
224
|
+
slug: extractTaskSlug(abs),
|
|
225
|
+
dest: null,
|
|
226
|
+
dryRun: !yes,
|
|
227
|
+
moved: false,
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
const evaluated = evaluateCloseChecks(filePath, { cwd });
|
|
232
|
+
const { checks, inActive, slug, fileSlug, invoke_hats: invokeHats } = evaluated;
|
|
233
|
+
|
|
234
|
+
const pushCheck = (id, allow, allowLabel) => {
|
|
235
|
+
const c = checks[id];
|
|
236
|
+
if (!c) return;
|
|
237
|
+
if (c.status === 'pass') {
|
|
238
|
+
if (id === 'close_invoke' && /unknown_profile=/.test(c.detail)) {
|
|
239
|
+
warnings.push(
|
|
240
|
+
`warn: unknown invoke_retention_profile(已回退 default)· ${c.detail}`,
|
|
155
241
|
);
|
|
156
242
|
}
|
|
243
|
+
return;
|
|
157
244
|
}
|
|
158
|
-
|
|
245
|
+
if (allow) {
|
|
246
|
+
warnings.push(`warn: ${c.detail}(${allowLabel} 豁免 · 留痕)`);
|
|
247
|
+
} else {
|
|
248
|
+
blockers.push(
|
|
249
|
+
id === 'close_acceptance'
|
|
250
|
+
? `${c.detail}(或 --allow-unchecked 显式豁免)`
|
|
251
|
+
: id === 'close_review'
|
|
252
|
+
? 'missing R<n> review(docs/harness/reviews/ 无该 task 的 _audit_R 文件 · 或 --allow-no-review)'
|
|
253
|
+
: c.detail,
|
|
254
|
+
);
|
|
255
|
+
}
|
|
256
|
+
};
|
|
159
257
|
|
|
160
|
-
|
|
258
|
+
pushCheck('close_slug', false);
|
|
259
|
+
pushCheck('close_invoke', allowInvokeGap, '--allow-invoke-gap');
|
|
260
|
+
pushCheck('close_self_check', false);
|
|
261
|
+
pushCheck('close_acceptance', allowUnchecked, '--allow-unchecked');
|
|
262
|
+
pushCheck('close_status', false);
|
|
263
|
+
pushCheck('close_review', allowNoReview, '--allow-no-review');
|
|
264
|
+
|
|
265
|
+
// 归档目标(布局约束 · 非 yaml 守卫)
|
|
266
|
+
const activeDir = path.dirname(abs);
|
|
161
267
|
let dest = null;
|
|
162
268
|
if (targetArg) {
|
|
163
269
|
dest = path.resolve(cwd, targetArg);
|
|
@@ -187,14 +293,7 @@ export function closeTaskFile(filePath, options = {}) {
|
|
|
187
293
|
dest,
|
|
188
294
|
dryRun: !yes,
|
|
189
295
|
moved,
|
|
190
|
-
invoke_hats:
|
|
191
|
-
? {
|
|
192
|
-
required: invokeHatsEval.required,
|
|
193
|
-
covered: invokeHatsEval.covered,
|
|
194
|
-
missing: invokeHatsEval.missing,
|
|
195
|
-
source: invokeHatsEval.resolution.source,
|
|
196
|
-
}
|
|
197
|
-
: null,
|
|
296
|
+
invoke_hats: invokeHats,
|
|
198
297
|
};
|
|
199
298
|
}
|
|
200
299
|
|