@cyning/harness 2.8.0 → 2.9.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 +16 -0
- package/docs/ONBOARDING.md +7 -3
- package/docs/USER_GUIDE_v1.0_zh.md +9 -3
- package/docs/spec/SPEC-verify-full-reviews-gate_v1.md +183 -0
- package/harness/lifecycle.yaml +3 -2
- package/lib/cli.js +2 -1
- package/lib/task-meta.js +17 -9
- package/lib/verify.js +30 -4
- package/package.json +1 -1
- package/wizard/gate-check.sh +34 -20
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,21 @@
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
## [2.9.0] - 2026-07-25
|
|
8
|
+
|
|
9
|
+
### Changed
|
|
10
|
+
|
|
11
|
+
- **⚠ 行为变更 · 裸 `verify`(无 `--task`/`--spec`)纳入 reviews 留档闸(N4 · G2 residual)**:gate-check 通过后对每个 active 跑 `findReview`;任一缺失 → `VERIFY: BLOCKED · missing R<n> review(n/m tasks · basename…)` exit 2。豁免:`--allow-no-review`(WARN 放行)。
|
|
12
|
+
- **active 发现双路径**:`listActiveTasks` 与 `gate-check.sh` 同步扫描 `docs/tasks/active` ∪ `docs/harness/tasks/active`(同 basename 优先 Starter)。
|
|
13
|
+
- 全量 `--json`:每 task 的 `may_start_30` / `verify_ok` 纳入 `review_found`(与 `--task` 同构)。
|
|
14
|
+
- `lifecycle.yaml` `to_30` / `reviews_retention`:描述覆盖「`--task` 与全量」。
|
|
15
|
+
|
|
16
|
+
### Notes
|
|
17
|
+
|
|
18
|
+
- 全量模式仍**不**跑 task lint / audit D5(维持 N2)。
|
|
19
|
+
- SPEC:`docs/spec/SPEC-verify-full-reviews-gate_v1.md`
|
|
20
|
+
- minor · 行为更严 · 存量「闸表可 30 但无审查文」将被挡(可补审或 `--allow-no-review`)
|
|
21
|
+
|
|
7
22
|
## [2.8.0] - 2026-07-25
|
|
8
23
|
|
|
9
24
|
### Added
|
|
@@ -18,6 +33,7 @@
|
|
|
18
33
|
- 与 `--task` 互斥;**不**改 task 侧 verify/close。
|
|
19
34
|
- SPEC:`docs/spec/SPEC-spec-reviews-retention-gate_v1.md`
|
|
20
35
|
- minor · 行为新增(新模式)
|
|
36
|
+
- **已发布**:`@cyning/harness@2.8.0`(npm `latest` · 2026-07-25)· tag `v2.8.0` 已推送
|
|
21
37
|
|
|
22
38
|
## [2.7.0] - 2026-07-24
|
|
23
39
|
|
package/docs/ONBOARDING.md
CHANGED
|
@@ -45,11 +45,13 @@ cd your-project
|
|
|
45
45
|
`npx @cyning/harness verify` 在 30 执行前聚合扫描人工闸与测试声明,确保 ICVO 公理可机械检查:
|
|
46
46
|
|
|
47
47
|
```bash
|
|
48
|
-
# 30 前聚合验证(gate-check +
|
|
48
|
+
# 30 前聚合验证(gate-check + reviews + S5 warn + 可选 --graph)
|
|
49
|
+
# 无 --task:双路径 active(docs/tasks/active ∪ docs/harness/tasks/active)+ 全量 reviews(v2.9+)
|
|
49
50
|
npx @cyning/harness verify --target /path/to/your-repo
|
|
51
|
+
# 缺审查文豁免:--allow-no-review
|
|
50
52
|
npx @cyning/harness verify --target /path/to/your-repo \
|
|
51
53
|
--task docs/tasks/active/task_xxx.md
|
|
52
|
-
# --task 另跑 task lint(v2.7+ · E 级仅 WARN,不挡 may_start_30)
|
|
54
|
+
# --task 另跑 audit D5 + task lint(v2.7+ · E 级仅 WARN,不挡 may_start_30)
|
|
53
55
|
# 抑制 lint WARN:加 --allow-lint-fail
|
|
54
56
|
|
|
55
57
|
# SPEC→00 前:审查文存在性(v2.8+ · 与 --task 互斥)
|
|
@@ -76,7 +78,9 @@ npx @cyning/harness sync index --target /path/to/your-repo
|
|
|
76
78
|
| 公理 | 检查项 | 行为 |
|
|
77
79
|
|------|--------|------|
|
|
78
80
|
| **D3** | 30 前置人闸 | 复用 `gate-check.sh`,HG-AUDIT-R1 非 approved 时 verify 非 0 |
|
|
79
|
-
| **D5** | 改码任务测试声明 |
|
|
81
|
+
| **D5** | 改码任务测试声明 | 仅 `--task`:`test_strategy=required` 但无测试/CI 引用时 verify 非 0 |
|
|
82
|
+
| **reviews** | R<n> 审查文存在 | `--task` 与全量(v2.9+)· 缺文 BLOCKED · `--allow-no-review` |
|
|
83
|
+
| **active** | 任务发现 | 双路径 `docs/tasks/active` ∪ `docs/harness/tasks/active`(v2.9+) |
|
|
80
84
|
| **S5** | Git 工作区干净 | dirty 时 warn(不直接 fail verify,但 apply 须 `--force`) |
|
|
81
85
|
| **lint** | task 结构(仅 `--task`) | v2.7+ E 级 → `WARN: task lint`(不改 exit / `may_start_30`) |
|
|
82
86
|
|
|
@@ -111,7 +111,11 @@ npx @cyning/harness@1.0.1 init --preset harness-only --ide cursor,agents --yes
|
|
|
111
111
|
**原则**:Agent 可以写 task 和 review,**人工闸只有维护者能签**。
|
|
112
112
|
|
|
113
113
|
```bash
|
|
114
|
-
#
|
|
114
|
+
# 全量(无 --task):双路径 active + 闸表 + reviews(v2.9+ · 不跑 lint/D5)
|
|
115
|
+
npx @cyning/harness verify --target .
|
|
116
|
+
# 缺审查文豁免:--allow-no-review
|
|
117
|
+
|
|
118
|
+
# 单 task:gate-check + audit D5 + reviews + task lint WARN + S5 · 可选 --graph
|
|
115
119
|
npx @cyning/harness verify --target . --task docs/tasks/active/task_xxx.md
|
|
116
120
|
# lint FAIL 仅 WARN(v2.7+ · 不挡 30);抑制:--allow-lint-fail
|
|
117
121
|
|
|
@@ -146,6 +150,7 @@ npx @cyning/harness gate-check --graph --target . # Inform 图谱闸
|
|
|
146
150
|
| `HG-GRAPH-MODULES` | 架构模块表人签 | pending → 拒改码 30 |
|
|
147
151
|
| `HG-RELEASE` | 发版闸(产品仓) | 一般业务仓不涉及 |
|
|
148
152
|
| `task_lint`(v2.7+) | `verify --task` 结构检查 | **仅 WARN** · 不改 `may_start_30` |
|
|
153
|
+
| `reviews`(全量 v2.9+) | 裸 verify 亦查 R<n> 文 | 缺文 → BLOCKED · `--allow-no-review` |
|
|
149
154
|
|
|
150
155
|
### 5.1 Agent handoff(v2.0.2+)
|
|
151
156
|
|
|
@@ -153,7 +158,8 @@ npx @cyning/harness gate-check --graph --target . # Inform 图谱闸
|
|
|
153
158
|
|
|
154
159
|
| JSON 字段 | 含义 |
|
|
155
160
|
| --- | --- |
|
|
156
|
-
| `may_start_30` |
|
|
161
|
+
| `may_start_30` | 闸表可 30 **且** `review_found`(或豁免) |
|
|
162
|
+
| `review_found` | R<n> 审查文是否存在(全量与 `--task` 同构 · v2.9+) |
|
|
157
163
|
| `blocked_reason` | 阻塞时 D2/D3 文案 |
|
|
158
164
|
| `review_path` | 最新 `*_audit_R1_*.md`(相对 target) |
|
|
159
165
|
| `entry_invoke_30` | task 表原始路径 |
|
|
@@ -177,7 +183,7 @@ Schema:[`schema/verify_result.v1.schema.json`](../schema/verify_result.v1.sche
|
|
|
177
183
|
| `npx @cyning/harness init` | 首次安装模板与 manifest(可选 `--with-scripts`) |
|
|
178
184
|
| `npx @cyning/harness upgrade` | 同步产品包更新(可加 `--gate-check` 先 audit) |
|
|
179
185
|
| `npx @cyning/harness check` | 检查是否有新版本 |
|
|
180
|
-
| `npx @cyning/harness verify` |
|
|
186
|
+
| `npx @cyning/harness verify` | 全量:双路径 + reviews(v2.9+);`--task`:30 前聚合;`--spec`:SPEC→00(v2.8+ · 互斥) |
|
|
181
187
|
| `npx @cyning/harness lifecycle show` | 只读展示 `harness/lifecycle.yaml`(状态/转移/守卫 · v2.7+ · 非引擎) |
|
|
182
188
|
| `npx @cyning/harness gate-check` | 仅人工闸(`--graph` / `--json`) |
|
|
183
189
|
| `npx @cyning/harness audit` | ICVO 机械审计(D3/D5/S5) |
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
# SPEC:verify 全量模式纳入 reviews 留档闸(N4)(v1)
|
|
2
|
+
|
|
3
|
+
> **状态**:`signed`(维护者签收 2026-07-25 · 对话「签收」)
|
|
4
|
+
> **track**:`feature`
|
|
5
|
+
> **关联图谱**:无(纯 Harness 工具链)
|
|
6
|
+
> **上游**:[`PLAN_post_g4_next_mechanization_v1_zh.md`](../../../docs/harness/guides/PLAN_post_g4_next_mechanization_v1_zh.md) · N4 · G2 residual
|
|
7
|
+
> **前置**:G2 `@cyning/harness@2.5.0`(`--task` 已查 reviews)· N3 `@2.8.0`
|
|
8
|
+
> **下游**:30/40 已完成(分支 `task/cyning-harness-verify-full-reviews-gate` · package **2.9.0** · 待 publish)
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## Harness 元信息
|
|
13
|
+
|
|
14
|
+
| 字段 | 值 |
|
|
15
|
+
|------|-----|
|
|
16
|
+
| **spec_slug** | `verify-full-reviews-gate` |
|
|
17
|
+
| **test_strategy** | `required` |
|
|
18
|
+
| **test_strategy_note** | 全量模式:有文 PASS / 缺文 BLOCKED / `--allow-no-review`;双路径发现;既有 `--task` 回归不变 |
|
|
19
|
+
| **entry_invoke_10_spec** | `Projects/docs/harness/invokes/by-task/cyning-harness-verify-full-reviews-gate/invoke_20260725_10_spec_verify_full_reviews_gate.md` |
|
|
20
|
+
| **entry_invoke_00_draft** | 工作区 `docs/harness/prompts/PROMPT_00_draft_spec_or_task_v1_zh.md` |
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## 1. 背景与目标
|
|
25
|
+
|
|
26
|
+
G2(v2.5.0)把「R<n> 审查文存在」挂在 **`verify --task`** 与 `task close`,但 R2 明确:**无 `--task` 全量模式本波只查闸表**。后果:
|
|
27
|
+
|
|
28
|
+
1. **语义裂缝**:`verify --task X` 会因缺审查文 BLOCKED,但裸 `verify`(全量)在闸表全 approved 时仍可能 **PASS**——「仓库可否 30」答案因是否传 `--task` 而分裂。
|
|
29
|
+
2. **发现路径裂缝**:`gate-check` / `listActiveTasks` 只扫 `docs/tasks/active`;工作区 Extended 真值在 `docs/harness/tasks/active`。对 Projects 根跑裸 `verify` →「无 active」vacuous PASS,与人工闸表现实脱节。
|
|
30
|
+
|
|
31
|
+
**dogfood(2026-07-25 · `findReview` + `evaluateMayStart30`)**:`docs/harness/tasks/active` **15** 个 task:
|
|
32
|
+
|
|
33
|
+
| 指标 | 值 |
|
|
34
|
+
|------|-----|
|
|
35
|
+
| `may_start_30=true` | **4** |
|
|
36
|
+
| 其中缺 R<n> 审查文 | **3**(`a5_cli_verify` · `hat_chain_pointer_sync` · `task_validate_human_gate_ci_gate`) |
|
|
37
|
+
| 有审查文且可 30 | **1**(`ops_clarify_single_run_resume`) |
|
|
38
|
+
|
|
39
|
+
→ 若仅扩展 reviews 检查而不扩路径,N4 对工作区几乎无效果;若扩路径 + 查 reviews,裸 `verify` 将暴露上述 3 个「闸表可 30 但无审查文」任务(预期拦截 · 可用 `--allow-no-review`)。
|
|
40
|
+
|
|
41
|
+
**目标**:闭合 G2 residual——全量模式与 `--task` 在 **reviews 存在性** 上同构;并让全量发现覆盖 Starter(`docs/tasks/active`)与 Extended(`docs/harness/tasks/active`)。
|
|
42
|
+
|
|
43
|
+
**职责切分(同 G2)**:机器 = 存在性;人 = `HG-AUDIT-R1` 结论。
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## 2. 范围
|
|
48
|
+
|
|
49
|
+
### D1 · 全量模式 reviews 闸(`lib/verify.js`)
|
|
50
|
+
|
|
51
|
+
- 当**无** `--task` 且**无** `--spec`:在 gate-check 通过后,对发现的每个 active task 调用 `findReview`:
|
|
52
|
+
- 任一缺失 → `VERIFY: BLOCKED · missing R<n> review`(reason 列出 basename,如 `3/15 tasks · a.md, b.md, …`)· exit 2
|
|
53
|
+
- `--allow-no-review` → 对缺失者 WARN 放行(与 `--task` 同旗)
|
|
54
|
+
- **不**在全量模式跑 task lint(维持 N2:仅 `--task`)
|
|
55
|
+
- **不**在全量模式跑 audit D5 逐 task(本波不变;另议)
|
|
56
|
+
|
|
57
|
+
### D2 · active 发现双路径
|
|
58
|
+
|
|
59
|
+
- `listActiveTasks(target)`:返回
|
|
60
|
+
`docs/tasks/active/task_*.md` ∪ `docs/harness/tasks/active/task_*.md`
|
|
61
|
+
(去重:同 basename 优先 Starter 路径;排序稳定)
|
|
62
|
+
- `wizard/gate-check.sh`:**同步**双路径扫描(无 `--task` 时),避免闸表集合 ≠ reviews 集合
|
|
63
|
+
- 文档:ONBOARDING/USER_GUIDE 注明双路径
|
|
64
|
+
|
|
65
|
+
### D3 · handoff 对齐
|
|
66
|
+
|
|
67
|
+
- 全量 `--json`:每个 task 的 `may_start_30` / `verify_ok` **纳入** `review_found`(与单 `--task` 一致);根级 `verify_ok` = 全部可 30(含审查文)
|
|
68
|
+
- `--agent-hint` 可显示缺审查文的 blocked 行
|
|
69
|
+
|
|
70
|
+
### D4 · lifecycle 登记
|
|
71
|
+
|
|
72
|
+
- `harness/lifecycle.yaml`:`to_30` 的 `reviews_retention` 注释/描述改为覆盖「`--task` 与全量模式」;不新增转移 id(同守卫 · 两入口)
|
|
73
|
+
|
|
74
|
+
### D5 · 测试 / 文档 / 版本
|
|
75
|
+
|
|
76
|
+
- 测试:全量三态(全有文 PASS / 一缺 BLOCKED / allow 豁免)+ 双路径夹具(仅 harness 路径也能被扫到)+ 既有 `--task` 回归
|
|
77
|
+
- CHANGELOG **v2.9.0**(⚠ 行为变更:裸 verify 更严)+ 豁免指引
|
|
78
|
+
- PLAN / G2 SPEC residual 回链关闭
|
|
79
|
+
|
|
80
|
+
### D6 · dogfood(实现波)
|
|
81
|
+
|
|
82
|
+
- 对工作区根或含 `docs/harness/tasks/active` 的 target 跑裸 `verify`,报告 BLOCKED 列表与豁免后结果;落自检结论(可修审查文或 `--allow-no-review`,不强制本波修齐 3 个存量)
|
|
83
|
+
|
|
84
|
+
---
|
|
85
|
+
|
|
86
|
+
## 3. 非范围
|
|
87
|
+
|
|
88
|
+
- 全量模式接入 **task lint**(N2-C / 另 SPEC)
|
|
89
|
+
- 全量模式逐 task **audit D5**
|
|
90
|
+
- SPEC 侧全量扫描(已有 `verify --spec` 单文件)
|
|
91
|
+
- 强制补齐 dogfood 3 个缺审查文的存量 task
|
|
92
|
+
- 审查文内容判定;状态机引擎
|
|
93
|
+
|
|
94
|
+
---
|
|
95
|
+
|
|
96
|
+
## 4. 验收标准
|
|
97
|
+
|
|
98
|
+
- [ ] 无 `--task`:active 均有 R<n> 文 → PASS(闸表亦过)
|
|
99
|
+
- [ ] 无 `--task`:至少一 task 缺审查文 → BLOCKED exit 2 · reason 含该 basename
|
|
100
|
+
- [ ] `--allow-no-review`:全量缺失 → WARN + PASS
|
|
101
|
+
- [ ] 仅存在 `docs/harness/tasks/active` 时,gate-check / listActiveTasks 仍能发现 task
|
|
102
|
+
- [ ] `--task` 行为与 v2.8 一致(回归)
|
|
103
|
+
- [ ] 全量 `--json`:`may_start_30` 含审查文条件
|
|
104
|
+
- [ ] `npm test` 全绿;CHANGELOG v2.9.0 行为变更醒目
|
|
105
|
+
- [ ] dogfood 分布落自检结论
|
|
106
|
+
|
|
107
|
+
---
|
|
108
|
+
|
|
109
|
+
## 5. failure_paths
|
|
110
|
+
|
|
111
|
+
| 触发条件 | 系统行为 | 可重试 |
|
|
112
|
+
|----------|----------|--------|
|
|
113
|
+
| 全量扫描发现缺审查文 | BLOCKED · 列出 task | 补 20 审 / `--allow-no-review` |
|
|
114
|
+
| 双路径同 basename 重复 | 去重后只检一次 | — |
|
|
115
|
+
| 业务仓只有 Starter 路径 | 行为 = 只扫 `docs/tasks/active`(与今同构 + reviews) | — |
|
|
116
|
+
| 未升级 2.9.0 | 全量仍不查 reviews | upgrade |
|
|
117
|
+
| 存量 3 个可 30 无审查文被挡 | **预期** | 补审或豁免 |
|
|
118
|
+
|
|
119
|
+
---
|
|
120
|
+
|
|
121
|
+
## 6. 依赖与引用
|
|
122
|
+
|
|
123
|
+
- G2:`findReview` · `--allow-no-review` · SPEC residual ③
|
|
124
|
+
- N2:全量不跑 lint(本 SPEC 维持)
|
|
125
|
+
- `listActiveTasks` · `gate-check.sh` ACTIVE_DIR
|
|
126
|
+
- PLAN N4
|
|
127
|
+
|
|
128
|
+
---
|
|
129
|
+
|
|
130
|
+
## 7. 思考轮(10-spec 回填 · R0–R5)
|
|
131
|
+
|
|
132
|
+
### R0 · 读入与约束
|
|
133
|
+
|
|
134
|
+
读入:PLAN N4;G2 R2「全量本波不变」+ residual ③;`verify.js` 仅 `taskFile` 分支查 reviews;`listActiveTasks` / gate-check 仅 `docs/tasks/active`;dogfood 15/4/3。约束:行为变更须豁免阀 + CHANGELOG;挂点须与检查物同时刻(审查文应在 R1 后已存在)。
|
|
135
|
+
|
|
136
|
+
### R1 · 范围 / 非范围 / 场景
|
|
137
|
+
|
|
138
|
+
**场景**:① 维护者/Agent 裸 `verify` 做仓库健康检查,应与逐 task `--task` 对「可 30」同答案;② Extended 工作区 active 在 harness 路径下也要被扫到;③ 存量缺文用豁免,不强制本波修齐。
|
|
139
|
+
**非范围**:lint/D5 全量化、SPEC 全量、内容判定——避免 N4 膨胀成「verify 大一统」。
|
|
140
|
+
|
|
141
|
+
### R2 · 方案对比
|
|
142
|
+
|
|
143
|
+
| 决策点 | 选项 | 裁定 | 理由 |
|
|
144
|
+
|---|---|---|---|
|
|
145
|
+
| 是否纳入 reviews | 否(关账 N4)/ warn / block | **block + `--allow-no-review`** | 与 G2 `--task` 同构;warn 再留自觉层;dogfood 已能量化误伤面(3 task) |
|
|
146
|
+
| 检查哪些 task | 全部 active / 仅 may_start_30 | **全部发现的 active**(与 gate-check 集合对齐) | 闸表未 approved 者 gate-check 已挡;对已过闸者补审查文洞;实现简单 |
|
|
147
|
+
| 双路径 | 仅 Starter / Starter∪Extended | **∪ 双路径**(listActiveTasks + gate-check 同步) | 否则 N4 对 Projects dogfood 无效 |
|
|
148
|
+
| 全量 lint | 顺带做 / 不做 | **不做** | N2 已拒;FAIL 率仍高 |
|
|
149
|
+
| 版本 | 2.8.x / 2.9.0 | **2.9.0** | 裸 verify 行为变更 · minor |
|
|
150
|
+
|
|
151
|
+
### R3 · 边界 / 失败语义 / 安全
|
|
152
|
+
|
|
153
|
+
- **误伤**:主要是「闸表可 30 且无审查文」——正是 G2 要抓的类;豁免旗复用。
|
|
154
|
+
- **空 active**:两路径皆无 → 维持现状(gate-check 提示无 active · exit 0)。
|
|
155
|
+
- **安全**:只读。
|
|
156
|
+
- **兼容**:`--task` / `--spec` 不变。
|
|
157
|
+
|
|
158
|
+
### R4 · 验收 / 可测性 / test_strategy
|
|
159
|
+
|
|
160
|
+
`test_strategy: required`。夹具:双路径目录、全量缺一文、allow 豁免;回归 verify-review-gate / verify-lint-warn。dogfood 命令写进 40 自检。
|
|
161
|
+
|
|
162
|
+
### R5 · SPEC 签收就绪 · 是否可交 00 出 task
|
|
163
|
+
|
|
164
|
+
自足:裁定、路径、豁免、版本清晰。**可交 00**:单 task `cyning-harness-verify-full-reviews-gate`。图谱无需。关闭 G2 residual ③。
|
|
165
|
+
|
|
166
|
+
### 思考轮控制
|
|
167
|
+
|
|
168
|
+
| 字段 | 值 |
|
|
169
|
+
|------|-----|
|
|
170
|
+
| `actual_last_round` | `R5` |
|
|
171
|
+
| `early_stop` | `no` |
|
|
172
|
+
| `early_stop_reason` | — |
|
|
173
|
+
| `residual_risks` | ① gate-check 双路径改 bash 须防破既有单测路径假设;② 去重规则若两目录同 slug 不同内容属数据错误——本波只去重不深比较;③ 全量 D5 仍空缺,健康检查仍非「可 30」充分条件 |
|
|
174
|
+
| `round_extension_note` | — |
|
|
175
|
+
|
|
176
|
+
---
|
|
177
|
+
|
|
178
|
+
## 修订记录
|
|
179
|
+
|
|
180
|
+
| 日期 | 摘要 |
|
|
181
|
+
|------|------|
|
|
182
|
+
| 2026-07-25 | 10-spec R0–R5(维护者点菜「继续 N4」)· dogfood 15/4/3 |
|
|
183
|
+
| 2026-07-25 | 维护者签收(对话「签收」)· → 00 起草 task |
|
package/harness/lifecycle.yaml
CHANGED
|
@@ -34,7 +34,7 @@ transitions:
|
|
|
34
34
|
from: ["draft", "in_progress"]
|
|
35
35
|
to: in_progress
|
|
36
36
|
hat: "30"
|
|
37
|
-
description: "执行帽开工(verify --task
|
|
37
|
+
description: "执行帽开工(verify --task 与全量模式聚合 · v2.9+)"
|
|
38
38
|
guards:
|
|
39
39
|
- id: HG-AUDIT-R1
|
|
40
40
|
command_or_check: "gate-check · human_gate HG-AUDIT-R1=approved"
|
|
@@ -46,9 +46,10 @@ transitions:
|
|
|
46
46
|
command_or_check: "verify/audit · test_strategy vs 测试文件存在"
|
|
47
47
|
severity: block
|
|
48
48
|
- id: reviews_retention
|
|
49
|
-
command_or_check: "verify · findReview R<n> 存在性(v2.5+)"
|
|
49
|
+
command_or_check: "verify --task 与全量 · findReview R<n> 存在性(v2.5+ · 全量 v2.9+)"
|
|
50
50
|
severity: block
|
|
51
51
|
allow_flag: "--allow-no-review"
|
|
52
|
+
note: "全量模式扫描双路径 active;不跑 lint / D5"
|
|
52
53
|
- id: task_lint
|
|
53
54
|
command_or_check: "verify --task · lintTaskFile(v2.7+)"
|
|
54
55
|
severity: warn
|
package/lib/cli.js
CHANGED
|
@@ -400,7 +400,8 @@ async function cmdVerify(args) {
|
|
|
400
400
|
if (args.includes('--help') || args.includes('-h')) {
|
|
401
401
|
console.log(`用法: npx @cyning/harness verify [--target PATH] [--task FILE | --spec FILE] [--graph] [--json] [--agent-hint] [--workspace-root PATH] [--allow-no-review] [--allow-lint-fail] [--allow-no-spec-review]
|
|
402
402
|
|
|
403
|
-
无 --task/--spec:扫描 docs/tasks/active/
|
|
403
|
+
无 --task/--spec:扫描 docs/tasks/active ∪ docs/harness/tasks/active(同 basename 优先 Starter);
|
|
404
|
+
闸表 + 全量 reviews 存在性(v2.9+)· 不跑 task lint / D5 · --allow-no-review。
|
|
404
405
|
--task:30 前聚合(gate + audit + reviews + lint WARN)· --allow-no-review / --allow-lint-fail。
|
|
405
406
|
--spec:SPEC→00 前查审查文存在性(v2.8+ · 与 --task 互斥)· --allow-no-spec-review。
|
|
406
407
|
推荐:--spec <产品仓 SPEC> --workspace-root <含 docs/harness/reviews 的工作区根>
|
package/lib/task-meta.js
CHANGED
|
@@ -310,17 +310,25 @@ export function buildTaskHandoff(target, taskFile, options = {}) {
|
|
|
310
310
|
}
|
|
311
311
|
|
|
312
312
|
/**
|
|
313
|
-
* 扫描 target 下 active task
|
|
313
|
+
* 扫描 target 下 active task 列表(N4 · v2.9+)。
|
|
314
|
+
* 双路径:docs/tasks/active ∪ docs/harness/tasks/active;
|
|
315
|
+
* 同 basename 优先 Starter;排序稳定。
|
|
314
316
|
*/
|
|
315
317
|
export function listActiveTasks(target) {
|
|
316
|
-
const
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
.
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
318
|
+
const dirs = ['docs/tasks/active', 'docs/harness/tasks/active'];
|
|
319
|
+
const byBasename = new Map();
|
|
320
|
+
|
|
321
|
+
for (const dir of dirs) {
|
|
322
|
+
const abs = path.join(target, dir);
|
|
323
|
+
if (!fs.existsSync(abs)) continue;
|
|
324
|
+
for (const f of fs.readdirSync(abs)) {
|
|
325
|
+
if (!f.startsWith('task_') || !f.endsWith('.md')) continue;
|
|
326
|
+
if (byBasename.has(f)) continue;
|
|
327
|
+
byBasename.set(f, path.join(dir, f).replace(/\\/g, '/'));
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
return [...byBasename.values()].sort();
|
|
324
332
|
}
|
|
325
333
|
|
|
326
334
|
function escapeRegExp(s) {
|
package/lib/verify.js
CHANGED
|
@@ -14,8 +14,9 @@ import {
|
|
|
14
14
|
import { lintTaskFile } from './task-lint.js';
|
|
15
15
|
|
|
16
16
|
/**
|
|
17
|
-
* 30 前聚合验证:gate-check + audit D5(仅 --task)+ reviews
|
|
18
|
-
|
|
17
|
+
* 30 前聚合验证:gate-check + audit D5(仅 --task)+ reviews 留档闸
|
|
18
|
+
*(--task 与全量 · N4/v2.9+)+ task lint WARN(仅 --task · v2.7+)
|
|
19
|
+
* + S5 git-clean warn + 可选 --graph
|
|
19
20
|
*/
|
|
20
21
|
export function verifyTarget(target, options = {}) {
|
|
21
22
|
const { taskFile, graph, allowNoReview = false, allowLintFail = false } = options;
|
|
@@ -69,6 +70,26 @@ export function verifyTarget(target, options = {}) {
|
|
|
69
70
|
gateResult.stdout += `WARN: task lint FAIL · ${rules || 'errors'}(不挡 may_start_30 · 修结构或 --allow-lint-fail)\n`;
|
|
70
71
|
}
|
|
71
72
|
}
|
|
73
|
+
} else {
|
|
74
|
+
// 3c. 全量 reviews 闸(N4 · v2.9+):不跑 lint / D5
|
|
75
|
+
const taskFiles = listActiveTasks(target);
|
|
76
|
+
const missing = [];
|
|
77
|
+
for (const tf of taskFiles) {
|
|
78
|
+
if (!findReview(target, tf).found) missing.push(path.basename(tf));
|
|
79
|
+
}
|
|
80
|
+
if (missing.length > 0) {
|
|
81
|
+
const detail = `${missing.length}/${taskFiles.length} tasks · ${missing.join(', ')}`;
|
|
82
|
+
if (allowNoReview) {
|
|
83
|
+
gateResult.stdout += `WARN: missing R<n> review(--allow-no-review 豁免 · 留痕)· ${detail}\n`;
|
|
84
|
+
} else {
|
|
85
|
+
return {
|
|
86
|
+
ok: false,
|
|
87
|
+
exitCode: 2,
|
|
88
|
+
reason: `missing R<n> review(${detail})`,
|
|
89
|
+
stdout: gateResult.stdout,
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
}
|
|
72
93
|
}
|
|
73
94
|
|
|
74
95
|
// 4. S5 git-clean:warn 不挡
|
|
@@ -335,14 +356,19 @@ export function buildVerifyHandoff(target, options = {}) {
|
|
|
335
356
|
const taskFiles = listActiveTasks(target);
|
|
336
357
|
const tasks = taskFiles.map((tf) => {
|
|
337
358
|
const handoff = buildTaskHandoff(target, tf, handoffOpts);
|
|
359
|
+
const reviewOk = handoff.review_found || allowNoReview;
|
|
360
|
+
const verifyOk = handoff.may_start_30 && reviewOk;
|
|
338
361
|
return {
|
|
339
362
|
schema_version: '1',
|
|
340
|
-
verify_ok:
|
|
363
|
+
verify_ok: verifyOk,
|
|
341
364
|
...handoff,
|
|
365
|
+
may_start_30: verifyOk,
|
|
366
|
+
blocked_reason: handoff.blocked_reason ?? (reviewOk ? null : 'missing R<n> review'),
|
|
367
|
+
next_hat: verifyOk ? '30' : null,
|
|
342
368
|
};
|
|
343
369
|
});
|
|
344
370
|
|
|
345
|
-
const verifyOk = tasks.length > 0 && tasks.every((t) => t.
|
|
371
|
+
const verifyOk = tasks.length > 0 && tasks.every((t) => t.verify_ok);
|
|
346
372
|
return {
|
|
347
373
|
schema_version: '1',
|
|
348
374
|
verify_ok: verifyOk,
|
package/package.json
CHANGED
package/wizard/gate-check.sh
CHANGED
|
@@ -174,6 +174,29 @@ if [[ "$JSON_MODE" != "1" ]]; then
|
|
|
174
174
|
echo ""
|
|
175
175
|
fi
|
|
176
176
|
|
|
177
|
+
# N4 · v2.9+:双路径发现(Starter ∪ Extended);同 basename 优先 Starter
|
|
178
|
+
# 输出绝对路径列表到 TASK_LIST(bash 3.2 兼容 · 无关联数组)
|
|
179
|
+
collect_active_tasks() {
|
|
180
|
+
TASK_LIST=()
|
|
181
|
+
local dir tf base existing already
|
|
182
|
+
for dir in "$TARGET/docs/tasks/active" "$TARGET/docs/harness/tasks/active"; do
|
|
183
|
+
[[ -d "$dir" ]] || continue
|
|
184
|
+
for tf in "$dir"/task_*.md; do
|
|
185
|
+
[[ -f "$tf" ]] || continue
|
|
186
|
+
base="$(basename "$tf")"
|
|
187
|
+
already=0
|
|
188
|
+
for existing in "${TASK_LIST[@]+"${TASK_LIST[@]}"}"; do
|
|
189
|
+
if [[ "$(basename "$existing")" == "$base" ]]; then
|
|
190
|
+
already=1
|
|
191
|
+
break
|
|
192
|
+
fi
|
|
193
|
+
done
|
|
194
|
+
[[ "$already" == "1" ]] && continue
|
|
195
|
+
TASK_LIST+=("$tf")
|
|
196
|
+
done
|
|
197
|
+
done
|
|
198
|
+
}
|
|
199
|
+
|
|
177
200
|
BLOCKED=0
|
|
178
201
|
|
|
179
202
|
if [[ "$GRAPH_MODE" == "1" ]]; then
|
|
@@ -191,26 +214,16 @@ if [[ "$GRAPH_MODE" == "1" ]]; then
|
|
|
191
214
|
fi
|
|
192
215
|
graph_for_task "$TASK_FILE"
|
|
193
216
|
else
|
|
194
|
-
|
|
195
|
-
if [[
|
|
217
|
+
collect_active_tasks
|
|
218
|
+
if [[ ${#TASK_LIST[@]} -eq 0 ]]; then
|
|
196
219
|
if [[ "$JSON_MODE" == "1" ]]; then
|
|
197
220
|
printf ']\n'
|
|
198
221
|
else
|
|
199
|
-
echo "
|
|
222
|
+
echo "无 active task_*.md(docs/tasks/active ∪ docs/harness/tasks/active)"
|
|
200
223
|
fi
|
|
201
224
|
exit 0
|
|
202
225
|
fi
|
|
203
|
-
|
|
204
|
-
if [[ ! -e "${TASK_FILES[0]}" ]]; then
|
|
205
|
-
if [[ "$JSON_MODE" == "1" ]]; then
|
|
206
|
-
printf ']\n'
|
|
207
|
-
else
|
|
208
|
-
echo "无 active task_*.md"
|
|
209
|
-
fi
|
|
210
|
-
exit 0
|
|
211
|
-
fi
|
|
212
|
-
for tf in "$ACTIVE_DIR"/task_*.md; do
|
|
213
|
-
[[ -f "$tf" ]] || continue
|
|
226
|
+
for tf in "${TASK_LIST[@]}"; do
|
|
214
227
|
if [[ "$JSON_MODE" == "1" ]]; then
|
|
215
228
|
[[ "$local_first" == "1" ]] || printf ','
|
|
216
229
|
local_first=0
|
|
@@ -234,15 +247,16 @@ if [[ -n "$TASK_FILE" ]]; then
|
|
|
234
247
|
fi
|
|
235
248
|
check_one_task "$TASK_FILE" || BLOCKED=1
|
|
236
249
|
else
|
|
237
|
-
|
|
238
|
-
[[ -
|
|
239
|
-
|
|
240
|
-
|
|
250
|
+
collect_active_tasks
|
|
251
|
+
if [[ ${#TASK_LIST[@]} -eq 0 ]]; then
|
|
252
|
+
if [[ ! -d "$TARGET/docs/tasks/active" && ! -d "$TARGET/docs/harness/tasks/active" ]]; then
|
|
253
|
+
echo "无 docs/tasks/active 且无 docs/harness/tasks/active" >&2
|
|
254
|
+
exit 1
|
|
255
|
+
fi
|
|
241
256
|
echo "无 active task_*.md"
|
|
242
257
|
exit 0
|
|
243
258
|
fi
|
|
244
|
-
for tf in "$
|
|
245
|
-
[[ -f "$tf" ]] || continue
|
|
259
|
+
for tf in "${TASK_LIST[@]}"; do
|
|
246
260
|
check_one_task "$tf" || BLOCKED=1
|
|
247
261
|
done
|
|
248
262
|
fi
|