@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 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
 
@@ -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)(HGM · **v2.0+** 提案 · v1.0 后)。
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_*`)分层:本文件描述 **产品如何实现**,战略文档描述 **对外演进**。
@@ -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` audit) |
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
- | 机械化率资产 | 包内 `harness/discipline-coverage.yaml`(SoT · v2.10+ · audit UI) |
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
- | **HGM 过程轨** | `.cyning-harness/events/*.jsonl` | Task / Gate / Review 实例与事件史 | **v2.0+** |
287
+ | **PIP(过程实例投影)过程轨** | `.cyning-harness/events/*.jsonl` | Task / Gate / Review 实例与事件史 | **v2.0+** |
287
288
 
288
- **原则**:YAML 为 Inform 编辑源,HGM 为过程事件图;YAML **不替代** task/review 真值,HGM **不替代** YAML/MD 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
- | HGM / 图数据库 | **v2.0+ 已实现** `graph ingest|snapshot|axioms` · 本地 JSONL + snapshot;Neo4j / 远端同步 **仍提案** |
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 + HGM axioms 事件流精确匹配 |
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:HGM Inform-YAML 是什么关系?**
353
- A:Inform-YAML 是 **架构图谱** 的编辑源;HGM(v2.0+)是 **过程协作** 的事件图。二者并列,HGM 通过 `InformArtifact` 节点引用 Inform 产物,但不覆盖其正文。
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. HGM 过程轨(v2.0+)
361
+ ## 13. PIP(过程实例投影)过程轨(v2.0+)
361
362
 
362
- Harness Graph Model(HGM)把 task、gate、review、invoke、sync 等过程实例变成 **append-only 事件流** 与 **可重放图快照**,用于机械检查 SDD 公理。
363
+ PIP(过程实例投影;Process Instance Projection)把 task、gate、review、invoke、sync 等过程实例变成 **append-only 事件流** 与 **可重放图快照**,用于机械检查 SDD 公理。
363
364
 
364
365
  ### 13.1 事件文件
365
366
 
366
- HGM 事件写入 `.cyning-harness/events/YYYY-MM.jsonl`,修正 = 追加 `CorrectionEvent`,**禁止删改历史**。
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
- - HGM **不存储** Inform 正文,只存路径指针与 schema 版本
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 HGM 过程轨 · `graph ingest|snapshot|axioms` · InformArtifact 与 MUST_READ 边 |
435
+ | 2026-06-17 | v2.0.0:新增 §13 PIP(过程实例投影)过程轨 · `graph ingest|snapshot|axioms` · InformArtifact 与 MUST_READ 边 |
@@ -1,4 +1,6 @@
1
- # Harness Graph Model (HGM) · 设计草案 v0
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
- Harness Graph Model (HGM)
25
+ PIP(过程实例投影)
24
26
  = OOP 的结构化对象(本体 Class 的实例)
25
27
  + 显式带类型的边(ObjectProperty · HatRef · 基数)
26
28
  + 不可变事件历史(append-only · 可重放投影)
@@ -1,6 +1,8 @@
1
- # graph · Harness Graph Model (HGM)
1
+ # graph · PIP(过程实例投影;Process Instance Projection)
2
2
 
3
- > **Track G · G1 / v2.0+**:HGM 为 **v1.0 公理稳定后的可选增强**(proposal · 未实现)· 详见 [`../ROADMAP_v1_zh.md`](../ROADMAP_v1_zh.md) §2.0 · §2.2。
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
- HGM = 结构化对象 + 显式带类型的边 + 不可变事件历史 + 可推理的公理
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
- | **HGM** | Process 实例图 · Task / Gate / Review 时序 |
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.10.0"
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
 
@@ -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
- function runTestCheck(target, taskFile) {
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
- /** 本波已接线的守卫(其余 unevaluated */
126
+ /** 已接线的守卫(未登记者 unevaluated;close_* 本波仍未接线) */
125
127
  const GUARD_ADAPTERS = {
126
- 'HG-AUDIT-R1': evaluateHgAuditR1,
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 evaluateHgAuditR1({ taskPath }) {
148
- const content = fs.readFileSync(taskPath, 'utf8');
149
- const gates = parseHumanGates(content);
150
- const audit = gates.find((g) => g.id === 'HG-AUDIT-R1');
151
- if (!audit) {
152
- return { status: 'fail', detail: '闸表无 HG-AUDIT-R1 行' };
153
- }
154
- if (audit.status === 'approved') {
155
- return { status: 'pass', detail: 'HG-AUDIT-R1=approved' };
156
- }
157
- return {
158
- status: 'fail',
159
- detail: `HG-AUDIT-R1=${audit.status}(须 approved)`,
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cyning/harness",
3
- "version": "2.10.0",
3
+ "version": "2.11.1",
4
4
  "description": "cyning-harness discipline package · init / upgrade / check CLI",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -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 跳过
@@ -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
- # 始终刷新 local.json,避免路径漂移(如 Desktop Projects)
105
- printf '%s\n' "{\"cyning_harness_root\":\"$CYNING_HARNESS\"}" > "$TARGET/.cyning-harness/local.json"
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