@cyning/harness 2.10.0 → 2.11.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 +17 -1
- package/docs/ONBOARDING.md +6 -0
- package/docs/USER_GUIDE_v1.0_zh.md +2 -1
- package/docs/spec/SPEC-discipline-show_v1.md +89 -0
- package/docs/spec/SPEC-lifecycle-guard-expand_v1.md +100 -0
- package/harness/discipline-coverage.yaml +1 -1
- package/harness/lifecycle.yaml +2 -0
- package/lib/audit.js +5 -1
- package/lib/cli.js +45 -1
- package/lib/discipline-coverage.js +52 -0
- package/lib/lifecycle.js +48 -15
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,22 @@
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
## [2.11.0] - 2026-07-25
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- **`discipline show [--json]`(Post-2.10 Epic E)**:只读展示 `harness/discipline-coverage.yaml`(镜像 `lifecycle show`)。**无** `audit --discipline` UI。
|
|
12
|
+
|
|
13
|
+
### Changed
|
|
14
|
+
|
|
15
|
+
- **`lifecycle dry-run` 守卫扩面(Post-2.10 Epic A)**:`--task` + `to_30` 增接 `HG-TASK-DRAFT` · `audit_D5` · `task_lint`(与既有 `HG-AUDIT-R1` / `reviews_retention`)。`task_lint` 仍为 yaml **warn**;`--allow-lint-fail` 可将 fail 豁免为 warn。`close_*` 仍 `unevaluated`。**无** `--apply` / G7 / N2-C。
|
|
16
|
+
|
|
17
|
+
### Notes
|
|
18
|
+
|
|
19
|
+
- SPEC:`docs/spec/SPEC-lifecycle-guard-expand_v1.md` · `docs/spec/SPEC-discipline-show_v1.md`
|
|
20
|
+
- minor · `lifecycle show` / verify 硬闸语义不变
|
|
21
|
+
- `discipline-coverage.yaml` `as_of_package_version` → **2.11.0**
|
|
22
|
+
|
|
7
23
|
## [2.10.0] - 2026-07-25
|
|
8
24
|
|
|
9
25
|
### Added
|
|
@@ -19,8 +35,8 @@
|
|
|
19
35
|
### Notes
|
|
20
36
|
|
|
21
37
|
- SPEC:`docs/spec/SPEC-lifecycle-engine-min_v1.md` · `docs/spec/SPEC-discipline-coverage-yaml_v1.md`
|
|
22
|
-
- Epic 统一发版窗 · 维护者 publish 前复检
|
|
23
38
|
- minor · `lifecycle show` 行为不变
|
|
39
|
+
- **已发布**:`@cyning/harness@2.10.0`(npm `latest` · 2026-07-25)· tag `v2.10.0` 已推送
|
|
24
40
|
|
|
25
41
|
## [2.9.0] - 2026-07-25
|
|
26
42
|
|
package/docs/ONBOARDING.md
CHANGED
|
@@ -63,11 +63,16 @@ npx @cyning/harness verify --spec docs/spec/SPEC-xxx_v1.md \
|
|
|
63
63
|
npx @cyning/harness lifecycle show
|
|
64
64
|
npx @cyning/harness lifecycle show --json
|
|
65
65
|
# 转移资格 dry-run(v2.10+ · 旁路报告 · 不替代 verify)
|
|
66
|
+
# v2.11+ to_30 已接线:HG-* · reviews · audit_D5 · task_lint(close_* 仍可 unevaluated)
|
|
66
67
|
npx @cyning/harness lifecycle dry-run --transition to_30 --from draft \
|
|
67
68
|
--task docs/tasks/active/task_xxx.md
|
|
68
69
|
# 无 --task:仅结构 + 守卫清单 unevaluated
|
|
69
70
|
npx @cyning/harness lifecycle dry-run --transition to_30 --from draft
|
|
70
71
|
|
|
72
|
+
# 机械化率资产只读(v2.11+ · SoT=discipline-coverage.yaml)
|
|
73
|
+
npx @cyning/harness discipline show
|
|
74
|
+
npx @cyning/harness discipline show --json
|
|
75
|
+
|
|
71
76
|
# 仅人工闸
|
|
72
77
|
npx @cyning/harness gate-check --target /path/to/your-repo
|
|
73
78
|
npx @cyning/harness gate-check --graph --target /path/to/your-repo
|
|
@@ -90,6 +95,7 @@ npx @cyning/harness sync index --target /path/to/your-repo
|
|
|
90
95
|
| **lint** | task 结构(仅 `--task`) | v2.7+ E 级 → `WARN: task lint`(不改 exit / `may_start_30`) |
|
|
91
96
|
|
|
92
97
|
生命周期真值:[`harness/lifecycle.yaml`](../harness/lifecycle.yaml)(`lifecycle show` 只读 · `lifecycle dry-run` 资格判定 v2.10+)。
|
|
98
|
+
机械化率资产:[`harness/discipline-coverage.yaml`](../harness/discipline-coverage.yaml)(`discipline show` 只读 · v2.11+)。
|
|
93
99
|
|
|
94
100
|
机械化率覆盖资产(Starter):[`harness/discipline-coverage.yaml`](../harness/discipline-coverage.yaml)(SoT · 随版本改 statements/gaps;**无** audit UI)。
|
|
95
101
|
|
|
@@ -133,6 +133,7 @@ npx @cyning/harness verify --target . \
|
|
|
133
133
|
# 只读生命周期登记(方向二 · harness/lifecycle.yaml · 登记真值)
|
|
134
134
|
npx @cyning/harness lifecycle show [--json]
|
|
135
135
|
# 转移资格 dry-run(v2.10+ · 引擎消费 yaml · 旁路报告 · 非 runner / 非 G7)
|
|
136
|
+
# v2.11+ to_30 已接线 HG-* / reviews / audit_D5 / task_lint(close_* 仍可 unevaluated)
|
|
136
137
|
npx @cyning/harness lifecycle dry-run --transition to_30 --from draft \
|
|
137
138
|
[--task docs/tasks/active/task_xxx.md] [--json] [--allow-no-review]
|
|
138
139
|
|
|
@@ -189,7 +190,7 @@ Schema:[`schema/verify_result.v1.schema.json`](../schema/verify_result.v1.sche
|
|
|
189
190
|
| `npx @cyning/harness verify` | 全量:双路径 + reviews(v2.9+);`--task`:30 前聚合;`--spec`:SPEC→00(v2.8+ · 互斥) |
|
|
190
191
|
| `npx @cyning/harness lifecycle show` | 只读展示 `harness/lifecycle.yaml`(登记 · v2.7+) |
|
|
191
192
|
| `npx @cyning/harness lifecycle dry-run` | 转移资格判定(引擎消费 yaml · v2.10+ · 旁路 · 非 G7) |
|
|
192
|
-
|
|
|
193
|
+
| `npx @cyning/harness discipline show` | 只读展示 `harness/discipline-coverage.yaml`(v2.11+ · 非 audit UI) |
|
|
193
194
|
| `npx @cyning/harness gate-check` | 仅人工闸(`--graph` / `--json`) |
|
|
194
195
|
| `npx @cyning/harness audit` | ICVO 机械审计(D3/D5/S5) |
|
|
195
196
|
| `npx @cyning/harness sync index` | 生成 `.cyning-harness/invoke_index.json` |
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# SPEC:discipline show CLI(Post-2.10 Epic E)(v1)
|
|
2
|
+
|
|
3
|
+
> **状态**:`signed`(维护者签收 2026-07-25 · 对话「签收 A+E」)
|
|
4
|
+
> **track**:`feature`
|
|
5
|
+
> **上游**:Epic [`EPIC_post_210_menu_serial_a_e_j_v1_zh.md`](../../../docs/harness/guides/EPIC_post_210_menu_serial_a_e_j_v1_zh.md) · RETRO 候选 **E** · T3 SPEC [`SPEC-discipline-coverage-yaml_v1.md`](./SPEC-discipline-coverage-yaml_v1.md)(资产已落地 · 本波补发现性)
|
|
6
|
+
> **前置**:`@cyning/harness@2.10.0` · Epic **A CLOSE** 后方可进本棒 30(串行)
|
|
7
|
+
> **下游**:人签 → 00 draft task(可与 A 签收同批起草,**30 仍串行**)→ … → 30
|
|
8
|
+
> **目标版本**:与 A 统一 **`2.11.0`**
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## Harness 元信息
|
|
13
|
+
|
|
14
|
+
| 字段 | 值 |
|
|
15
|
+
|------|-----|
|
|
16
|
+
| **spec_slug** | `discipline-show` |
|
|
17
|
+
| **invoke_slug** | `cyning-harness-discipline-show` |
|
|
18
|
+
| **test_strategy** | `required` |
|
|
19
|
+
| **test_strategy_note** | show 人读/JSON · 非法 yaml exit · 与 loadDisciplineCoverage 同源;lifecycle show 回归 |
|
|
20
|
+
| **Open Folder(实现)** | `cyning-harness/` |
|
|
21
|
+
| **epic_serial** | E(J → A → **本棒**) |
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## 1. 背景与目标
|
|
26
|
+
|
|
27
|
+
T3 已落盘 `harness/discipline-coverage.yaml` + schema + `npm test`,但 **无 CLI**,发现性弱(T3 residual)。
|
|
28
|
+
|
|
29
|
+
**一句话目标**:提供 **`discipline show [--json]`**,镜像 `lifecycle show` 的只读体验,不引入 `audit --discipline` 大 UI。
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
## 2. 范围
|
|
34
|
+
|
|
35
|
+
### D1 · CLI
|
|
36
|
+
|
|
37
|
+
- `npx @cyning/harness discipline show [--json]`
|
|
38
|
+
- 人读:摘要 `version` · `as_of_package_version` · `scope` · statements 计数按 status · gaps 计数按 status;可选短表(id/status/summary 截断)
|
|
39
|
+
- `--json`:输出 `loadDisciplineCoverage` 校验后的数据对象(或稳定包装 `{ ok, data }`——30 选一并写进测试;**推荐直接 dump data** 对齐 lifecycle show)
|
|
40
|
+
- 非法 / 缺失 yaml → exit **1**(与 lifecycle 风格一致)
|
|
41
|
+
|
|
42
|
+
### D2 · 库
|
|
43
|
+
|
|
44
|
+
- 扩展 `lib/discipline-coverage.js`:`formatDisciplineShow(data)`(或等价)
|
|
45
|
+
- 复用既有 `loadDisciplineCoverage` / `validateDisciplineCoverage`;**不**改 schema 必填
|
|
46
|
+
|
|
47
|
+
### D3 · 文档 / 测试 / 版本
|
|
48
|
+
|
|
49
|
+
- help / USER_GUIDE / ONBOARDING 挂点;CHANGELOG **2.11.0**
|
|
50
|
+
- 单测:包内 yaml show 成功;损坏 fixture exit ≠0
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
## 3. 非范围
|
|
55
|
+
|
|
56
|
+
- `audit --discipline` 交互视图 / 过滤 UI / HTML
|
|
57
|
+
- 全量人工重盘周 / 改 statements 真值内容(除非 show 发现明显自相矛盾且 A 波已改——默认 **不**改矩阵正文)
|
|
58
|
+
- A 的守卫扩面(不得同未 CLOSE 的 A 抢 PR;本棒 30 在 A CLOSE 后)
|
|
59
|
+
- apply / G7 / N2-C
|
|
60
|
+
|
|
61
|
+
---
|
|
62
|
+
|
|
63
|
+
## 4. 验收清单
|
|
64
|
+
|
|
65
|
+
- [ ] `discipline show` 与 `discipline show --json` 对包内 yaml 成功
|
|
66
|
+
- [ ] 人读含 version / as_of / 计数;JSON 可被 `JSON.parse`
|
|
67
|
+
- [ ] help 列出 `discipline show`
|
|
68
|
+
- [ ] `npm test` 全绿 · lifecycle show 不回归
|
|
69
|
+
- [ ] 文档一句说明 SoT 仍为 yaml,show 只读
|
|
70
|
+
|
|
71
|
+
---
|
|
72
|
+
|
|
73
|
+
## 5. 思考轮控制表(摘要)
|
|
74
|
+
|
|
75
|
+
| 字段 | 值 |
|
|
76
|
+
| --- | --- |
|
|
77
|
+
| `actual_last_round` | R1 |
|
|
78
|
+
| `early_stop` | yes |
|
|
79
|
+
| `early_stop_reason` | 与 lifecycle show 同构;T3 已预留「下波 mirror」 |
|
|
80
|
+
| `residual_risks` | ① 人读表格过长需截断;② 与 audit 子命令命名空间未来冲突——本波用顶级 `discipline`,不做 audit 子命令 |
|
|
81
|
+
|
|
82
|
+
---
|
|
83
|
+
|
|
84
|
+
## 8. 下一棒 Prompt(人签后 · A CLOSE 后 30)
|
|
85
|
+
|
|
86
|
+
```text
|
|
87
|
+
【30】cyning-harness-discipline-show · 实现 discipline show [--json]
|
|
88
|
+
约束:只读;无 audit UI;A 已 CLOSE;目标进 2.11.0 CHANGELOG
|
|
89
|
+
```
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
# SPEC:lifecycle dry-run 守卫扩面(Post-2.10 Epic A)(v1)
|
|
2
|
+
|
|
3
|
+
> **状态**:`signed`(维护者签收 2026-07-25 · 对话「签收 A+E」)
|
|
4
|
+
> **track**:`feature`
|
|
5
|
+
> **上游**:Epic [`EPIC_post_210_menu_serial_a_e_j_v1_zh.md`](../../../docs/harness/guides/EPIC_post_210_menu_serial_a_e_j_v1_zh.md) · RETRO [`RETRO_post_210_next_menu_v1_zh.md`](../../../docs/harness/guides/RETRO_post_210_next_menu_v1_zh.md) 候选 **A**
|
|
6
|
+
> **前置**:`@cyning/harness@2.10.0`(dry-run 骨架)· Epic **J CLOSE ✅**
|
|
7
|
+
> **下游**:人签 → 00 draft task → 20-task-audit → HG-AUDIT-R1 → 30
|
|
8
|
+
> **目标版本**:Epic 统一 **`2.11.0`**(与 E 同窗)
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## Harness 元信息
|
|
13
|
+
|
|
14
|
+
| 字段 | 值 |
|
|
15
|
+
|------|-----|
|
|
16
|
+
| **spec_slug** | `lifecycle-guard-expand` |
|
|
17
|
+
| **invoke_slug** | `cyning-harness-lifecycle-guard-expand` |
|
|
18
|
+
| **test_strategy** | `required` |
|
|
19
|
+
| **test_strategy_note** | to_30 新 adapter 的 pass/fail/warn/unevaluated;既有 HG-AUDIT-R1 / reviews 回归;CLI dry-run exit;无 apply |
|
|
20
|
+
| **Open Folder(实现)** | `cyning-harness/` |
|
|
21
|
+
| **epic_serial** | A(J → **本棒** → E) |
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## 1. 背景与目标
|
|
26
|
+
|
|
27
|
+
T2(v2.10)已落地 `lifecycle dry-run`,但 `to_30` 仅接线 **`HG-AUDIT-R1`** 与 **`reviews_retention`**;`HG-TASK-DRAFT` · `audit_D5` · `task_lint` 及 `to_00` / `close_*` 仍为 **`unevaluated`**,报告易被误读。
|
|
28
|
+
|
|
29
|
+
**一句话目标**:在**仍无 `--apply` / 仍非 runner / 仍非 G7** 的前提下,把 **`to_30` 剩余守卫**(及可选 `to_00`)接到薄 adapter,降低 unevaluated 面。
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
## 2. 范围
|
|
34
|
+
|
|
35
|
+
### D1 · 必须接线(`--task` + `to_30`)
|
|
36
|
+
|
|
37
|
+
| guard id | 语义(对齐既有) | 说明 |
|
|
38
|
+
| --- | --- | --- |
|
|
39
|
+
| `HG-TASK-DRAFT` | human_gate 表 `approved` | 同 HG-AUDIT-R1 解析路径 |
|
|
40
|
+
| `audit_D5` | test_strategy vs 测试文件存在 | 复用 audit/verify 既有判定,禁止盲 spawn 全量 verify |
|
|
41
|
+
| `task_lint` | `lintTaskFile`;severity=**warn** | fail→报告 `warn` 或 `fail` 按 yaml severity;`--allow-lint-fail` 抑制为非 block(本波 lint 仍非 block) |
|
|
42
|
+
|
|
43
|
+
既有 `HG-AUDIT-R1` / `reviews_retention` **行为不得回归**。
|
|
44
|
+
|
|
45
|
+
### D2 · 可选同波(若成本低)
|
|
46
|
+
|
|
47
|
+
- `to_00` · `spec_reviews_retention`(复用 `findSpecReview` / verify `--spec` 语义)
|
|
48
|
+
- 若 30 评估成本高 → 记 residual,**不**阻塞 CLOSE
|
|
49
|
+
|
|
50
|
+
### D3 · 仍允许 unevaluated
|
|
51
|
+
|
|
52
|
+
- 全部 `close_*` 守卫(本波不扩 close 转移,除非顺手且单测充分)
|
|
53
|
+
- 无 `--task` 时:除结构外全部 unevaluated(语义同 T2)
|
|
54
|
+
|
|
55
|
+
### D4 · 文档 / 测试 / 版本
|
|
56
|
+
|
|
57
|
+
- CHANGELOG **2.11.0** 条目;USER_GUIDE / ONBOARDING 一句:扩面守卫列表
|
|
58
|
+
- 单测覆盖新 adapter 三态 + unevaluated 计数下降(相对 T2 fixture)
|
|
59
|
+
- **无** schema 必填扩张(除非缺字段阻塞)
|
|
60
|
+
|
|
61
|
+
---
|
|
62
|
+
|
|
63
|
+
## 3. 非范围
|
|
64
|
+
|
|
65
|
+
- `--apply` / 写回 task status / 事件溯源(候选 B)
|
|
66
|
+
- G7 / `harness run` / 完整 runner
|
|
67
|
+
- N2-C · lint severity 升 **block**(候选 D;J 已测 FAIL≈93%)
|
|
68
|
+
- 改变 `verify` / `close` 硬闸语义(dry-run 仍旁路)
|
|
69
|
+
- E · `discipline show`(下一棒)
|
|
70
|
+
|
|
71
|
+
---
|
|
72
|
+
|
|
73
|
+
## 4. 验收清单
|
|
74
|
+
|
|
75
|
+
- [ ] `to_30` + fixture:`HG-TASK-DRAFT` · `audit_D5` · `task_lint` 均出现非 `unevaluated` 的 `pass|fail|warn`
|
|
76
|
+
- [ ] 既有两 adapter 回归绿
|
|
77
|
+
- [ ] `--allow-lint-fail` 对 `task_lint` 行为可测
|
|
78
|
+
- [ ] 无 `--task`:unevaluated 语义不变
|
|
79
|
+
- [ ] `npm test` 全绿 · 文档列出已接线守卫
|
|
80
|
+
- [ ] **无** `--apply` 旗
|
|
81
|
+
|
|
82
|
+
---
|
|
83
|
+
|
|
84
|
+
## 5. 思考轮控制表(摘要)
|
|
85
|
+
|
|
86
|
+
| 字段 | 值 |
|
|
87
|
+
| --- | --- |
|
|
88
|
+
| `actual_last_round` | R2 |
|
|
89
|
+
| `early_stop` | yes |
|
|
90
|
+
| `early_stop_reason` | 扩面集合由 yaml to_30 未接线项冻结;与 T2 SPEC 方案 B 同构,争议面小 |
|
|
91
|
+
| `residual_risks` | ① D5 与 verify 路径细微漂移;② close_* 仍 unevaluated 须文档标明;③ 与 E 同窗发版叙述勿漏 |
|
|
92
|
+
|
|
93
|
+
---
|
|
94
|
+
|
|
95
|
+
## 8. 下一棒 Prompt(人签后)
|
|
96
|
+
|
|
97
|
+
```text
|
|
98
|
+
【00】draft task · slug cyning-harness-lifecycle-guard-expand · Open Folder cyning-harness/
|
|
99
|
+
约束:本 SPEC D1 必须接线;无 apply;无 N2-C;E 不得同 PR 抢做
|
|
100
|
+
```
|
|
@@ -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.11.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
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
#
|
|
3
3
|
# 登记真值:状态 / 转移 / 守卫。yaml 本身不是引擎。
|
|
4
4
|
# 由 `lifecycle dry-run` 引擎消费(v2.10+ · 资格判定旁路报告 · 不写盘)。
|
|
5
|
+
# v2.11+ to_30 已接线:HG-AUDIT-R1 · HG-TASK-DRAFT · reviews_retention · audit_D5 · task_lint
|
|
6
|
+
# (close_* / 可选 to_00 仍可 unevaluated)
|
|
5
7
|
# 只读展示:npx @cyning/harness lifecycle show [--json]
|
|
6
8
|
# Schema:schema/lifecycle.v1.schema.json
|
|
7
9
|
|
package/lib/audit.js
CHANGED
|
@@ -45,7 +45,11 @@ function runGateCheck(target, taskFile) {
|
|
|
45
45
|
};
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
-
|
|
48
|
+
/**
|
|
49
|
+
* D5 · test_strategy vs 测试/CI 制品(供 audit / lifecycle dry-run 复用)。
|
|
50
|
+
* @returns {{ ok: boolean, skipped?: boolean, reason: string }}
|
|
51
|
+
*/
|
|
52
|
+
export function runTestCheck(target, taskFile) {
|
|
49
53
|
// 未指定 task 文件时,D5 跳过(在 active task 上跑 gate-check 已覆盖 D3)
|
|
50
54
|
if (!taskFile) {
|
|
51
55
|
return { ok: true, skipped: true, reason: '未指定 --task,跳过 D5' };
|
package/lib/cli.js
CHANGED
|
@@ -43,6 +43,7 @@ function usage(version = 'unknown') {
|
|
|
43
43
|
npx @cyning/harness gate-check [--target PATH] [--task FILE] [--graph] [--json]
|
|
44
44
|
npx @cyning/harness verify [--target PATH] [--task FILE | --spec FILE] [--graph] [--json] [--agent-hint] [--workspace-root PATH]
|
|
45
45
|
npx @cyning/harness lifecycle show [--json]
|
|
46
|
+
npx @cyning/harness discipline show [--json]
|
|
46
47
|
npx @cyning/harness sync index [--target PATH]
|
|
47
48
|
npx @cyning/harness task check --file PATH [--no-circular] [--registry DIR]...
|
|
48
49
|
npx @cyning/harness task close --file PATH [--target PATH] [--yes] [--allow-unchecked]
|
|
@@ -63,7 +64,8 @@ function usage(version = 'unknown') {
|
|
|
63
64
|
audit ICVO 机械审计:复用 gate-check · D5 测试声明检查 · S5 git-clean 提醒
|
|
64
65
|
gate-check 调用 wizard/gate-check.sh(业务仓无需 clone 即可用)
|
|
65
66
|
verify 30 前聚合(--task)或 SPEC→00 审查文闸(--spec · v2.8+)
|
|
66
|
-
lifecycle 只读展示 harness/lifecycle.yaml(状态/转移/守卫 · v2.7+)
|
|
67
|
+
lifecycle 只读展示 harness/lifecycle.yaml(状态/转移/守卫 · v2.7+;dry-run v2.10+)
|
|
68
|
+
discipline 只读展示 harness/discipline-coverage.yaml(机械化率资产 · v2.11+)
|
|
67
69
|
sync index 生成 .cyning-harness/invoke_index.json
|
|
68
70
|
task check 校验 task.harness.v1.json sidecar · --no-circular 检测 depends_on 环
|
|
69
71
|
task close 受闸归档:5 项机械校验(invoke/自检结论/勾选/slug/状态)后 mv active→done(v2.2+)
|
|
@@ -134,6 +136,10 @@ export async function runCli(argv) {
|
|
|
134
136
|
await cmdLifecycle(rest);
|
|
135
137
|
return;
|
|
136
138
|
}
|
|
139
|
+
if (cmd === 'discipline') {
|
|
140
|
+
await cmdDiscipline(rest);
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
137
143
|
if (cmd === 'sync') {
|
|
138
144
|
await cmdSync(rest);
|
|
139
145
|
return;
|
|
@@ -599,6 +605,44 @@ dry-run · 转移资格判定(引擎消费 yaml · v2.10+ · 旁路报告 ·
|
|
|
599
605
|
throw err;
|
|
600
606
|
}
|
|
601
607
|
|
|
608
|
+
async function cmdDiscipline(args) {
|
|
609
|
+
const { loadDisciplineCoverage, formatDisciplineShow } = await import(
|
|
610
|
+
'./discipline-coverage.js'
|
|
611
|
+
);
|
|
612
|
+
const [sub, ...rest] = args;
|
|
613
|
+
if (!sub || sub === '--help' || sub === '-h' || args.includes('--help') || args.includes('-h')) {
|
|
614
|
+
console.log(`用法:
|
|
615
|
+
npx @cyning/harness discipline show [--json]
|
|
616
|
+
|
|
617
|
+
show · 只读展示 harness/discipline-coverage.yaml(机械化率资产 · v2.11+ · 非 audit UI)
|
|
618
|
+
`);
|
|
619
|
+
return;
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
if (sub === 'show') {
|
|
623
|
+
const json = rest.includes('--json');
|
|
624
|
+
const unknown = rest.filter((a) => a !== '--json');
|
|
625
|
+
if (unknown.length > 0) {
|
|
626
|
+
const err = new Error(`discipline show 未知参数: ${unknown.join(' ')}`);
|
|
627
|
+
err.exitCode = 1;
|
|
628
|
+
throw err;
|
|
629
|
+
}
|
|
630
|
+
const { data } = loadDisciplineCoverage();
|
|
631
|
+
if (json) {
|
|
632
|
+
console.log(JSON.stringify(data, null, 2));
|
|
633
|
+
} else {
|
|
634
|
+
console.log(formatDisciplineShow(data));
|
|
635
|
+
}
|
|
636
|
+
return;
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
const err = new Error(
|
|
640
|
+
`discipline 子命令未知: ${sub ?? '(空)'}\n用法: discipline show [--json]`,
|
|
641
|
+
);
|
|
642
|
+
err.exitCode = 1;
|
|
643
|
+
throw err;
|
|
644
|
+
}
|
|
645
|
+
|
|
602
646
|
async function cmdSync(args) {
|
|
603
647
|
if (args.includes('--help') || args.includes('-h')) {
|
|
604
648
|
console.log(`用法: npx @cyning/harness sync index [--target PATH]`);
|
|
@@ -94,3 +94,55 @@ export function loadDisciplineCoverage(options = {}) {
|
|
|
94
94
|
|
|
95
95
|
return { data, filePath, harnessRoot };
|
|
96
96
|
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* 人读摘要(镜像 lifecycle show · 只读)。
|
|
100
|
+
*/
|
|
101
|
+
export function formatDisciplineShow(data) {
|
|
102
|
+
const byStatus = {};
|
|
103
|
+
for (const s of data.statements ?? []) {
|
|
104
|
+
byStatus[s.status] = (byStatus[s.status] ?? 0) + 1;
|
|
105
|
+
}
|
|
106
|
+
const gapBy = {};
|
|
107
|
+
for (const g of data.gaps ?? []) {
|
|
108
|
+
gapBy[g.status] = (gapBy[g.status] ?? 0) + 1;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const lines = [
|
|
112
|
+
`discipline-coverage v${data.version}`,
|
|
113
|
+
`as_of: ${data.as_of_package_version}`,
|
|
114
|
+
`scope: ${data.scope}`,
|
|
115
|
+
'',
|
|
116
|
+
`## statements (${data.statements?.length ?? 0})`,
|
|
117
|
+
...Object.entries(byStatus)
|
|
118
|
+
.sort(([a], [b]) => a.localeCompare(b))
|
|
119
|
+
.map(([k, n]) => `- ${k}: ${n}`),
|
|
120
|
+
'',
|
|
121
|
+
`## gaps (${data.gaps?.length ?? 0})`,
|
|
122
|
+
];
|
|
123
|
+
if ((data.gaps?.length ?? 0) === 0) {
|
|
124
|
+
lines.push('- (无)');
|
|
125
|
+
} else {
|
|
126
|
+
lines.push(
|
|
127
|
+
...Object.entries(gapBy)
|
|
128
|
+
.sort(([a], [b]) => a.localeCompare(b))
|
|
129
|
+
.map(([k, n]) => `- ${k}: ${n}`),
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
lines.push('', '## sample statements(最多 12)');
|
|
134
|
+
for (const s of (data.statements ?? []).slice(0, 12)) {
|
|
135
|
+
const sum =
|
|
136
|
+
s.summary.length > 72 ? `${s.summary.slice(0, 69)}…` : s.summary;
|
|
137
|
+
lines.push(`- [${s.status}] ${s.id}: ${sum}`);
|
|
138
|
+
}
|
|
139
|
+
if ((data.statements?.length ?? 0) > 12) {
|
|
140
|
+
lines.push(`- … +${data.statements.length - 12} more`);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
lines.push(
|
|
144
|
+
'',
|
|
145
|
+
'注: SoT = harness/discipline-coverage.yaml · show 只读 · 非 audit --discipline UI',
|
|
146
|
+
);
|
|
147
|
+
return lines.join('\n');
|
|
148
|
+
}
|
package/lib/lifecycle.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import yaml from 'js-yaml';
|
|
4
|
+
import { runTestCheck } from './audit.js';
|
|
4
5
|
import { resolveHarnessRoot } from './paths.js';
|
|
5
6
|
import { findReview, parseHumanGates } from './task-meta.js';
|
|
7
|
+
import { lintTaskFile } from './task-lint.js';
|
|
6
8
|
|
|
7
9
|
const SEVERITIES = new Set(['block', 'warn']);
|
|
8
10
|
|
|
@@ -121,10 +123,13 @@ export function formatLifecycleShow(data) {
|
|
|
121
123
|
return lines.join('\n').trimEnd();
|
|
122
124
|
}
|
|
123
125
|
|
|
124
|
-
/**
|
|
126
|
+
/** 已接线的守卫(未登记者 → unevaluated;close_* 本波仍未接线) */
|
|
125
127
|
const GUARD_ADAPTERS = {
|
|
126
|
-
'HG-AUDIT-R1':
|
|
128
|
+
'HG-AUDIT-R1': evaluateHgGate('HG-AUDIT-R1'),
|
|
129
|
+
'HG-TASK-DRAFT': evaluateHgGate('HG-TASK-DRAFT'),
|
|
127
130
|
reviews_retention: evaluateReviewsRetention,
|
|
131
|
+
audit_D5: evaluateAuditD5,
|
|
132
|
+
task_lint: evaluateTaskLint,
|
|
128
133
|
};
|
|
129
134
|
|
|
130
135
|
function findRepoRootFromTask(absTask) {
|
|
@@ -144,19 +149,21 @@ function flagsAllow(flags, allowFlag) {
|
|
|
144
149
|
return false;
|
|
145
150
|
}
|
|
146
151
|
|
|
147
|
-
function
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
152
|
+
function evaluateHgGate(gateId) {
|
|
153
|
+
return ({ taskPath }) => {
|
|
154
|
+
const content = fs.readFileSync(taskPath, 'utf8');
|
|
155
|
+
const gates = parseHumanGates(content);
|
|
156
|
+
const row = gates.find((g) => g.id === gateId);
|
|
157
|
+
if (!row) {
|
|
158
|
+
return { status: 'fail', detail: `闸表无 ${gateId} 行` };
|
|
159
|
+
}
|
|
160
|
+
if (row.status === 'approved') {
|
|
161
|
+
return { status: 'pass', detail: `${gateId}=approved` };
|
|
162
|
+
}
|
|
163
|
+
return {
|
|
164
|
+
status: 'fail',
|
|
165
|
+
detail: `${gateId}=${row.status}(须 approved)`,
|
|
166
|
+
};
|
|
160
167
|
};
|
|
161
168
|
}
|
|
162
169
|
|
|
@@ -174,6 +181,32 @@ function evaluateReviewsRetention({ taskPath, target, flags }) {
|
|
|
174
181
|
return { status: 'fail', detail: 'missing R<n> review' };
|
|
175
182
|
}
|
|
176
183
|
|
|
184
|
+
function evaluateAuditD5({ taskPath, target }) {
|
|
185
|
+
const result = runTestCheck(target, taskPath);
|
|
186
|
+
if (result.ok) {
|
|
187
|
+
return {
|
|
188
|
+
status: 'pass',
|
|
189
|
+
detail: result.skipped
|
|
190
|
+
? `D5 skipped · ${result.reason}`
|
|
191
|
+
: result.reason,
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
return { status: 'fail', detail: result.reason };
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
function evaluateTaskLint({ taskPath, target }) {
|
|
198
|
+
try {
|
|
199
|
+
const result = lintTaskFile(taskPath, { cwd: target });
|
|
200
|
+
if (result.ok) {
|
|
201
|
+
return { status: 'pass', detail: 'task lint PASS' };
|
|
202
|
+
}
|
|
203
|
+
const rules = (result.errors ?? []).map((e) => e.rule).join(',') || 'errors';
|
|
204
|
+
return { status: 'fail', detail: `task lint FAIL · ${rules}` };
|
|
205
|
+
} catch (err) {
|
|
206
|
+
return { status: 'fail', detail: `task lint 异常: ${err.message}` };
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
177
210
|
/**
|
|
178
211
|
* 转移 dry-run(引擎最小骨架 · v2.10+)。
|
|
179
212
|
* 默认不写盘;无 --apply。
|