@cyning/harness 2.0.0 → 2.0.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,25 @@
4
4
 
5
5
  ## [Unreleased]
6
6
 
7
+ ## [2.0.1] - 2026-06-17
8
+
9
+ ### Added
10
+
11
+ - **D4-a 公理**:`checkAxioms()` 检测 in_progress + `HG-GRAPH-MODULES` pending/rejected · MUST_READ 缺 Inform 可选 warn
12
+ - **compliance-bench S5**:`S5_rejected_draft/` · rejected→draft 静态夹具 · `--all` 含 S5(5/5 = 100%)
13
+ - **optional pre-commit**:`.cyning-harness/hooks/pre-commit.sample` · USER_GUIDE §13.3 · 默认 init 不安装
14
+
15
+ ### Changed
16
+
17
+ - **rejected→draft**:事件流精确匹配 `TaskStatusChanged(draft)` · 有回退则 pass · 缺则 error
18
+ - **版本**:`package.json` → **2.0.1**
19
+ - **USER_GUIDE**:§13 公理集 · optional Git hooks
20
+
21
+ ### Notes
22
+
23
+ - **HG-RELEASE**:待维护者 publish `@cyning/harness@2.0.1`
24
+ - bench S5 语义向 HGM axioms 靠拢 · drift 时改夹具不改 axioms
25
+
7
26
  ## [2.0.0] - 2026-06-17
8
27
 
9
28
  ### Added
@@ -23,7 +42,7 @@
23
42
 
24
43
  ### Notes
25
44
 
26
- - **HG-RELEASE**(tag v2.0.0 · npm publish)仍留维护者人闸
45
+ - **HG-RELEASE**:tag v2.0.0 · npm `@cyning/harness@2.0.0` 已发布
27
46
  - `gate-check --graph` 语义不变;HGM 独立子命令
28
47
  - HGM v2.0 默认本地 JSONL + snapshot,不含 Neo4j / SQLite / 远端同步
29
48
 
@@ -276,12 +276,12 @@ npx @cyning/harness graph yaml check --all --input docs/_tech_graph
276
276
  | 项 | 说明 |
277
277
  | --- | --- |
278
278
  | 不是胜率工具 | [`README` 试点证据 B2](../README.md) · 完整表 [`PILOT_EVIDENCE_B2_v1_zh.md`](./methodology/execution/PILOT_EVIDENCE_B2_v1_zh.md) · **小样本机制证据**,不可外推 |
279
- | bench `100` | [SDD-Compliance](../examples/compliance_bench/README.md) 四场景合规率 · 见上文 §6.1 |
279
+ | bench `100` | [SDD-Compliance](../examples/compliance_bench/README.md) 五场景合规率 · 见上文 §6.1 |
280
280
  | Extended 帽 | 00/50/链式 PROMPT 不在 Starter 默认包 · 见 [`harness/prompts/README.md`](../harness/prompts/README.md) |
281
281
  | Inform-YAML | **v1.1+** · 可选编辑源 · 须 `graph yaml check` 与 `graph.json` 一致 |
282
282
  | HGM / 图数据库 | **v2.0+ 已实现** `graph ingest|snapshot|axioms` · 本地 JSONL + snapshot;Neo4j / 远端同步 **仍提案** |
283
283
  | Agent-shell | 研究轨 #9,非 npm 功能 |
284
- | rejected→draft | bench S5 场景 v1 未纳入;gate-check 对非 approved 拒 30 |
284
+ | rejected→draft | **v2.0.1+** bench S5 + HGM axioms 事件流精确匹配 |
285
285
 
286
286
  ---
287
287
 
@@ -327,18 +327,31 @@ npx @cyning/harness graph ingest --target /path/to/your-repo
327
327
  # 事件重放 → .cyning-harness/graph/snapshot.json
328
328
  npx @cyning/harness graph snapshot --target /path/to/your-repo
329
329
 
330
- # 公理检查(D2 · D3 · S2 · rejected→draft)
330
+ # 公理检查(D2 · D3 · D4-a · S2 · rejected→draft)
331
331
  npx @cyning/harness graph axioms check --target /path/to/your-repo
332
332
  npx @cyning/harness graph axioms check --target /path/to/your-repo --json
333
333
  ```
334
334
 
335
- ### 13.3 Inform-YAML 的接口
335
+ **公理最小集(v2.0.1)**:D2 · D3 · **D4-a**(`HG-GRAPH-MODULES` pending/rejected + in_progress)· rejected→draft(事件流精确匹配)· S2。
336
+
337
+ ### 13.3 可选 · Git hooks
338
+
339
+ `init` **默认不安装** pre-commit。维护者可手动复制:
340
+
341
+ ```bash
342
+ cp .cyning-harness/hooks/pre-commit.sample .git/hooks/pre-commit
343
+ chmod +x .git/hooks/pre-commit
344
+ ```
345
+
346
+ 示例 hook 调用 `graph ingest --target`(幂等增量)· 失败默认 **warn** 不阻断 commit(可改 `exit 1`)。
347
+
348
+ ### 13.4 与 Inform-YAML 的接口
336
349
 
337
350
  - `InformArtifact` 节点 ID:`inform:{repo_rel_path}`
338
351
  - Task → InformArtifact 边:`MUST_READ`
339
352
  - HGM **不存储** Inform 正文,只存路径指针与 schema 版本
340
353
 
341
- ### 13.4 局限
354
+ ### 13.5 局限
342
355
 
343
356
  - v2.0 默认本地 JSONL + snapshot;**不含** Neo4j / SQLite / 远端同步
344
357
  - 多仓聚合、时光机重建任意时点图:v2.1+ 提案
@@ -370,4 +383,5 @@ npx @cyning/harness graph axioms check --target /path/to/your-repo --json
370
383
  | 2026-06-16 | 补全相对链接 · §6.1 compliance-bench · §12 阅读索引 |
371
384
  | 2026-06-16 | v1.0.1:verify / gate-check / sync index CLI · `--with-scripts` · QUICKREF |
372
385
  | 2026-06-17 | v1.1.0:新增 §10 Inform-YAML · `graph yaml compile|check` · 三轨边界说明 |
386
+ | 2026-06-17 | v2.0.1:D4-a axioms · rejected→draft 精确化 · S5 bench · optional pre-commit hook |
373
387
  | 2026-06-17 | v2.0.0:新增 §13 HGM 过程轨 · `graph ingest|snapshot|axioms` · InformArtifact 与 MUST_READ 边 |
@@ -58,7 +58,7 @@
58
58
 
59
59
  | 能力标签 | 代号 | 须交付(实现) | 文档 |
60
60
  | --- | --- | --- | --- |
61
- | **G1 · v2.0+** | HGM 起步 | `events/*.jsonl` · graph ingest · snapshot · axioms check 子集(**均未实现**) | HGM design v0.1→v0.2 |
61
+ | **G1 · v2.0+** | HGM 起步 | `events/*.jsonl` · graph ingest · snapshot · axioms check 子集(**v2.0.0 已实现**) | HGM design v0.1→v0.2 · [`task_g1`](../../../../docs/harness/tasks/done/cyning-harness/task_cyning_harness_g1_hgm_v2_v1.md) |
62
62
  | **G2 · v2.1+** | HGM 查询 | timeline · patterns · SQLite 投影 | 可选 Neo4j 导出 |
63
63
 
64
64
  **刻意不做(至 v1.0)**:GrowingReasoningAgent · OWL 推理机 · 热插拔 Track(Q5)· Neo4j 默认后端 · **Track G ingest(v1.0 前)**。
@@ -118,8 +118,8 @@ Epic 与子 task 真值:`Projects/docs/harness/tasks/active/`
118
118
  | #7 A4 v1.0 | v1.0.0 · 引用 #8 |
119
119
  | **#8 B2 + bench** | Track B · **1+3 主证据** · P0 绿后优先 |
120
120
  | **#9 Agent-shell** | Track B · **后置** · HGM+agent 后 | deferred · 见 §8 |
121
- | **I-YAML** | Track A · v1.1+ Inform | [`task_y1`](../../../../docs/harness/tasks/active/task_cyning_harness_y1_yaml_inform_v19_v1.md) · YAML P0 后 |
122
- | **Track G G1** | HGM v2.0 | [`task_g1`](../../../../docs/harness/tasks/active/task_cyning_harness_g1_hgm_v2_v1.md) · I-YAML + G0 后 |
121
+ | **I-YAML** | Track A · v1.1+ Inform | [`task_y1`](../../../../docs/harness/tasks/done/cyning-harness/task_cyning_harness_y1_yaml_inform_v19_v1.md) · YAML P0 后 |
122
+ | **Track G G1** | HGM v2.0 | [`task_g1`](../../../../docs/harness/tasks/done/cyning-harness/task_cyning_harness_g1_hgm_v2_v1.md) · I-YAML + G0 后 |
123
123
 
124
124
  **证据策略(2026-06-15 锁定)**:
125
125
 
@@ -9,7 +9,7 @@
9
9
  | **设计真值(已有)** | [`HARNESS_GRAPH_MODEL_design_v0_zh.md`](./HARNESS_GRAPH_MODEL_design_v0_zh.md) |
10
10
  | **语义真值** | [`../product/DESIGN_ONTOLOGY_v1_zh.md`](../product/DESIGN_ONTOLOGY_v1_zh.md) v1.2 |
11
11
  | **L2 路线** | [`../ROADMAP_v1_zh.md`](../ROADMAP_v1_zh.md) §2.2 · §8 |
12
- | **编排 task** | 工作区 [`task_cyning_harness_g1_hgm_v2_v1.md`](../../../../docs/harness/tasks/active/task_cyning_harness_g1_hgm_v2_v1.md) |
12
+ | **编排 task** | 工作区 [`task_cyning_harness_g1_hgm_v2_v1.md`](../../../../docs/harness/tasks/done/cyning-harness/task_cyning_harness_g1_hgm_v2_v1.md) |
13
13
  | **G1 维护者决策** | [`DECISIONS_hgm_g1_maintainer_v1_zh.md`](./DECISIONS_hgm_g1_maintainer_v1_zh.md) |
14
14
  | **Post-G1 序列表** | [`POST_G1_SEQUENCE_v1_zh.md`](../../../../docs/harness/guides/POST_G1_SEQUENCE_v1_zh.md) |
15
15
 
@@ -227,8 +227,8 @@ SEM-02 维持:**主轨 v1.x · HGM 从 v2.0 主版本起** · 不写「v0.5 =
227
227
  | --- | --- |
228
228
  | Post-v1.0 序列表 | [`POST_V1_0_SEQUENCE_v1_zh.md`](../../../../docs/harness/guides/POST_V1_0_SEQUENCE_v1_zh.md) |
229
229
  | 后端 YAML P0 | [`task_engineering_graph_yaml_p0_00_main_v1.md`](../../../../ai-ink-brain-api-python/docs/tasks/active/task_engineering_graph_yaml_p0_00_main_v1.md) |
230
- | 产品 I-YAML | [`task_cyning_harness_y1_yaml_inform_v19_v1.md`](../../../../docs/harness/tasks/active/task_cyning_harness_y1_yaml_inform_v19_v1.md) |
231
- | HGM G1 task | [`task_cyning_harness_g1_hgm_v2_v1.md`](../../../../docs/harness/tasks/active/task_cyning_harness_g1_hgm_v2_v1.md) |
230
+ | 产品 I-YAML | [`task_cyning_harness_y1_yaml_inform_v19_v1.md`](../../../../docs/harness/tasks/done/cyning-harness/task_cyning_harness_y1_yaml_inform_v19_v1.md) |
231
+ | HGM G1 task | [`task_cyning_harness_g1_hgm_v2_v1.md`](../../../../docs/harness/tasks/done/cyning-harness/task_cyning_harness_g1_hgm_v2_v1.md) |
232
232
  | B9(后置) | [`task_cyning_harness_b9_agent_shell_v1.md`](../../../../docs/harness/tasks/active/task_cyning_harness_b9_agent_shell_v1.md) |
233
233
  | A5 v1.0.1 | [`task_cyning_harness_a5_cli_verify_v101_v1.md`](../../../../docs/harness/tasks/active/task_cyning_harness_a5_cli_verify_v101_v1.md) |
234
234
 
@@ -239,9 +239,11 @@ SEM-02 维持:**主轨 v1.x · HGM 从 v2.0 主版本起** · 不写「v0.5 =
239
239
  | 节 | 状态 | 签收人 | 日期 |
240
240
  | --- | --- | --- | --- |
241
241
  | §0 决策摘要 | `confirmed` | (维护者 2026-06-16 口头) | |
242
- | §3 G1 范围 | `pending` | | |
243
- | §5 事件 schema | `pending` | | |
244
- | §6 I-YAML 接口 | `pending` | | |
242
+ | §3 G1 范围 | `confirmed` | 维护者 | 2026-06-17 |
243
+ | §5 事件 schema | `confirmed` | 维护者 | 2026-06-17 |
244
+ | §6 I-YAML 接口 | `confirmed` | 维护者 | 2026-06-17 |
245
+ | §8 agent/B9 | `confirmed` | 维护者 | 2026-06-17 |
246
+ | **G0 整体** | `signed` | 维护者 | 2026-06-17 |
245
247
  | §8 agent/B9 | `pending` | | |
246
248
  | **G0 整体** | `pending` | 全表勾选后开 G1 30 帽 | |
247
249
 
@@ -1,8 +1,8 @@
1
1
  # SDD-Compliance micro-bench · v1
2
2
 
3
3
  > **标题**:SDD-Compliance · **非** Agent Score / pass@1 类模型 benchmark。
4
- > **范围**:S1–S4 · 公理 D2/D3/S2 可机械检查部分 · 不覆盖 D4/D5 与 Terminal-Bench。
5
- > **状态**:v1.0 · B8 冻结 · 4/4 期望合规率 **100%**。
4
+ > **范围**:S1–S5 · 公理 D2/D3/S2/rejected→draft 可机械检查部分 · 不覆盖 D5 与 Terminal-Bench。
5
+ > **状态**:v1.1 · G1.1 · 5/5 期望合规率 **100%**。
6
6
 
7
7
  ---
8
8
 
@@ -12,7 +12,7 @@
12
12
  | --- | --- |
13
13
  | **测什么** | `gate-check.sh` 与 `harness-sync.sh` 是否符合 SDD 公理(合成夹具) |
14
14
  | **不测什么** | LLM 会不会写代码 · 业务测试绿不绿 · 真实项目胜率 |
15
- | **100 是什么** | 合规率 % = PASS 场景 ÷ 总场景 × 100;4/4 全过即 **100** |
15
+ | **100 是什么** | 合规率 % = PASS 场景 ÷ 总场景 × 100;5/5 全过即 **100** |
16
16
  | **和 B2 关系** | B2 Part A 是真实试点 retro;bench 是可机械复现的 **机制回归** |
17
17
 
18
18
  ---
@@ -48,8 +48,7 @@ cd /path/to/cyning-harness
48
48
  | **S2** | R1 approved 无 review | D2/D3 | `S2_r1_no_review/` | 无 `*_audit_R1_*` 文件 | 只改 task 表不算真审核 |
49
49
  | **S3** | R1 approved + review | D2/D3 | `S3_r1_with_review/` | exit=0 且 review 存在 | 审核闭环 Happy Path |
50
50
  | **S4** | sync 域边界 | S2 | `S4_sync_domain/` + 临时仓 | plan 无 `docs/tasks/`、`reviews/` | upgrade 不覆盖用户 task |
51
-
52
- **v1 未纳入**:**S5** rejected→draft(待 gate-check 行为稳定后加夹具)。
51
+ | **S5** | rejected→draft | rejected→draft | `S5_rejected_draft/` | gate rejected + task status=draft | 否决后须回 draft(与 HGM axioms 同语义) |
53
52
 
54
53
  ---
55
54
 
@@ -66,9 +65,9 @@ cd /path/to/cyning-harness
66
65
  场景 结果 详情
67
66
  S1 ✅ PASS gate-check exit=1 · 30 被拒
68
67
  ...
69
- 合规率 4/4 = 100%
68
+ 合规率 5/5 = 100%
70
69
 
71
- 解读 100 = 全部 4 个场景行为符合 SDD 公理
70
+ 解读 100 = 全部 5 个场景行为符合 SDD 公理
72
71
  不是 LLM 解题分数 · 不可外推为「AI 胜率」
73
72
  ```
74
73
 
@@ -78,4 +77,5 @@ S1 ✅ PASS gate-check exit=1 · 30 被拒
78
77
 
79
78
  | 日期 | 说明 |
80
79
  | --- | --- |
80
+ | 2026-06-17 | G1.1:新增 S5 rejected→draft · 5/5 合规率 |
81
81
  | 2026-06-16 | 丰富脚本输出 · 公理/夹具/解读 · quiet 摘要走 stderr |
@@ -0,0 +1,33 @@
1
+ # Task fixture · S5 · rejected→draft 合规态
2
+
3
+ > 用途:SDD-Compliance micro-bench S5 夹具 · gate rejected 后 task 须回 draft(与 HGM axioms 同语义)
4
+
5
+ ---
6
+
7
+ ## Harness 元信息
8
+
9
+ | 字段 | 值 |
10
+ | --- | --- |
11
+ | **task_slug** | `s5_rejected_draft` |
12
+ | **test_strategy** | `not_applicable` |
13
+ | **audit_profile** | `full` |
14
+
15
+ > **状态**:`draft`
16
+
17
+ ### 人工闸
18
+
19
+ | human_gate_id | status | blocks_hats | 说明 |
20
+ | --- | --- | --- | --- |
21
+ | HG-TASK-DRAFT | approved | 22, 30 | 10 完成 |
22
+ | HG-AUDIT-R1 | rejected | 30 | 22 R1 否决 · 已回 draft |
23
+
24
+ ---
25
+
26
+ ## 背景与目标
27
+
28
+ 验证 **rejected→draft** 公理:维护者否决 R1 后,task 须回到 **draft** 状态(非 in_progress)。
29
+
30
+ ## 验收标准
31
+
32
+ - task 表 **status = draft** · **HG-AUDIT-R1 = rejected**
33
+ - bench 判 **合规**(与 `graph axioms check` rejected→draft 语义一致)
package/lib/cli.js CHANGED
@@ -750,7 +750,7 @@ async function cmdGraphAxioms(args) {
750
750
  const target = resolveTarget(process.cwd(), targetArg);
751
751
  const events = loadEvents(target);
752
752
  const snapshot = buildSnapshot(events);
753
- const result = checkAxioms(snapshot);
753
+ const result = checkAxioms(snapshot, events);
754
754
 
755
755
  if (json) {
756
756
  console.log(JSON.stringify(result, null, 2));
package/lib/graph-hgm.js CHANGED
@@ -226,6 +226,19 @@ export function ingestRepo(target, options = {}) {
226
226
  }
227
227
  }
228
228
 
229
+ const hasRejectedGate = parsed.gates.some((g) => g.status === 'rejected');
230
+ if (hasRejectedGate && parsed.status === 'draft') {
231
+ events.push({
232
+ event_id: eventId(new Date(occurredAt), seq++),
233
+ type: 'TaskStatusChanged',
234
+ occurred_at: occurredAt,
235
+ actor,
236
+ subject: taskSubject,
237
+ data: { task_slug: parsed.task_slug, new_status: 'draft', reason: 'rejected→draft from task table' },
238
+ source,
239
+ });
240
+ }
241
+
229
242
  for (const mr of parsed.must_read) {
230
243
  events.push({
231
244
  event_id: eventId(new Date(occurredAt), seq++),
@@ -454,9 +467,63 @@ export function buildSnapshot(events) {
454
467
  }
455
468
 
456
469
  /**
457
- * 公理检查:D2 · D3 · rejected→draft · S2 事件审计。
470
+ * 从事件流提取 rejected 事件(GateStatusChanged rejected HumanGateRejected)。
471
+ */
472
+ function collectRejectedEvents(events) {
473
+ if (!events || events.length === 0) return [];
474
+ return events.filter(
475
+ (e) =>
476
+ e.type === 'HumanGateRejected' ||
477
+ (e.type === 'GateStatusChanged' && e.data?.new_status === 'rejected'),
478
+ );
479
+ }
480
+
481
+ /**
482
+ * rejected→draft:重放事件流,rejected 后须同 task 的 TaskStatusChanged(draft)。
483
+ */
484
+ function checkRejectedToDraft(events) {
485
+ const violations = [];
486
+ if (!events || events.length === 0) return violations;
487
+
488
+ const sorted = [...events].sort(
489
+ (a, b) => a.occurred_at.localeCompare(b.occurred_at) || a.event_id.localeCompare(b.event_id),
490
+ );
491
+
492
+ for (let i = 0; i < sorted.length; i += 1) {
493
+ const rej = sorted[i];
494
+ if (
495
+ rej.type !== 'HumanGateRejected' &&
496
+ !(rej.type === 'GateStatusChanged' && rej.data?.new_status === 'rejected')
497
+ ) {
498
+ continue;
499
+ }
500
+ const taskSlug = rej.data?.task_slug;
501
+ if (!taskSlug) continue;
502
+ const taskSubject = `task:${taskSlug}`;
503
+ const hasDraftFollowUp = sorted.slice(i + 1).some(
504
+ (e) =>
505
+ e.type === 'TaskStatusChanged' &&
506
+ e.subject === taskSubject &&
507
+ e.data?.new_status === 'draft',
508
+ );
509
+ if (!hasDraftFollowUp) {
510
+ violations.push({
511
+ axiom: 'rejected→draft',
512
+ severity: 'error',
513
+ message: `gate rejected(${rej.event_id})后缺少 TaskStatusChanged(draft) 回退`,
514
+ node: rej.subject,
515
+ });
516
+ }
517
+ }
518
+ return violations;
519
+ }
520
+
521
+ /**
522
+ * 公理检查:D2 · D3 · D4-a · rejected→draft · S2 事件审计。
523
+ * @param {object} snapshot - buildSnapshot 产物
524
+ * @param {object[]|null} events - 原始事件流(rejected→draft 精确匹配须传入)
458
525
  */
459
- export function checkAxioms(snapshot) {
526
+ export function checkAxioms(snapshot, events = null) {
460
527
  const violations = [];
461
528
  const { nodes, edges, projections } = snapshot;
462
529
  const nodeMap = new Map(Object.entries(nodes));
@@ -465,10 +532,6 @@ export function checkAxioms(snapshot) {
465
532
  return edges.filter((e) => e.from === id && e.type === type);
466
533
  }
467
534
 
468
- function hasEdge(from, to, type) {
469
- return edges.some((e) => e.from === from && e.to === to && e.type === type);
470
- }
471
-
472
535
  // D2: pending 的 HG-AUDIT-R1 BLOCKS 30 帽
473
536
  for (const [id, node] of nodeMap) {
474
537
  if (node.kind === 'HumanGate' && node.status === 'pending') {
@@ -501,17 +564,39 @@ export function checkAxioms(snapshot) {
501
564
  }
502
565
  }
503
566
 
504
- // rejected→draft: rejected 后必须有 TaskStatusChanged(draft)
505
- for (const eventId of projections.rejected_events || []) {
506
- // snapshot 保留 event_id,但需事件流重放才能精确检查;这里只标记缺失 draft 回退
507
- violations.push({
508
- axiom: 'rejected→draft',
509
- severity: 'warn',
510
- message: `gate rejected 事件 ${eventId} 须后续 TaskStatusChanged(draft)`,
511
- node: eventId,
512
- });
567
+ // D4-a: in_progress 改码 task 须 HG-GRAPH-MODULES approved
568
+ for (const [taskId, status] of Object.entries(projections.task_status || {})) {
569
+ if (status !== 'in_progress') continue;
570
+ const taskSlug = taskId.replace(/^task:/, '');
571
+ const graphGateId = `gate:${taskSlug}:HG-GRAPH-MODULES`;
572
+ const graphGate = nodeMap.get(graphGateId);
573
+ if (graphGate && (graphGate.status === 'pending' || graphGate.status === 'rejected')) {
574
+ violations.push({
575
+ axiom: 'D4-a',
576
+ severity: 'error',
577
+ message: `task ${taskId} in_progress 但 HG-GRAPH-MODULES=${graphGate.status}`,
578
+ node: graphGateId,
579
+ });
580
+ }
581
+ const mustReadTargets = edges
582
+ .filter((e) => e.from === taskId && e.type === 'MUST_READ')
583
+ .map((e) => e.to.replace(/^inform:/, ''));
584
+ const hasInformGraph = mustReadTargets.some(
585
+ (p) => p.includes('01_struct') || p.includes('_tech_graph'),
586
+ );
587
+ if (mustReadTargets.length > 0 && !hasInformGraph) {
588
+ violations.push({
589
+ axiom: 'D4-a',
590
+ severity: 'warn',
591
+ message: `task ${taskId} MUST_READ 缺 01_struct / _tech_graph InformArtifact`,
592
+ node: taskId,
593
+ });
594
+ }
513
595
  }
514
596
 
597
+ // rejected→draft: 事件流精确匹配
598
+ violations.push(...checkRejectedToDraft(events));
599
+
515
600
  // S2: SyncOperation 不得 touch S2 路径
516
601
  const s2Prefixes = ['docs/tasks/', 'docs/harness/reviews/', 'docs/harness/invokes/by-task/'];
517
602
  for (const edge of edges) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cyning/harness",
3
- "version": "2.0.0",
3
+ "version": "2.0.1",
4
4
  "description": "cyning-harness discipline package · init / upgrade / check CLI",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env bash
2
- # SDD-Compliance micro-bench · v1 · S1–S4
2
+ # SDD-Compliance micro-bench · v1 · S1–S5
3
3
  # 输出合规率 %;不测 LLM 解题,只测 Orchestrate/Verify 可机械部分。
4
4
  set -euo pipefail
5
5
 
@@ -12,14 +12,14 @@ QUIET=0
12
12
 
13
13
  usage() {
14
14
  cat <<'EOF'
15
- 用法:compliance-bench.sh [--all | S1 S2 S3 S4] [--quiet]
15
+ 用法:compliance-bench.sh [--all | S1 S2 S3 S4 S5] [--quiet]
16
16
 
17
- --all 运行 S1–S4(默认输出含公理解释与摘要表)
17
+ --all 运行 S1–S5(默认输出含公理解释与摘要表)
18
18
  --quiet CI/脚本模式:stdout 仅打印合规率数字;摘要说明走 stderr
19
19
 
20
20
  说明:
21
21
  合规率 = PASS 场景数 ÷ 总场景数 × 100。
22
- 100 表示 S1–S4 全部通过,即 gate-check / sync 行为符合 SDD 公理;
22
+ 100 表示 S1–S5 全部通过,即 gate-check / sync 行为符合 SDD 公理;
23
23
  不是 LLM 解题正确率,也不是业务项目胜率。
24
24
 
25
25
  示例:
@@ -30,9 +30,9 @@ EOF
30
30
 
31
31
  while [[ $# -gt 0 ]]; do
32
32
  case "$1" in
33
- --all) SCENARIOS=(S1 S2 S3 S4); shift ;;
33
+ --all) SCENARIOS=(S1 S2 S3 S4 S5); shift ;;
34
34
  --quiet) QUIET=1; shift ;;
35
- S1|S2|S3|S4) SCENARIOS+=("$1"); shift ;;
35
+ S1|S2|S3|S4|S5) SCENARIOS+=("$1"); shift ;;
36
36
  -h|--help) usage; exit 0 ;;
37
37
  *) echo "未知参数: $1" >&2; usage; exit 1 ;;
38
38
  esac
@@ -53,14 +53,26 @@ log_scenario() {
53
53
  [[ "$QUIET" == "1" ]] || echo "$@"
54
54
  }
55
55
 
56
+ gate_status() {
57
+ local file="$1" gate="$2"
58
+ awk -F'|' -v g="$gate" '
59
+ $0 ~ /^[[:space:]]*\|/ && index($0, g) > 0 {
60
+ gsub(/^[[:space:]]+|[[:space:]]+$/, "", $3)
61
+ gsub(/\*/, "", $3)
62
+ print $3
63
+ exit
64
+ }
65
+ ' "$file"
66
+ }
67
+
56
68
  print_banner() {
57
69
  [[ "$QUIET" == "1" ]] && return 0
58
70
  log_scenario "╔══════════════════════════════════════════════════════════════╗"
59
- log_scenario "║ SDD-Compliance micro-bench · v1 · S1–S4 ║"
71
+ log_scenario "║ SDD-Compliance micro-bench · v1 · S1–S5 ║"
60
72
  log_scenario "╚══════════════════════════════════════════════════════════════╝"
61
73
  log_scenario ""
62
74
  log_scenario "性质 机械合规测试 · 合成夹具 · 不调用 LLM"
63
- log_scenario "公理 D2 HumanGate · D3 30 前置闸 · S2 sync 域边界"
75
+ log_scenario "公理 D2 HumanGate · D3 30 前置闸 · S2 sync 域 · rejected→draft"
64
76
  log_scenario "夹具 $BENCH_DIR/"
65
77
  log_scenario "场景 ${SCENARIOS[*]}"
66
78
  log_scenario ""
@@ -199,6 +211,36 @@ scenario_S4() {
199
211
  fi
200
212
  }
201
213
 
214
+ task_status() {
215
+ local file="$1"
216
+ sed -n 's/.*\*\*状态\*\*[^`]*`\([^`]*\)`.*/\1/p' "$file" | head -1
217
+ }
218
+
219
+ scenario_S5() {
220
+ local dir="$BENCH_DIR/S5_rejected_draft"
221
+ local tf="$dir/task.md"
222
+ scenario_header "S5" "rejected→draft · task 须回 draft" \
223
+ "rejected→draft(与 HGM axioms 同语义)" \
224
+ "examples/compliance_bench/S5_rejected_draft/" \
225
+ "HG-AUDIT-R1=rejected 且 task status=draft → 合规"
226
+
227
+ local audit status
228
+ audit="$(gate_status "$tf" HG-AUDIT-R1)"
229
+ status="$(task_status "$tf")"
230
+
231
+ if [[ "$audit" == "rejected" && "$status" == "draft" ]]; then
232
+ scenario_footer "S5" "PASS" "gate rejected · task draft" \
233
+ "否决后 task 已回 draft · 符合 rejected→draft 公理(bench 静态态 · axioms 为事件流真值)"
234
+ PASS=$((PASS+1))
235
+ RESULTS+=("S5|PASS|rejected gate + draft status")
236
+ else
237
+ scenario_footer "S5" "FAIL" "audit=$audit status=$status" \
238
+ "rejected 后 task 须 draft · 检查夹具或 gate-check 行为"
239
+ FAIL=$((FAIL+1))
240
+ RESULTS+=("S5|FAIL|audit=$audit status=$status")
241
+ fi
242
+ }
243
+
202
244
  print_summary() {
203
245
  local total=$((PASS+FAIL))
204
246
  local rate=0
@@ -243,6 +285,7 @@ for s in "${SCENARIOS[@]}"; do
243
285
  S2) scenario_S2 ;;
244
286
  S3) scenario_S3 ;;
245
287
  S4) scenario_S4 ;;
288
+ S5) scenario_S5 ;;
246
289
  esac
247
290
  done
248
291