@furongjun1999/dsh-memory 0.6.0 → 0.6.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.
Files changed (170) hide show
  1. package/README.md +588 -572
  2. package/codebuddy/CODEBUDDY.md +10 -10
  3. package/data/policy.json +27 -0
  4. package/docs/discipline/harnesses.yaml +31 -0
  5. package/docs/discipline/templates/full.md.tmpl +1 -1
  6. package/docs/discipline/templates/rules.mdc.tmpl +1 -1
  7. package/docs/eval/N225_/347/264/242/345/274/225/346/227/245/345/277/227/351/235/236/345/257/271/350/261/241/350/243/205/350/275/275/351/235/242/347/261/273/345/236/213/351/227/270_v1.0.md +418 -0
  8. package/docs/eval/issue43_/351/273/230/350/256/244/347/255/226/347/225/245/344/270/216/351/224/256/347/261/273/345/236/213/351/227/270_v1.1.md +441 -0
  9. package/docs/eval/issue43_/351/273/230/350/256/244/347/255/226/347/225/245/345/212/240/350/275/275/344/270/216/345/207/255/346/215/256/346/230/216/346/226/207/351/230/237/345/210/227_v1.0.md +398 -0
  10. package/docs/eval//344/274/230/345/214/226/347/254/254/344/270/200/346/211/271_/346/216/245/347/272/277/344/270/216/347/255/211/344/273/267/345/217/230/346/215/242_v1.0.md +498 -0
  11. package/docs/eval//344/274/230/345/214/226/347/254/254/344/270/211/346/211/271_/351/227/250/347/246/201/350/275/254/346/255/243/344/270/216/350/260/203/345/272/246/346/255/242/350/241/200/344/270/216/351/227/250/346/216/247/346/224/266/345/217/243_v1.0.md +669 -0
  12. package/docs/eval//344/274/230/345/214/226/347/254/254/344/272/214/346/211/271_/344/276/235/350/265/226/351/200/217/344/274/240/344/270/216/345/257/271/346/213/215/345/217/243/345/276/204/344/270/216/350/264/237/347/274/223/345/255/230_v1.0.md +683 -0
  13. package/docs/eval//345/217/221/345/270/20307_/345/244/226/351/203/250/346/212/245/345/221/212/345/233/233/346/211/271/344/277/256/345/244/215/344/270/216/346/217/222/344/273/266/351/235/242/345/212/240/345/233/272_v1.0.md +205 -0
  14. package/docs/eval//346/200/247/350/203/275/344/270/223/351/241/271_/345/206/267/346/237/245/350/257/242/344/270/216/345/206/205/345/255/230_v1.0.md +218 -0
  15. package/docs/eval//346/225/205/351/232/234/346/263/250/345/205/245/345/256/236/346/265/213_v1.0.md +10 -0
  16. package/docs/eval//347/274/226/347/240/201/351/235/242/345/211/215/347/275/256_/345/205/245/345/217/243/350/207/252/344/277/235/350/257/201UTF8/344/270/216/346/226/207/346/234/254open/345/256/210/345/215/253_v1.0.md +646 -0
  17. package/docs/eval//347/274/272/351/231/267/346/214/226/346/216/230_/350/207/252/344/270/273/350/277/255/344/273/243_v20.md +283 -0
  18. package/docs/eval//347/274/272/351/231/267/346/214/226/346/216/230_/350/207/252/344/270/273/350/277/255/344/273/243_v21.md +224 -0
  19. package/docs/eval//347/274/272/351/231/267/346/214/226/346/216/230_/350/207/252/344/270/273/350/277/255/344/273/243_v22.md +223 -0
  20. package/docs/eval//347/274/272/351/231/267/346/214/226/346/216/230_/350/207/252/344/270/273/350/277/255/344/273/243_v23.md +293 -0
  21. package/docs/eval//347/274/272/351/231/267/346/214/226/346/216/230_/350/207/252/344/270/273/350/277/255/344/273/243_v24.md +303 -0
  22. package/docs/eval//347/274/272/351/231/267/346/214/226/346/216/230_/350/207/252/344/270/273/350/277/255/344/273/243_v25.md +230 -0
  23. package/docs/hive//346/243/200/347/264/242/350/267/257/345/276/204/344/270/216/350/256/244/347/237/245/347/273/223/346/236/204/345/245/221/347/272/246_v0.1.md +20 -0
  24. package/docs/hive//350/234/202/345/267/242M6_ingest/345/256/236/346/226/275/350/256/241/345/210/222_v0.1.md +1 -1
  25. package/docs/mdcg/README/350/257/246/347/273/206/347/211/210_v0.4.10.md +12 -0
  26. package/docs/mdcg//345/212/237/350/203/275/350/260/203/347/224/250/346/230/240/345/260/204/350/241/250_v0.1.md +40 -40
  27. package/docs/mdcg//345/217/221/345/270/203/351/227/250/347/246/201/351/223/276_v0.1.md +21 -9
  28. package/docs/mdcg//347/201/265/346/236/242/350/256/260/345/277/206/345/212/250/350/257/215/345/215/217/350/256/256_v1.0-draft.md +7 -2
  29. package/docs/mdcg//350/256/244/347/237/245/345/233/276_/347/264/242/345/274/225/344/270/216/345/267/245/347/250/213/350/247/204/350/214/203/345/214/226_/350/256/241/345/210/222_v0.1.md +39 -3
  30. package/docs/plans//345/205/250/344/270/255/346/226/207/347/274/226/347/240/201/344/270/216/350/234/202/345/267/242/344/273/273/345/212/241/346/240/207/350/257/206/345/245/221/347/272/246_v2.0.md +220 -0
  31. package/docs/plans//347/234/237/346/272/220/347/264/242/345/274/225_/351/200/232/347/224/250/346/234/272/345/210/266_v0.3.md +227 -0
  32. package/docs/plans//350/234/202/345/267/242/346/250/241/345/236/213/345/257/206/351/222/245/351/205/215/347/275/256/351/235/242_v1.0.md +385 -0
  33. package/docs//345/267/245/344/275/234/347/272/252/345/276/213_/350/256/244/347/237/245/345/233/276/346/235/241/347/233/256_v1.1.json +460 -460
  34. package/lib/bridge.js +24 -2
  35. package/lib/hooks.d.ts +31 -2
  36. package/lib/hooks.js +219 -16
  37. package/lib/init.js +16 -1
  38. package/lib/lib/prompt_safety.d.ts +52 -1
  39. package/lib/lib/prompt_safety.js +76 -1
  40. package/md_cg/audit.py +652 -379
  41. package/md_cg/bench6_arms.py +1 -1
  42. package/md_cg/bench_p0.py +1 -1
  43. package/md_cg/branches.py +2 -2
  44. package/md_cg/ccgc.py +1071 -1006
  45. package/md_cg/chain.py +1 -1
  46. package/md_cg/consistency.py +97 -9
  47. package/md_cg/consolidate.py +34 -9
  48. package/md_cg/crypto.py +59 -26
  49. package/md_cg/docindex.py +16 -1
  50. package/md_cg/evolution.py +17 -1
  51. package/md_cg/export.py +1 -1
  52. package/md_cg/forgetting.py +309 -13
  53. package/md_cg/fsutil.py +454 -8
  54. package/md_cg/identity.py +3 -3
  55. package/md_cg/insight.py +24 -3
  56. package/md_cg/linkref.py +1 -1
  57. package/md_cg/logref.py +327 -0
  58. package/md_cg/mcp_server.py +4152 -3818
  59. package/md_cg/mdcg.py +4208 -3547
  60. package/md_cg/mdcos.py +4491 -4159
  61. package/md_cg/mreview/pipeline.py +15 -1
  62. package/md_cg/nodefile.py +639 -575
  63. package/md_cg/protect.py +158 -11
  64. package/md_cg/protocol.py +41 -2
  65. package/md_cg/provenance.py +22 -4
  66. package/md_cg/reach.py +4 -4
  67. package/md_cg/readcache.py +76 -15
  68. package/md_cg/reconcile.py +1 -1
  69. package/md_cg/refindex.py +246 -37
  70. package/md_cg/refine.py +1 -1
  71. package/md_cg/routing.py +32 -5
  72. package/md_cg/run_tests.py +17 -0
  73. package/md_cg/scrub.py +30 -3
  74. package/md_cg/security.py +47 -1
  75. package/md_cg/self_state.py +5 -5
  76. package/md_cg/sources.py +23 -6
  77. package/md_cg/srcindex.py +352 -0
  78. package/md_cg/stg.py +47 -3
  79. package/md_cg/subgraph.py +2 -2
  80. package/md_cg/sustain.py +1299 -1168
  81. package/md_cg/tasks.py +469 -470
  82. package/md_cg/test_b1_auto_id_multiproc.py +277 -0
  83. package/md_cg/test_b1b2_write_face.py +417 -0
  84. package/md_cg/test_b2_sensitivity_landing.py +223 -0
  85. package/md_cg/test_b3_merge_keeps_content.py +576 -0
  86. package/md_cg/test_b4_shard_dir_selfheal.py +843 -0
  87. package/md_cg/test_b4_shard_dir_selfheal_guard.py +238 -0
  88. package/md_cg/test_c3_transient_read_negative.py +793 -0
  89. package/md_cg/test_c8_search_rrf_gates.py +502 -0
  90. package/md_cg/test_ccg_form_parity.py +188 -0
  91. package/md_cg/test_ccgc.py +28 -3
  92. package/md_cg/test_govern_directread.py +17 -4
  93. package/md_cg/test_h2_session_view_norm.py +233 -0
  94. package/md_cg/test_h4_sustain_snapshot.py +1377 -0
  95. package/md_cg/test_hive_ingest.py +285 -285
  96. package/md_cg/test_issue39_utf8_stdio.py +307 -16
  97. package/md_cg/test_issue43_default_policy.py +865 -0
  98. package/md_cg/test_legacy_p3_node_id_type.py +375 -0
  99. package/md_cg/test_linkref.py +10 -3
  100. package/md_cg/test_lock.py +2 -2
  101. package/md_cg/test_logref.py +1109 -0
  102. package/md_cg/test_m3_h9_bucket_health_protect_mark.py +301 -0
  103. package/md_cg/test_m3_h9_semantic_guard.py +525 -0
  104. package/md_cg/test_mr_m2.py +15 -3
  105. package/md_cg/test_n130_verify_falsified_protect.py +1 -1
  106. package/md_cg/test_n139_dek_provision_failclosed.py +185 -0
  107. package/md_cg/test_n176_link_trust.py +221 -0
  108. package/md_cg/test_n178_units_jobid_gate.py +212 -0
  109. package/md_cg/test_n184_keys_concurrent_provision.py +307 -0
  110. package/md_cg/test_n195_writepath_reload.py +283 -0
  111. package/md_cg/test_n196_stale_gate_skip.py +169 -0
  112. package/md_cg/test_n197_n208_write_face_gates.py +471 -0
  113. package/md_cg/test_n198_tokens_corrupt_failclosed.py +225 -0
  114. package/md_cg/test_n199_tokens_concurrent_write.py +378 -0
  115. package/md_cg/test_n201_proposal_visibility.py +382 -0
  116. package/md_cg/test_n202_session_notes_visibility.py +461 -0
  117. package/md_cg/test_n204_n205_n226_n227_n228_n229_exit_gates.py +542 -0
  118. package/md_cg/test_n206_stdio_jsonrpc_type.py +403 -0
  119. package/md_cg/test_n209_verify_write_face_gates.py +461 -0
  120. package/md_cg/test_n212_n213_n224_generation_gates.py +513 -0
  121. package/md_cg/test_n214_n215_n221_n222_write_face_gates.py +563 -0
  122. package/md_cg/test_n225_nonobject_load.py +1110 -0
  123. package/md_cg/test_n62_tenant_bind_failclosed.py +200 -0
  124. package/md_cg/test_neg_condition_hits.py +333 -0
  125. package/md_cg/test_neg_tail_honesty.py +663 -0
  126. package/md_cg/test_none_id_write_guard.py +4 -3
  127. package/md_cg/test_opt_batch1_md_cg.py +451 -0
  128. package/md_cg/test_p1.py +5 -5
  129. package/md_cg/test_p11_consistency.py +161 -12
  130. package/md_cg/test_p26_refindex.py +2 -2
  131. package/md_cg/test_p27_docindex.py +777 -774
  132. package/md_cg/test_p28_refcheck.py +740 -13
  133. package/md_cg/test_p29_session_ingest_export.py +2 -2
  134. package/md_cg/test_p2_mcp.py +12 -1
  135. package/md_cg/test_p30_maintain.py +33 -2
  136. package/md_cg/test_p31_insight.py +1 -0
  137. package/md_cg/test_p9c_dedup_hints.py +276 -0
  138. package/md_cg/test_policy_required_ccg.py +426 -0
  139. package/md_cg/test_protocol.py +22 -0
  140. package/md_cg/test_rank_parity_score_mode.py +516 -0
  141. package/md_cg/test_read_face_input_gates.py +363 -0
  142. package/md_cg/test_read_face_semantics.py +489 -0
  143. package/md_cg/test_recall_face_guards.py +812 -0
  144. package/md_cg/test_rejected_credential_forms.py +188 -0
  145. package/md_cg/test_rejected_redact.py +146 -0
  146. package/md_cg/test_retr_s1b.py +2 -2
  147. package/md_cg/test_retr_s5.py +20 -7
  148. package/md_cg/test_review_conformance.py +21 -3
  149. package/md_cg/test_security_audit_v21.py +2 -2
  150. package/md_cg/test_server_version.py +67 -0
  151. package/md_cg/test_srcindex.py +171 -0
  152. package/md_cg/test_tenant_env_override_warn.py +63 -50
  153. package/md_cg/test_token_lowercase_form.py +315 -0
  154. package/md_cg/test_v21r1_package_version.py +310 -0
  155. package/md_cg/test_writepipe.py +10 -4
  156. package/md_cg/tokens.py +225 -95
  157. package/md_cg/tool_face.py +4 -4
  158. package/md_cg/trust.py +49 -5
  159. package/md_cg/units.py +204 -6
  160. package/md_cg/weights.py +4 -4
  161. package/md_cg/whitebox_kb/wisdom/knowledge_points.py +1 -1
  162. package/md_cg/writelimit.py +42 -8
  163. package/md_cg/writepipe.py +651 -554
  164. package/package.json +3 -2
  165. package/skills/plugin.json +1 -1
  166. package/src/bridge.ts +25 -2
  167. package/src/hooks.ts +229 -16
  168. package/src/init.ts +15 -1
  169. package/src/lib/prompt_safety.ts +88 -1
  170. package/zcode/AGENTS.md +10 -10
@@ -14,9 +14,10 @@
14
14
  A L0 情绪通道:三态 / 二阶差分 / 不参与信任计算
15
15
  B L1 反思四态:ACCEPT / 自否定 REJECT / 违反纪律 REJECT / 条件互斥 DEFER / BLINDSPOT
16
16
  C L2 递归反思:沿边收敛 / 增益门槛停搜 / 深度上限 / 循环不爆炸
17
- D 冲突自动触发飞轮(落 unresolved)
17
+ D 冲突自动触发飞轮(**只限真冲突 REJECT**,H11⑥ 2026-09-30;落 unresolved)
18
18
  E 写入接入:add(consistency) 抛错 / defer 不落盘 / record 放行 / 闸门叠加
19
19
  F 留痕 / 统计 / 自描述 / health 面
20
+ G H8 结论同一性判据(CONCLUSION_SAME):逐字/空白差异不判分歧,真分歧不放宽
20
21
 
21
22
  运行:python -m md_cg.test_p11_consistency
22
23
  """
@@ -25,7 +26,7 @@ from __future__ import annotations
25
26
  import tempfile
26
27
 
27
28
  from .mdcos import MdCGOS
28
- from . import consistency
29
+ from . import consistency, protect
29
30
 
30
31
  PASS = FAIL = 0
31
32
  FAILS = []
@@ -149,15 +150,57 @@ def main():
149
150
  check("C4 循环检测:环不导致无限展开",
150
151
  0 < rec.get("nodes_visited", 0) <= consistency.MAX_NODES, str(rec))
151
152
 
152
- # ---------- D. 冲突自动触发飞轮 ----------
153
- print("\n[D] 冲突自动触发飞轮(误差 → 补条件 → 结构更新,:725)")
153
+ # ---------- D. 冲突自动触发飞轮(⑥ 2026-09-30 收窄:只限真冲突 REJECT) ----------
154
+ print("\n[D] 飞轮建单收窄(误差 → 补条件 → 结构更新,:725)")
155
+
156
+ def _unres():
157
+ return [n for n, e in list((cg.index.get("nodes") or {}).items())
158
+ if e.get("layer") == "unresolved"]
159
+
160
+ # D1/D2 收窄后**唯一**的触发面(REJECT)必须仍在——否则「收窄」就变成了
161
+ # 「静默关掉飞轮」,那是把缺陷挪到另一个位置。
162
+ _n_before = len(_unres())
163
+ r = cg.check_consistency("删除生产数据", layer="self", auto_flywheel=True)
164
+ check("D1 真冲突 REJECT 仍自动投递飞轮并返回 unresolved_id",
165
+ r["verdict"] == "REJECT" and bool(r.get("unresolved_id")),
166
+ f'{r["verdict"]} / {r.get("unresolved_id")}')
167
+ check("D2 飞轮落 unresolved 条目", len(_unres()) > _n_before,
168
+ str(_unres()[:3]))
169
+
170
+ # D3 收窄:DEFER 是「条件互斥/部分覆盖、**待确认**」,不是已判定的冲突
171
+ # ——改前它自动建单(本轮 ⑧ 的误判 DEFER/strength=1.0 就是这样固化成
172
+ # unr_9af4803381 的)。判定本身必须照旧如实返回,只是不再自动建单。
173
+ _n0 = len(_unres())
154
174
  r = cg.check_consistency("# 功能:生产批处理\n# 生效条件:生产环境\n",
155
175
  layer="knowledge", auto_flywheel=True)
156
- check("D1 冲突自动投递飞轮并返回 unresolved_id",
157
- bool(r.get("unresolved_id")), str(r.get("unresolved_id")))
158
- un = [n for n, e in (cg.index.get("nodes") or {}).items()
159
- if e.get("layer") == "unresolved"]
160
- check("D2 飞轮落 unresolved 条目", len(un) >= 1, str(un[:3]))
176
+ check("D3 DEFER 不再自动建单(判定仍如实返回 DEFER/≥CLASH_LOW)",
177
+ r["verdict"] == "DEFER"
178
+ and r["conflict_strength"] >= consistency.CLASH_LOW
179
+ and not r.get("unresolved_id")
180
+ and len(_unres()) == _n0,
181
+ f'{r["verdict"]}/{r["conflict_strength"]}/'
182
+ f'unr={r.get("unresolved_id")}/n={len(_unres())}')
183
+
184
+ # D4 收窄:BLINDSPOT 是「既有节点全无声明 ⇒ 检测前提不存在」,空库上恒成立
185
+ # ——改前每写一条就建一张工单(死胡同)。用独立空库测(本库已有可比对节点)。
186
+ d_root = tempfile.mkdtemp(prefix="mdcg_p11_d4_")
187
+ d = MdCGOS(d_root)
188
+ r = d.check_consistency("# 功能:某情景\n# 生效条件:某个特殊条件\n",
189
+ layer="contextual", auto_flywheel=True)
190
+ _du = [n for n, e in list((d.index.get("nodes") or {}).items())
191
+ if e.get("layer") == "unresolved"]
192
+ check("D4 BLINDSPOT 不再自动建单(空库不产工单,判定仍为 BLINDSPOT)",
193
+ r["verdict"] == "BLINDSPOT" and not r.get("unresolved_id")
194
+ and not _du,
195
+ f'{r["verdict"]} / unr={r.get("unresolved_id")} / n={len(_du)}')
196
+ d.close()
197
+
198
+ # D5 自描述表与判据同源(不许「文档写 REJECT、代码仍建三种」)
199
+ _cat_d = cg.consistency_catalog()
200
+ check("D5 catalog 的 auto_flywheel.triggers_on 与判据单点同源",
201
+ _cat_d["auto_flywheel"]["triggers_on"] == list(
202
+ consistency.FLYWHEEL_TRIGGERS),
203
+ str(_cat_d["auto_flywheel"]["triggers_on"]))
161
204
 
162
205
  # ---------- E. 写入接入 ----------
163
206
  print("\n[E] 写入接入(信息的修改必须与已有规则校验)")
@@ -187,9 +230,20 @@ def main():
187
230
  (fm3.get("consistency") or {}).get("verdict") == "REJECT",
188
231
  str(fm3.get("consistency")))
189
232
 
190
- cg.remember_gated("g_1", "删除生产数据", layer="self", consistency=True)
191
- check("E5 遗忘闸门叠加冲突检测:冲突内容不落盘",
192
- cg.get("g_1") is None, str(cg.get("g_1")))
233
+ # N214(2026-09-28,既有套件适配):该调用的判据是 **MERGE**,落库目标就是
234
+ # 上面的 **self 层纪律节点** `discipline_prod`——`forgetting.reinforce` 现在
235
+ # 写盘前过 `protect.guard_overwrite`(层闸 + 保护闸,与 `cg.add` 同口径),
236
+ # 受保护层节点不再被无 override 覆写,故此处**抛 `ProtectionError`**(fail-
237
+ # closed,不是静默改写)。语义与断言不变:冲突内容一律不落盘(g_1 从不创建),
238
+ # 另加一条「拒绝而非静默改写」的形态断言。
239
+ _gated_denied = False
240
+ try:
241
+ cg.remember_gated("g_1", "删除生产数据", layer="self",
242
+ consistency=True)
243
+ except protect.ProtectionError:
244
+ _gated_denied = True
245
+ check("E5 遗忘闸门叠加冲突检测:冲突内容不落盘(受保护层目标被写保护闸拒)",
246
+ cg.get("g_1") is None and _gated_denied, str(cg.get("g_1")))
193
247
 
194
248
  # ---------- F. 留痕 / 统计 / 自描述 ----------
195
249
  print("\n[F] 留痕 / 统计 / 自描述")
@@ -215,6 +269,101 @@ def main():
215
269
  "consistency" in hh.get("os", {}),
216
270
  str(hh.get("os", {}).get("consistency"))[:90])
217
271
 
272
+ # ---------- G. H8:结论同一性判据(CONCLUSION_SAME) ----------
273
+ # 为什么单独立段:改前`CONCLUSION_SAME`这条判据**零测试覆盖**(编排侧全仓搜过,
274
+ # 无任何断言触及)。而它是 L1-c(same_condition_divergence)唯一的分流门——
275
+ # 门被误触就凭空多出「同条件分歧」工单,门被放宽就把真分歧吞掉。
276
+ # 本段三件事一起钉死:
277
+ # ① 逐字重复 1.0 与**纯空白差异**(\r\n↔\n / 缩进(半角·全角)/ 空行 /
278
+ # 行尾空格 / 词内插空白 / 词间多空格)一律**不判分歧**;
279
+ # ② 标定注释里的「同槽不同值」「同值异措辞」两类**仍判分歧**(不许放宽);
280
+ # ③ 阈值常量本身仍是 0.95(防「为了让守卫变绿而调参」)。
281
+ print("\n[G] H8 结论同一性判据(CONCLUSION_SAME):逐字/空白差异不判分歧")
282
+ g_root = tempfile.mkdtemp(prefix="mdcg_p11_h8_")
283
+ g = MdCGOS(g_root)
284
+ h = ("# 功能名:网关心跳端口与重连参数\n"
285
+ "# 子功能:端口取值与退避次数\n"
286
+ "# 生效条件:网关在线且心跳已启用\n")
287
+ body = ("服务端心跳端口:9090。客户端使用长连接轮询,超时时间为 30 秒。\n"
288
+ "故障处置:连接断开后由客户端指数退避重连,最多重试 5 次。")
289
+ g.add("h8_old", h + "\n" + body + "\n", layer="knowledge",
290
+ verification_basis="test", importance=0.5)
291
+
292
+ def _concl(content, **kw):
293
+ """(verdict, 同条件分歧条目列表, conclusion_overlap 或 None)。"""
294
+ r = g.check_consistency(content, layer="knowledge", depth=0, **kw)
295
+ d = [c for c in r["conflicts"]
296
+ if c.get("type") == "same_condition_divergence"]
297
+ return r, d, (d[0]["conclusion_overlap"] if d else None)
298
+
299
+ check("G0 空白归一化契约:连续空白折叠为单空格,全角空格同归一",
300
+ consistency._norm_ws(" 甲\r\n乙\t\u3000丙 ") == "甲 乙 丙",
301
+ repr(consistency._norm_ws(" 甲\r\n乙\t\u3000丙 ")))
302
+ check("G0b 去空白契约:去掉全部空白(含全角空格/换行/制表)",
303
+ consistency._nows(" 甲\t乙\u3000丙 \n") == "甲乙丙",
304
+ repr(consistency._nows(" 甲\t乙\u3000丙 \n")))
305
+ check("G0c 判据未放宽:CONCLUSION_SAME 仍为标定值 0.95",
306
+ consistency.CONCLUSION_SAME == 0.95,
307
+ str(consistency.CONCLUSION_SAME))
308
+
309
+ r, d, _c = _concl(h + "\n" + body + "\n")
310
+ check("G1 标定①逐字重复(标定 concl=1.0)→ 不判分歧、ACCEPT、无工单缺口",
311
+ not d and r["verdict"] == "ACCEPT" and r["conflict_strength"] == 0.0,
312
+ f'{r["verdict"]}/{r["conflict_strength"]}/{len(d)}')
313
+
314
+ # H8 现场 8 变体:正文逐字相同,只是空白不同(原样送检会在 0.95 门下假红)
315
+ _h8_variants = [
316
+ ("尾部多换行", h + "\n" + body + "\n\n"),
317
+ ("行首缩进(半角)",
318
+ h + "\n" + "\n".join(" " + x for x in body.split("\n")) + "\n"),
319
+ ("行首缩进(全角)",
320
+ h + "\n" + "\n".join("\u3000" + x for x in body.split("\n")) + "\n"),
321
+ ("插空行", h + "\n" + body.replace("\n", "\n\n") + "\n"),
322
+ ("行尾空格",
323
+ h + "\n" + "\n".join(x + " " for x in body.split("\n")) + "\n"),
324
+ ("词内插空白",
325
+ h + "\n" + body.replace("指数退避重连", "指数 退避重连") + "\n"),
326
+ ("词间多空格",
327
+ h + "\n" + body.replace("超时时间为 30 秒", "超时时间为 30 秒")
328
+ + "\n"),
329
+ ("组合(缩进+尾换行+行尾空格)",
330
+ h + "\n" + "\n".join(" " + x + " " for x in body.split("\n"))
331
+ + "\n\n"),
332
+ ]
333
+ for i, (vname, vcontent) in enumerate(_h8_variants, 1):
334
+ r, d, _c = _concl(vcontent)
335
+ check(f"H8-A{i} 空白差异「{vname}」不得判成分歧(须 ACCEPT / 0 分歧)",
336
+ not d and r["verdict"] == "ACCEPT"
337
+ and r["conflict_strength"] == 0.0,
338
+ f'{r["verdict"]}/{len(d)}/{r["conflict_strength"]}')
339
+
340
+ # 空白差异不得触发飞轮建单(判成 DEFER 就会各建一张 unresolved 工单)
341
+ r, _d, _c = _concl(h + "\n" + body.replace("\n", "\n\n") + "\n",
342
+ auto_flywheel=True)
343
+ check("H8-B 空白差异不建 unresolved 工单(飞轮不误触)",
344
+ not r.get("unresolved_id") and r["verdict"] == "ACCEPT",
345
+ str(r.get("unresolved_id")))
346
+
347
+ # 反向:真分歧两类**必须仍判分歧**(否则就是靠放宽判据变绿)
348
+ r, d, c = _concl("# 功能名:网关心跳端口与重连参数\n"
349
+ "# 子功能:端口取值与退避次数\n"
350
+ "# 生效条件:网关在线且心跳已启用\n\n"
351
+ "服务端心跳端口:8080。客户端使用长连接轮询,超时时间为 60 秒。\n"
352
+ "故障处置:连接断开后由客户端指数退避重连,最多重试 9 次。\n")
353
+ check("G2 标定②同槽不同值仍判分歧(实测 concl=0.6928 < 0.95)",
354
+ bool(d) and r["verdict"] == "DEFER" and 0.55 <= c < 0.95,
355
+ f'{r["verdict"]}/{c}/{len(d)}')
356
+
357
+ r, d, c = _concl("# 功能名:网关心跳端口与重连参数\n"
358
+ "# 子功能:端口取值与退避次数\n"
359
+ "# 生效条件:网关在线且心跳已启用\n\n"
360
+ "心跳所用服务端端口为 9090;客户端以长连接方式轮询,"
361
+ "超时设定 30 秒。\n"
362
+ "断开后的处理:客户端按指数退避重新连接,尝试上限 5 次。\n")
363
+ check("G3 标定③同值异措辞仍判分歧(实测 concl=0.3385 < 0.95)",
364
+ bool(d) and r["verdict"] == "DEFER" and 0.2 <= c < 0.8,
365
+ f'{r["verdict"]}/{c}/{len(d)}')
366
+
218
367
  print(f"\n==== P11 结果:{PASS} 通过 / {FAIL} 失败 ====")
219
368
  if FAILS:
220
369
  print("失败项:" + "、".join(FAILS))
@@ -386,9 +386,9 @@ def main():
386
386
 
387
387
  # ===================================================== ⑪ 幂等 + 不清空
388
388
  print("\n【11】幂等(重跑 ≡ 首跑)与 reset_root 不删目录")
389
- n1 = sum(1 for e in cg.index["nodes"].values() if e["layer"] == "knowledge")
389
+ n1 = sum(1 for e in list(cg.index["nodes"].values()) if e["layer"] == "knowledge")
390
390
  index_read(cg, code_dir)
391
- n2 = sum(1 for e in cg.index["nodes"].values() if e["layer"] == "knowledge")
391
+ n2 = sum(1 for e in list(cg.index["nodes"].values()) if e["layer"] == "knowledge")
392
392
  check("重跑 index_code:knowledge 节点数不变(原子覆盖)",
393
393
  n1 == n2, f"{n1} == {n2}")
394
394
  check("代码节点数 == 直接抽取条目数",