@cyning/harness 2.10.0 → 2.11.1
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 +30 -1
- package/docs/ARCHITECTURE.md +1 -1
- package/docs/ONBOARDING.md +10 -0
- package/docs/USER_GUIDE_v1.0_zh.md +14 -13
- package/docs/methodology/graph/HARNESS_GRAPH_MODEL_design_v0_zh.md +4 -2
- package/docs/methodology/graph/README.md +6 -4
- 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/wizard/lib/common.sh +17 -0
- package/wizard/lib/manifest.sh +34 -0
- package/wizard/upgrade.sh +10 -10
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,35 @@
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
## [2.11.1] - 2026-07-25
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
|
|
11
|
+
- **F1 · `upgrade` 自绊 S5(ops-desk-web dogfood)**:`local.json` 改为 **sync apply 成功后** 写入(compare-before-write)。避免 npx 缓存路径变化时先脏工作区再被 git-clean 中止。
|
|
12
|
+
- **F2 · manifest 静默丢字段**:`write_manifest_upgrade` 重写前对 schema 外字段与 `ide` 裁剪打 **WARN** 清单(知情);**仍不**合并保留(`additionalProperties: false`)。
|
|
13
|
+
|
|
14
|
+
### Notes
|
|
15
|
+
|
|
16
|
+
- 摩擦报告:`Projects/docs/harness/guides/ANALYSIS_upgrade_wizard_friction_20260725_v1_zh.md`
|
|
17
|
+
- patch · 2.3+ manifest 仅五字段:`version` / `preset` / `ide` / `from_version` / `upgraded_at`(`name` / `tech_graph_dir` / `tasks_dir` / `hooks` 等已废弃)
|
|
18
|
+
|
|
19
|
+
## [2.11.0] - 2026-07-25
|
|
20
|
+
|
|
21
|
+
### Added
|
|
22
|
+
|
|
23
|
+
- **`discipline show [--json]`(Post-2.10 Epic E)**:只读展示 `harness/discipline-coverage.yaml`(镜像 `lifecycle show`)。**无** `audit --discipline` UI。
|
|
24
|
+
|
|
25
|
+
### Changed
|
|
26
|
+
|
|
27
|
+
- **`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。
|
|
28
|
+
|
|
29
|
+
### Notes
|
|
30
|
+
|
|
31
|
+
- SPEC:`docs/spec/SPEC-lifecycle-guard-expand_v1.md` · `docs/spec/SPEC-discipline-show_v1.md`
|
|
32
|
+
- minor · `lifecycle show` / verify 硬闸语义不变
|
|
33
|
+
- `discipline-coverage.yaml` `as_of_package_version` → **2.11.0**
|
|
34
|
+
- **已发布**:`@cyning/harness@2.11.0`(npm `latest` · 2026-07-25)· tag `v2.11.0` 已推送
|
|
35
|
+
|
|
7
36
|
## [2.10.0] - 2026-07-25
|
|
8
37
|
|
|
9
38
|
### Added
|
|
@@ -19,8 +48,8 @@
|
|
|
19
48
|
### Notes
|
|
20
49
|
|
|
21
50
|
- SPEC:`docs/spec/SPEC-lifecycle-engine-min_v1.md` · `docs/spec/SPEC-discipline-coverage-yaml_v1.md`
|
|
22
|
-
- Epic 统一发版窗 · 维护者 publish 前复检
|
|
23
51
|
- minor · `lifecycle show` 行为不变
|
|
52
|
+
- **已发布**:`@cyning/harness@2.10.0`(npm `latest` · 2026-07-25)· tag `v2.10.0` 已推送
|
|
24
53
|
|
|
25
54
|
## [2.9.0] - 2026-07-25
|
|
26
55
|
|
package/docs/ARCHITECTURE.md
CHANGED
|
@@ -68,5 +68,5 @@ CLI/Worker 与模板 **同一仓库** 演进,避免多仓割裂。
|
|
|
68
68
|
Track / Hat / Sync / SDD 公理与扩展规则见 [`methodology/product/DESIGN_ONTOLOGY_v1_zh.md`](./methodology/product/DESIGN_ONTOLOGY_v1_zh.md)(**v1.3**)。
|
|
69
69
|
**帽链流程(人读)**:[`methodology/product/SDD_HAT_FLOW_v2_zh.md`](./methodology/product/SDD_HAT_FLOW_v2_zh.md)
|
|
70
70
|
**方法论总指引**:[`methodology/README.md`](./methodology/README.md) · 关系图 [`methodology/DOCUMENT_MAP_v1_zh.md`](./methodology/DOCUMENT_MAP_v1_zh.md)
|
|
71
|
-
远期 **过程实例图 + 事件历史** 见 [`methodology/graph/HARNESS_GRAPH_MODEL_design_v0_zh.md`](./methodology/graph/HARNESS_GRAPH_MODEL_design_v0_zh.md)
|
|
71
|
+
远期 **过程实例图 + 事件历史** 见 [`methodology/graph/HARNESS_GRAPH_MODEL_design_v0_zh.md`](./methodology/graph/HARNESS_GRAPH_MODEL_design_v0_zh.md)(**PIP(过程实例投影)** · **v2.0+** · v1.0 后)。
|
|
72
72
|
与 **战略本体**(工作区 `docs/harness/guides/STRATEGY_ONTOLOGY_*`)分层:本文件描述 **产品如何实现**,战略文档描述 **对外演进**。
|
package/docs/ONBOARDING.md
CHANGED
|
@@ -26,6 +26,10 @@ npx @cyning/harness check # 仅检查是否有新版本
|
|
|
26
26
|
写入 `.cyning-harness/manifest.json`(钉版本 · preset · ide)与 `profile.json`。
|
|
27
27
|
Schema:[`schema/manifest.v1.schema.json`](../schema/manifest.v1.schema.json)
|
|
28
28
|
|
|
29
|
+
**manifest(2.3+)仅五字段**:`version` / `preset` / `ide` / `from_version` / `upgraded_at`(`additionalProperties: false`)。
|
|
30
|
+
旧字段 `name` / `harness_version` / `tech_graph_dir` / `tasks_dir` / `hooks` 已废弃且无消费方;`upgrade` 会整体重写并(v2.11.1+)对将被移除的字段打 WARN,**不会**合并保留。
|
|
31
|
+
`upgrade` 的 `local.json` 簿记在 sync apply **成功之后**写入(v2.11.1+),避免自绊 S5 git-clean。
|
|
32
|
+
|
|
29
33
|
**维护者 / 离线 · clone 路径:**
|
|
30
34
|
|
|
31
35
|
```bash
|
|
@@ -63,11 +67,16 @@ npx @cyning/harness verify --spec docs/spec/SPEC-xxx_v1.md \
|
|
|
63
67
|
npx @cyning/harness lifecycle show
|
|
64
68
|
npx @cyning/harness lifecycle show --json
|
|
65
69
|
# 转移资格 dry-run(v2.10+ · 旁路报告 · 不替代 verify)
|
|
70
|
+
# v2.11+ to_30 已接线:HG-* · reviews · audit_D5 · task_lint(close_* 仍可 unevaluated)
|
|
66
71
|
npx @cyning/harness lifecycle dry-run --transition to_30 --from draft \
|
|
67
72
|
--task docs/tasks/active/task_xxx.md
|
|
68
73
|
# 无 --task:仅结构 + 守卫清单 unevaluated
|
|
69
74
|
npx @cyning/harness lifecycle dry-run --transition to_30 --from draft
|
|
70
75
|
|
|
76
|
+
# 机械化率资产只读(v2.11+ · SoT=discipline-coverage.yaml)
|
|
77
|
+
npx @cyning/harness discipline show
|
|
78
|
+
npx @cyning/harness discipline show --json
|
|
79
|
+
|
|
71
80
|
# 仅人工闸
|
|
72
81
|
npx @cyning/harness gate-check --target /path/to/your-repo
|
|
73
82
|
npx @cyning/harness gate-check --graph --target /path/to/your-repo
|
|
@@ -90,6 +99,7 @@ npx @cyning/harness sync index --target /path/to/your-repo
|
|
|
90
99
|
| **lint** | task 结构(仅 `--task`) | v2.7+ E 级 → `WARN: task lint`(不改 exit / `may_start_30`) |
|
|
91
100
|
|
|
92
101
|
生命周期真值:[`harness/lifecycle.yaml`](../harness/lifecycle.yaml)(`lifecycle show` 只读 · `lifecycle dry-run` 资格判定 v2.10+)。
|
|
102
|
+
机械化率资产:[`harness/discipline-coverage.yaml`](../harness/discipline-coverage.yaml)(`discipline show` 只读 · v2.11+)。
|
|
93
103
|
|
|
94
104
|
机械化率覆盖资产(Starter):[`harness/discipline-coverage.yaml`](../harness/discipline-coverage.yaml)(SoT · 随版本改 statements/gaps;**无** audit UI)。
|
|
95
105
|
|
|
@@ -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
|
|
|
@@ -184,12 +185,12 @@ Schema:[`schema/verify_result.v1.schema.json`](../schema/verify_result.v1.sche
|
|
|
184
185
|
| 命令 | 用途 |
|
|
185
186
|
| --- | --- |
|
|
186
187
|
| `npx @cyning/harness init` | 首次安装模板与 manifest(可选 `--with-scripts`) |
|
|
187
|
-
| `npx @cyning/harness upgrade` | 同步产品包更新(可加 `--gate-check`
|
|
188
|
+
| `npx @cyning/harness upgrade` | 同步产品包更新(可加 `--gate-check`);v2.11.1+:`local.json` 在 apply 后写入;manifest 重写前 WARN 非标准字段(2.3+ 仅五字段) |
|
|
188
189
|
| `npx @cyning/harness check` | 检查是否有新版本 |
|
|
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` |
|
|
@@ -283,9 +284,9 @@ npm run harness -- check --target /tmp/foo
|
|
|
283
284
|
| --- | --- | --- | --- |
|
|
284
285
|
| **MD 人类轨** | `docs/_tech_graph/*.md` | 代码审阅、README 引用、Mermaid 渲染 | v1.0+ |
|
|
285
286
|
| **YAML 编辑源** | `docs/_tech_graph/*.graph.yaml` | 结构化编辑、diff、CI 校验 | **v1.1+** |
|
|
286
|
-
| **
|
|
287
|
+
| **PIP(过程实例投影)过程轨** | `.cyning-harness/events/*.jsonl` | Task / Gate / Review 实例与事件史 | **v2.0+** |
|
|
287
288
|
|
|
288
|
-
**原则**:YAML 为 Inform 编辑源,
|
|
289
|
+
**原则**:YAML 为 Inform 编辑源,PIP(过程实例投影)为过程事件图;YAML **不替代** task/review 真值,PIP(过程实例投影)**不替代** YAML/MD Inform 正文。
|
|
289
290
|
|
|
290
291
|
### 10.2 最小工作流
|
|
291
292
|
|
|
@@ -326,9 +327,9 @@ npx @cyning/harness graph yaml check --all --input docs/_tech_graph
|
|
|
326
327
|
| bench `100` | [SDD-Compliance](../examples/compliance_bench/README.md) 五场景合规率 · 见上文 §6.1 |
|
|
327
328
|
| Extended 帽 | 00/50/链式 PROMPT 不在 Starter 默认包 · 见 [`harness/prompts/README.md`](../harness/prompts/README.md) |
|
|
328
329
|
| Inform-YAML | **v1.1+** · 可选编辑源 · 须 `graph yaml check` 与 `graph.json` 一致 |
|
|
329
|
-
|
|
|
330
|
+
| PIP(过程实例投影)/ 图数据库 | **v2.0+ 已实现** `graph ingest|snapshot|axioms` · 本地 JSONL + snapshot;Neo4j / 远端同步 **仍提案** |
|
|
330
331
|
| Agent-shell | 研究轨 #9,非 npm 功能 |
|
|
331
|
-
| rejected→draft | **v2.0.1+** bench S5 +
|
|
332
|
+
| rejected→draft | **v2.0.1+** bench S5 + PIP(过程实例投影)axioms 事件流精确匹配 |
|
|
332
333
|
|
|
333
334
|
---
|
|
334
335
|
|
|
@@ -349,21 +350,21 @@ A:不会(S2 域)。若 prompts 模板有更新,apply 会更新 **模板
|
|
|
349
350
|
**Q:如何编辑 Inform 架构图?**
|
|
350
351
|
A:v1.1+ 可选 `docs/_tech_graph/*.graph.yaml` 作为编辑源,运行 `npx @cyning/harness graph yaml compile|check` 生成 MD / 校验 graph.json。
|
|
351
352
|
|
|
352
|
-
**Q:
|
|
353
|
-
A:Inform-YAML 是 **架构图谱** 的编辑源;
|
|
353
|
+
**Q:PIP(过程实例投影)与 Inform-YAML 是什么关系?**
|
|
354
|
+
A:Inform-YAML 是 **架构图谱** 的编辑源;PIP(过程实例投影,v2.0+)是 **过程协作** 的事件图。二者并列,PIP(过程实例投影)通过 `InformArtifact` 节点引用 Inform 产物,但不覆盖其正文。
|
|
354
355
|
|
|
355
356
|
**Q:如何贡献或报 issue?**
|
|
356
357
|
A:GitHub [Cyning12/cyning-harness](https://github.com/Cyning12/cyning-harness) · MIT。
|
|
357
358
|
|
|
358
359
|
---
|
|
359
360
|
|
|
360
|
-
## 13.
|
|
361
|
+
## 13. PIP(过程实例投影)过程轨(v2.0+)
|
|
361
362
|
|
|
362
|
-
|
|
363
|
+
PIP(过程实例投影;Process Instance Projection)把 task、gate、review、invoke、sync 等过程实例变成 **append-only 事件流** 与 **可重放图快照**,用于机械检查 SDD 公理。
|
|
363
364
|
|
|
364
365
|
### 13.1 事件文件
|
|
365
366
|
|
|
366
|
-
|
|
367
|
+
PIP(过程实例投影) 事件写入 `.cyning-harness/events/YYYY-MM.jsonl`,修正 = 追加 `CorrectionEvent`,**禁止删改历史**。
|
|
367
368
|
|
|
368
369
|
### 13.2 CLI
|
|
369
370
|
|
|
@@ -396,7 +397,7 @@ chmod +x .git/hooks/pre-commit
|
|
|
396
397
|
|
|
397
398
|
- `InformArtifact` 节点 ID:`inform:{repo_rel_path}`
|
|
398
399
|
- Task → InformArtifact 边:`MUST_READ`
|
|
399
|
-
-
|
|
400
|
+
- PIP(过程实例投影)**不存储** Inform 正文,只存路径指针与 schema 版本
|
|
400
401
|
|
|
401
402
|
### 13.5 局限
|
|
402
403
|
|
|
@@ -431,4 +432,4 @@ chmod +x .git/hooks/pre-commit
|
|
|
431
432
|
| 2026-06-16 | v1.0.1:verify / gate-check / sync index CLI · `--with-scripts` · QUICKREF |
|
|
432
433
|
| 2026-06-17 | v1.1.0:新增 §10 Inform-YAML · `graph yaml compile|check` · 三轨边界说明 |
|
|
433
434
|
| 2026-06-17 | v2.0.1:D4-a axioms · rejected→draft 精确化 · S5 bench · optional pre-commit hook |
|
|
434
|
-
| 2026-06-17 | v2.0.0:新增 §13
|
|
435
|
+
| 2026-06-17 | v2.0.0:新增 §13 PIP(过程实例投影)过程轨 · `graph ingest|snapshot|axioms` · InformArtifact 与 MUST_READ 边 |
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
#
|
|
1
|
+
# PIP(过程实例投影;Process Instance Projection)· 设计草案 v0
|
|
2
|
+
|
|
3
|
+
> 文件名历史:`HARNESS_GRAPH_MODEL_*`(路径兼容)。
|
|
2
4
|
|
|
3
5
|
|
|
4
6
|
| 项 | 内容 |
|
|
@@ -20,7 +22,7 @@
|
|
|
20
22
|
### 0.1 一句话
|
|
21
23
|
|
|
22
24
|
```text
|
|
23
|
-
|
|
25
|
+
PIP(过程实例投影)
|
|
24
26
|
= OOP 的结构化对象(本体 Class 的实例)
|
|
25
27
|
+ 显式带类型的边(ObjectProperty · HatRef · 基数)
|
|
26
28
|
+ 不可变事件历史(append-only · 可重放投影)
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
# graph ·
|
|
1
|
+
# graph · PIP(过程实例投影;Process Instance Projection)
|
|
2
2
|
|
|
3
|
-
>
|
|
3
|
+
> **对外名**:PIP(过程实例投影)。对内历史文件名可含 `HGM` / `hgm_*`(路径兼容,正文不用曾称括注)。
|
|
4
|
+
|
|
5
|
+
> **Track G · G1 / v2.0+**:PIP(过程实例投影)为 **v1.0 公理稳定后的可选增强**(proposal · 未实现)· 详见 [`../ROADMAP_v1_zh.md`](../ROADMAP_v1_zh.md) §2.0 · §2.2。
|
|
4
6
|
|
|
5
7
|
| 文档 | 状态 | 说明 |
|
|
6
8
|
| --- | --- | --- |
|
|
@@ -10,7 +12,7 @@
|
|
|
10
12
|
| [`HARNESS_GRAPH_MODEL_dialogue_archive_v1_zh.md`](./HARNESS_GRAPH_MODEL_dialogue_archive_v1_zh.md) | archive | 对话参考 · **非真值** |
|
|
11
13
|
|
|
12
14
|
```text
|
|
13
|
-
|
|
15
|
+
PIP(过程实例投影) = 结构化对象 + 显式带类型的边 + 不可变事件历史 + 可推理的公理
|
|
14
16
|
```
|
|
15
17
|
|
|
16
18
|
**勿混淆**:
|
|
@@ -19,6 +21,6 @@ HGM = 结构化对象 + 显式带类型的边 + 不可变事件历史 + 可推
|
|
|
19
21
|
| --- | --- |
|
|
20
22
|
| **GraphTrack** | `docs/_tech_graph/` · Inform 架构地图 |
|
|
21
23
|
| **gate-check --graph** | Inform 模块依赖图(Q3 · v1.0) |
|
|
22
|
-
| **
|
|
24
|
+
| **PIP(过程实例投影)** | Process 实例图 · Task / Gate / Review 时序 |
|
|
23
25
|
|
|
24
26
|
**依赖**:[`../product/DESIGN_ONTOLOGY_v1_zh.md`](../product/DESIGN_ONTOLOGY_v1_zh.md) v1.2
|
|
@@ -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。
|
package/package.json
CHANGED
package/wizard/lib/common.sh
CHANGED
|
@@ -198,6 +198,23 @@ profile_ide_cursor_rel() {
|
|
|
198
198
|
echo "$rel"
|
|
199
199
|
}
|
|
200
200
|
|
|
201
|
+
# 写入 .cyning-harness/local.json(compare-before-write · 内容不变则跳过)
|
|
202
|
+
# 供 upgrade post-apply 簿记;避免在 sync apply 前改已跟踪文件触发 S5
|
|
203
|
+
write_local_json_if_changed() {
|
|
204
|
+
local target="$1" root="$2"
|
|
205
|
+
local dir="$target/.cyning-harness"
|
|
206
|
+
local path="$dir/local.json"
|
|
207
|
+
local payload
|
|
208
|
+
payload="$(printf '%s\n' "{\"cyning_harness_root\":\"$root\"}")"
|
|
209
|
+
mkdir -p "$dir"
|
|
210
|
+
if [[ -f "$path" ]] && [[ "$(cat "$path")" == "$payload" ]]; then
|
|
211
|
+
echo "local.json 未变(跳过写入)→ cyning_harness_root=$root"
|
|
212
|
+
return 0
|
|
213
|
+
fi
|
|
214
|
+
printf '%s\n' "$payload" > "$path"
|
|
215
|
+
echo "已更新 local.json → cyning_harness_root=$root"
|
|
216
|
+
}
|
|
217
|
+
|
|
201
218
|
# 从 CLAUDE.md / AGENTS.md 剥离 Harness marker 块;若仅剩空文件则删除
|
|
202
219
|
# S5 · sync/apply 前检测 Git 工作区干净(v0.3+)
|
|
203
220
|
# 非 git 仓跳过;--force 或 HARNESS_SYNC_FORCE=1 跳过
|
package/wizard/lib/manifest.sh
CHANGED
|
@@ -77,6 +77,38 @@ EOF
|
|
|
77
77
|
echo "已写入 manifest: $mf"
|
|
78
78
|
}
|
|
79
79
|
|
|
80
|
+
# upgrade 重写前:对 schema 外字段 / ide 裁剪打 warning(知情而非静默)
|
|
81
|
+
# 标准五字段:version / preset / ide / from_version / upgraded_at(additionalProperties: false)
|
|
82
|
+
warn_manifest_upgrade_drops() {
|
|
83
|
+
local mf="$1" new_ide_json="$2"
|
|
84
|
+
[[ -f "$mf" ]] || return 0
|
|
85
|
+
if ! command -v node >/dev/null 2>&1; then
|
|
86
|
+
echo "WARN: 未找到 node,跳过 manifest 字段变更预检" >&2
|
|
87
|
+
return 0
|
|
88
|
+
fi
|
|
89
|
+
node -e '
|
|
90
|
+
const fs = require("fs");
|
|
91
|
+
const mf = process.argv[1];
|
|
92
|
+
const newIde = JSON.parse(process.argv[2]);
|
|
93
|
+
const schema = new Set(["version", "preset", "ide", "from_version", "upgraded_at"]);
|
|
94
|
+
let old;
|
|
95
|
+
try { old = JSON.parse(fs.readFileSync(mf, "utf8")); } catch { process.exit(0); }
|
|
96
|
+
const extra = Object.keys(old).filter((k) => !schema.has(k));
|
|
97
|
+
if (extra.length) {
|
|
98
|
+
console.log(
|
|
99
|
+
"WARN: manifest 以下非标准字段将被移除(2.3+ 仅五字段 · 见 ONBOARDING): " +
|
|
100
|
+
extra.join(", "),
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
const oldIde = old.ide;
|
|
104
|
+
const oldS = JSON.stringify(oldIde ?? null);
|
|
105
|
+
const newS = JSON.stringify(newIde);
|
|
106
|
+
if (oldS !== newS) {
|
|
107
|
+
console.log("WARN: manifest.ide 将从 profile 重算: " + oldS + " → " + newS);
|
|
108
|
+
}
|
|
109
|
+
' "$mf" "$new_ide_json"
|
|
110
|
+
}
|
|
111
|
+
|
|
80
112
|
# upgrade apply 后更新 manifest
|
|
81
113
|
write_manifest_upgrade() {
|
|
82
114
|
local target="$1" version="$2"
|
|
@@ -96,6 +128,8 @@ write_manifest_upgrade() {
|
|
|
96
128
|
preset="$(grep '"preset"' "$mf" | head -1 | sed -E 's/.*:[[:space:]]*"([^"]+)".*/\1/' || echo "$preset")"
|
|
97
129
|
fi
|
|
98
130
|
|
|
131
|
+
warn_manifest_upgrade_drops "$mf" "$ide_json"
|
|
132
|
+
|
|
99
133
|
mkdir -p "$target/.cyning-harness"
|
|
100
134
|
cat > "$mf" <<EOF
|
|
101
135
|
{
|
package/wizard/upgrade.sh
CHANGED
|
@@ -101,10 +101,8 @@ echo "preset: $(profile_preset_name "$PROFILE_FILE")"
|
|
|
101
101
|
echo "当前 IDE: $(profile_ide_summary "$PROFILE_FILE")"
|
|
102
102
|
echo ""
|
|
103
103
|
|
|
104
|
-
#
|
|
105
|
-
|
|
106
|
-
echo "已更新 local.json → cyning_harness_root=$CYNING_HARNESS"
|
|
107
|
-
echo ""
|
|
104
|
+
# 注意:local.json / manifest 簿记放在 sync apply **成功之后**(F1)。
|
|
105
|
+
# 勿在 apply 前改已跟踪的 .cyning-harness/local.json,否则 S5 git-clean 自绊。
|
|
108
106
|
|
|
109
107
|
if [[ -z "$IDE_LIST" ]] && [[ "$ASSUME_YES" -eq 0 ]]; then
|
|
110
108
|
if prompt_yes_no "是否更新 IDE 勾选(cursor / claude / agents)?" "n"; then
|
|
@@ -140,14 +138,16 @@ if [[ "$DO_APPLY" -eq 1 ]]; then
|
|
|
140
138
|
SYNC_ARGS=(apply --target "$TARGET")
|
|
141
139
|
[[ "$SYNC_FORCE" -eq 1 ]] && SYNC_ARGS+=(--force)
|
|
142
140
|
"$SCRIPT_DIR/harness-sync.sh" "${SYNC_ARGS[@]}"
|
|
141
|
+
# post-sync 簿记:apply 失败则不落(set -e 已中止)
|
|
142
|
+
write_local_json_if_changed "$TARGET" "$CYNING_HARNESS"
|
|
143
143
|
write_manifest_upgrade "$TARGET" "$VERSION"
|
|
144
144
|
cp "$CYNING_HARNESS/harness/templates/QUICKREF_v1_zh.md" "$TARGET/.cyning-harness/QUICKREF.md"
|
|
145
|
-
echo ""
|
|
146
|
-
echo "升级完成。"
|
|
147
|
-
echo "常用命令:"
|
|
148
|
-
echo " npx @cyning/harness verify --target $TARGET"
|
|
149
|
-
echo " npx @cyning/harness gate-check --target $TARGET"
|
|
150
|
-
echo " npx @cyning/harness upgrade --target $TARGET --yes"
|
|
145
|
+
echo ""
|
|
146
|
+
echo "升级完成。"
|
|
147
|
+
echo "常用命令:"
|
|
148
|
+
echo " npx @cyning/harness verify --target $TARGET"
|
|
149
|
+
echo " npx @cyning/harness gate-check --target $TARGET"
|
|
150
|
+
echo " npx @cyning/harness upgrade --target $TARGET --yes"
|
|
151
151
|
else
|
|
152
152
|
echo "已取消 apply(仅预览)。"
|
|
153
153
|
exit 0
|