@furongjun1999/dsh-memory 0.4.8 → 0.4.10

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 (232) hide show
  1. package/README.md +54 -26
  2. package/codebuddy/CODEBUDDY.md +196 -195
  3. package/codebuddy/README.md +13 -1
  4. package/codebuddy/mcp.json +9 -0
  5. package/docs/GBrain/345/217/257/345/200/237/351/211/264/347/202/271_/347/201/265/346/236/242/350/220/275/347/202/271/344/272/244/346/216/245_20260919.md +169 -0
  6. package/docs/README.md +1 -1
  7. package/docs/discipline/harnesses.yaml +18 -7
  8. package/docs/discipline/templates/full.md.tmpl +4 -3
  9. package/docs/experiments/linkref_backfill/candidates_20260917.json +726 -0
  10. package/docs/experiments/linkref_backfill/candidates_internal_20260917.json +602 -0
  11. package/docs/experiments/linkref_backfill/candidates_internal_v2.json +603 -0
  12. package/docs/experiments/linkref_backfill/candidates_secret_20260917.json +884 -0
  13. package/docs/experiments/linkref_backfill/candidates_secret_v2.json +789 -0
  14. package/docs/hive//345/244/232/347/253/257harness/351/200/232/344/277/241/345/245/221/347/272/246_v0.1.md +42 -0
  15. package/docs/hive//346/243/200/347/264/242/346/224/266/346/225/233/345/256/236/346/265/213/344/270/216S1b/350/256/276/350/256/241_v0.1.md +43 -0
  16. 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 +102 -0
  17. package/docs/hive//347/234/237/345/256/236/345/272/223/347/253/257/345/210/260/347/253/257/345/256/236/346/265/213_S1b/344/270/216/345/217/254/345/233/236/346/235/203/350/241/241_v0.1.md +57 -0
  18. package/docs/hive//347/234/237/345/256/236/345/272/223/347/253/257/345/210/260/347/253/257/345/256/236/346/265/213_S7/345/200/222/346/216/222/345/200/231/351/200/211/345/261/202_v0.1.md +126 -0
  19. package/docs/hive//350/234/202/345/267/242/345/217/214/345/256/236/344/276/213/344/272/222/351/252/214_/350/256/276/350/256/241/345/256/232/347/250/277.md +503 -0
  20. package/docs/mdcg/D_meta_/345/267/245/347/250/213/345/214/226/346/226/271/346/241/210_v0.2.md +216 -0
  21. package/docs/mdcg/README/350/257/246/347/273/206/347/211/210_v0.4.5.md +631 -625
  22. package/docs/mdcg//344/273/243/347/240/201/350/257/204/345/256/241/344/270/216/346/235/241/344/273/266/345/214/226/346/263/250/351/207/212_/345/245/221/347/272/246_v0.1.md +82 -0
  23. package/docs/mdcg//345/205/250/345/272/223/344/273/243/347/240/201/350/257/204/345/256/241/344/270/216/346/235/241/344/273/266/345/214/226/346/263/250/351/207/212_/350/256/241/345/210/222_v0.1.md +600 -0
  24. 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 +47 -45
  25. package/docs/mdcg//345/255/220/344/273/243/347/220/206/351/205/215/347/275/256/346/240/207/345/207/{206_v0.4.md → 206_v0.5.md} +92 -4
  26. 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 +172 -0
  27. package/docs/mdcg//347/216/257/344/272/214_/347/231/275/347/256/261/345/241/253/345/205/205/346/265/201/346/260/264/347/272/277_v0.1.md +31 -0
  28. package/docs/mdcg//350/267/250/347/253/257/351/252/214/350/257/201/344/270/216/345/220/214/346/255/245/345/215/217/350/256/256_v0.1.md +93 -0
  29. package/docs/theory//345/271/266/345/217/221/345/277/205/347/204/266/346/200/247/347/220/206/350/256/272_v0.2.md +2 -2
  30. package/docs/theory//347/220/206/350/256/272_/346/234/272/345/210/266_/344/273/243/347/240/201_/345/256/236/351/252/214_/347/274/272/345/217/243/347/237/251/351/230/265_v0.1.md +3 -3
  31. package/docs/theory//350/256/244/347/237/245/344/273/243/347/220/206/344/270/216/346/224/266/346/225/233/347/273/223/346/236/204_/346/235/241/344/273/266/350/256/272/351/207/215/346/236/204_v0.1.md +2 -2
  32. 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 +434 -433
  33. package/docs//347/201/265/346/236/242/350/207/252/346/210/221/346/224/271/350/277/233/345/267/245/344/275/234/350/256/241/345/210/222_/345/244/226/351/203/250/347/240/224/347/251/266/347/263/273/345/210/227/345/220/270/346/224/266_v1_20260919.md +286 -0
  34. package/dsh/README.md +33 -0
  35. package/dsh/cordis.yml.example +13 -7
  36. package/dsh/hive-mcp-probe.mjs +94 -0
  37. package/dsh/hive-mcp.example.yml +62 -0
  38. package/dsh/update-lingshu.bat +11 -0
  39. package/dsh/update-lingshu.ps1 +337 -0
  40. package/lib/hooks.d.ts +3 -0
  41. package/lib/hooks.js +17 -23
  42. package/lib/index.d.ts +4 -2
  43. package/lib/index.js +29 -6
  44. package/lib/lib/datapath.d.ts +76 -1
  45. package/lib/lib/datapath.js +199 -13
  46. package/lib/lib/mdcg_client.d.ts +40 -3
  47. package/lib/lib/mdcg_client.js +46 -22
  48. package/lib/lib/mutual.js +4 -4
  49. package/lib/lib/token_store.js +4 -5
  50. package/md_cg/audit.py +17 -2
  51. package/md_cg/autonomy.py +86 -15
  52. package/md_cg/backfill.py +36 -1
  53. package/md_cg/backfill_bigdomain.py +34 -0
  54. package/md_cg/bench6_arms.py +28 -1
  55. package/md_cg/bench6_common.py +10 -1
  56. package/md_cg/bench6_competitors.py +6 -1
  57. package/md_cg/bench_axis_domain.py +9 -1
  58. package/md_cg/bench_blind_comp.py +7 -1
  59. package/md_cg/bench_en_atoms_public.py +9 -0
  60. package/md_cg/bench_governance.py +348 -0
  61. package/md_cg/bench_lme_zh.py +16 -1
  62. package/md_cg/bench_locomo.py +2 -1
  63. package/md_cg/bench_locomo_zh.py +16 -1
  64. package/md_cg/bench_locomo_zh_public.py +4 -1
  65. package/md_cg/bench_longmem.py +2 -1
  66. package/md_cg/bench_membench.py +27 -1
  67. package/md_cg/bench_p0.py +4 -1
  68. package/md_cg/bench_progressive.py +13 -1
  69. package/md_cg/bench_role_views.py +238 -0
  70. package/md_cg/bench_task_ab.py +8 -1
  71. package/md_cg/bench_task_ab_llm.py +13 -1
  72. package/md_cg/bench_unified_en.py +6 -1
  73. package/md_cg/bench_zh_mad.py +20 -1
  74. package/md_cg/blindspot_tickets.py +123 -0
  75. package/md_cg/branches.py +12 -1
  76. package/md_cg/build_postings.py +73 -0
  77. package/md_cg/ccgc.py +67 -2
  78. package/md_cg/census.py +5 -1
  79. package/md_cg/chain.py +24 -3
  80. package/md_cg/codeindex.py +134 -17
  81. package/md_cg/coldverify.py +265 -0
  82. package/md_cg/comment_gate.py +338 -0
  83. package/md_cg/cond_compose.py +190 -0
  84. package/md_cg/cond_facts.py +155 -0
  85. package/md_cg/cond_template.json +107 -0
  86. package/md_cg/condition_anchor.py +143 -0
  87. package/md_cg/conformance.py +69 -4
  88. package/md_cg/consistency.py +24 -1
  89. package/md_cg/consolidate.py +53 -2
  90. package/md_cg/corpus.py +4 -0
  91. package/md_cg/crosscheck.py +42 -2
  92. package/md_cg/crypto.py +35 -1
  93. package/md_cg/d_meta.py +310 -0
  94. package/md_cg/datapath.py +201 -26
  95. package/md_cg/docindex.py +122 -1
  96. package/md_cg/eval_common.py +29 -1
  97. package/md_cg/evidence.py +27 -1
  98. package/md_cg/evolution.py +21 -1
  99. package/md_cg/export.py +11 -1
  100. package/md_cg/forgetting.py +23 -1
  101. package/md_cg/fsutil.py +18 -1
  102. package/md_cg/hotcache.py +214 -0
  103. package/md_cg/hyperedge.py +251 -0
  104. package/md_cg/identity.py +18 -1
  105. package/md_cg/insight.py +17 -1
  106. package/md_cg/lexicon/build_cedict_en_zh.py +9 -0
  107. package/md_cg/lexicon/build_standard_en.py +171 -168
  108. package/md_cg/lexicon/expand_en_zh.py +6 -0
  109. package/md_cg/lifecycle.py +12 -1
  110. package/md_cg/linkref.py +281 -0
  111. package/md_cg/links.py +29 -1
  112. package/md_cg/mcp_server.py +362 -43
  113. package/md_cg/md_whitebox.py +53 -1
  114. package/md_cg/mdcg.py +1003 -27
  115. package/md_cg/mdcos.py +558 -36
  116. package/md_cg/metacognition.py +37 -2
  117. package/md_cg/migrate.py +4 -0
  118. package/md_cg/migrate_aeis.py +221 -213
  119. package/md_cg/migrate_roleplay.py +8 -0
  120. package/md_cg/migrate_wisdom_graph.py +14 -1
  121. package/md_cg/mreview/__main__.py +3 -0
  122. package/md_cg/mreview/bundle.py +8 -0
  123. package/md_cg/mreview/candidates.py +9 -0
  124. package/md_cg/mreview/govern.py +21 -1
  125. package/md_cg/mreview/locate.py +34 -0
  126. package/md_cg/mreview/pipeline.py +29 -1
  127. package/md_cg/mreview/ruleset.py +16 -1
  128. package/md_cg/nodefile.py +233 -3
  129. package/md_cg/pooling.py +23 -1
  130. package/md_cg/postings.py +298 -0
  131. package/md_cg/predict.py +89 -9
  132. package/md_cg/progressive.py +3 -0
  133. package/md_cg/protect.py +14 -1
  134. package/md_cg/protocol.py +372 -0
  135. package/md_cg/provenance.py +262 -0
  136. package/md_cg/reach.py +453 -0
  137. package/md_cg/refindex.py +47 -2
  138. package/md_cg/refine.py +20 -1
  139. package/md_cg/roleviews.py +89 -0
  140. package/md_cg/routing.py +76 -0
  141. package/md_cg/scrub.py +63 -2
  142. package/md_cg/security.py +26 -1
  143. package/md_cg/self_state.py +64 -1
  144. package/md_cg/selfreport.py +151 -0
  145. package/md_cg/semantic/canonical.py +5 -0
  146. package/md_cg/semantic/en_normalizer.py +364 -355
  147. package/md_cg/semantic/zh_en_atoms.py +139 -136
  148. package/md_cg/signer.py +41 -1
  149. package/md_cg/sources.py +583 -547
  150. package/md_cg/statushdr.py +179 -0
  151. package/md_cg/stg.py +59 -18
  152. package/md_cg/subgraph.py +23 -0
  153. package/md_cg/sustain.py +56 -1
  154. package/md_cg/tasks.py +26 -2
  155. package/md_cg/test_autonomy.py +26 -0
  156. package/md_cg/test_bench_governance.py +102 -0
  157. package/md_cg/test_blindspot_tickets.py +166 -0
  158. package/md_cg/test_ccgc.py +10 -0
  159. package/md_cg/test_codeindex.py +338 -0
  160. package/md_cg/test_comment_gate.py +187 -0
  161. package/md_cg/test_cond_compose_anchors.py +76 -0
  162. package/md_cg/test_condition_anchor.py +82 -0
  163. package/md_cg/test_d_meta.py +412 -0
  164. package/md_cg/test_datapath_root.py +188 -0
  165. package/md_cg/test_gain_gate.py +47 -1
  166. package/md_cg/test_hot_cold.py +187 -0
  167. package/md_cg/test_hyperedge.py +245 -0
  168. package/md_cg/test_linkref.py +306 -0
  169. package/md_cg/test_md_access_parity.py +15 -3
  170. package/md_cg/test_mr_m1.py +108 -18
  171. package/md_cg/test_mr_m3.py +8 -1
  172. package/md_cg/test_p26_refindex.py +49 -20
  173. package/md_cg/test_p27_docindex.py +236 -2
  174. package/md_cg/test_p2_mcp.py +1 -1
  175. package/md_cg/test_p31_insight.py +24 -0
  176. package/md_cg/test_p44_md_whitebox.py +14 -1
  177. package/md_cg/test_protocol.py +243 -0
  178. package/md_cg/test_reach.py +378 -0
  179. package/md_cg/test_reach_keys.py +201 -0
  180. package/md_cg/test_reach_meta_exits.py +145 -0
  181. package/md_cg/test_read_clip.py +8 -4
  182. package/md_cg/test_retr_s1.py +340 -0
  183. package/md_cg/test_retr_s1b.py +209 -0
  184. package/md_cg/test_retr_s3.py +194 -0
  185. package/md_cg/test_retr_s4.py +163 -0
  186. package/md_cg/test_retr_s5.py +200 -0
  187. package/md_cg/test_retr_s6.py +157 -0
  188. package/md_cg/test_retr_s7.py +385 -0
  189. package/md_cg/test_retr_s8_time.py +316 -0
  190. package/md_cg/test_retr_s9_edges.py +286 -0
  191. package/md_cg/test_retr_s9_entity_ctx.py +175 -0
  192. package/md_cg/test_review_conformance.py +59 -2
  193. package/md_cg/test_role_views.py +354 -0
  194. package/md_cg/test_subproc_encoding.py +188 -0
  195. package/md_cg/test_trust.py +361 -0
  196. package/md_cg/test_units_poll.py +71 -0
  197. package/md_cg/test_v14_fixes.py +397 -0
  198. package/md_cg/test_validity_filter.py +280 -0
  199. package/md_cg/test_wisdom_md_store.py +7 -3
  200. package/md_cg/test_writepipe.py +5 -1
  201. package/md_cg/theory.py +16 -1
  202. package/md_cg/tokens.py +40 -8
  203. package/md_cg/tool_face.py +13 -2
  204. package/md_cg/trust.py +943 -0
  205. package/md_cg/twophase.py +12 -1
  206. package/md_cg/units.py +132 -10
  207. package/md_cg/vision_evidence.py +24 -1
  208. package/md_cg/weights.py +24 -1
  209. package/md_cg/whitebox.py +32 -1
  210. package/md_cg/whitebox_kb/data/verify_cache.json +21210 -365
  211. package/md_cg/whitebox_kb/data/verify_savings.jsonl +5078 -0
  212. package/md_cg/whitebox_kb/wisdom/audit_log/chain_heat.json +10 -10
  213. package/md_cg/whitebox_kb/wisdom/code_compose.py +113 -6
  214. package/md_cg/whitebox_kb/wisdom/code_solidified.json +1 -1
  215. package/md_cg/whitebox_kb/wisdom/verifier.py +340 -55
  216. package/md_cg/whitebox_kb/wisdom/wisdom-book-cloud.db +0 -0
  217. package/md_cg/writelimit.py +18 -4
  218. package/md_cg/writepipe.py +178 -7
  219. package/package.json +2 -2
  220. package/skills/skills/designer-perspective/scripts/__pycache__/designer.cpython-310.pyc +0 -0
  221. package/skills/skills/designer-perspective/scripts/designer.py +17 -1
  222. package/skills/skills/designer-perspective/tests/selftest.py +3 -1
  223. package/src/hooks.ts +17 -21
  224. package/src/index.ts +33 -6
  225. package/src/lib/datapath.ts +211 -13
  226. package/src/lib/mdcg_client.ts +64 -25
  227. package/src/lib/mutual.ts +411 -411
  228. package/src/lib/token_store.ts +4 -5
  229. package/zcode/AGENTS.md +196 -195
  230. package/zcode/README.md +4 -0
  231. package/md_cg/whitebox_kb/wisdom/wisdom-book-cloud.db-shm +0 -0
  232. package/md_cg/whitebox_kb/wisdom/wisdom-book-cloud.db-wal +0 -0
@@ -42,8 +42,10 @@ DEFAULT_ROOT = os.path.join(
42
42
  AUDIT_TAIL = 20000
43
43
 
44
44
 
45
+ # 生效条件:无入参,datapath.mdcg_root() 返回真值时返回该值,导入或调用抛异常、或返回假值(如空串)时返回模块常量 DEFAULT_ROOT;
45
46
  def _default_root() -> str:
46
- """根解析沿用本仓约定:env `MDCG_ROOT` > `data/paths.json` > 插件仓 `data/mdcg`。
47
+ """根解析沿用本仓约定:env `MDCG_ROOT` > `paths.json`(用户级,旧包内兼容读)
48
+ > 用户级状态根 `data/mdcg`。
47
49
 
48
50
  走 `datapath.mdcg_root()`(兜底纪律:与其它工具同源解析,不另立一套)。
49
51
  """
@@ -115,6 +117,7 @@ NEW_TYPE_TOUCHPOINTS = {
115
117
  }
116
118
 
117
119
 
120
+ # 生效条件:root 下 INDEX_FILE 可读且 JSON 顶层为 dict 并含 nodes 字典时返回该 nodes;不可读抛 OSError、JSON 非法抛 JSONDecodeError、nodes 非 dict 抛 ValueError;
118
121
  def load_index(root: str) -> dict:
119
122
  """读索引快照(唯一必需数据源);损坏即抛——断言集不建立在猜测上。"""
120
123
  with open(os.path.join(root, INDEX_FILE), encoding="utf-8") as f:
@@ -125,6 +128,7 @@ def load_index(root: str) -> dict:
125
128
  return nodes
126
129
 
127
130
 
131
+ # 生效条件:path 不存在(os.path.exists 为假)时返回 [];存在时逐行解析,空行与 JSONDecodeError 行被跳过,返回可解析记录的列表(可能为 []);
128
132
  def _read_jsonl(path: str) -> list:
129
133
  if not os.path.exists(path):
130
134
  return []
@@ -141,10 +145,12 @@ def _read_jsonl(path: str) -> list:
141
145
  return out
142
146
 
143
147
 
148
+ # 生效条件:rec 的 tags 缺失或为假值时按空列表处理,仅对含 ":" 的 tag 取首个冒号前的前缀构成集合并返回;无此类 tag 时返回空集合;
144
149
  def _tag_prefixes(rec: dict) -> set:
145
150
  return {str(t).split(":", 1)[0] for t in (rec.get("tags") or []) if ":" in str(t)}
146
151
 
147
152
 
153
+ # 生效条件:r 的 verification_basis 为 list/tuple 时返回其真值元素的 str 列表;否则该值真值时返回 [str(b)],缺失或为假值(None/空串/空容器)时返回 [];
148
154
  def _basis_of(r: dict):
149
155
  b = r.get("verification_basis")
150
156
  if isinstance(b, (list, tuple)):
@@ -152,11 +158,13 @@ def _basis_of(r: dict):
152
158
  return [str(b)] if b else []
153
159
 
154
160
 
161
+ # 生效条件:无入参,返回 layer(取自导入的模块常量 LAYERS 的排序值)+ edge_type/derived_relation/verification_basis/lifecycle_state 五键声明值映射,后四键对应模块导入或属性取值失败、属性值为假值(空容器/None)时该键 declared=None;
155
162
  def enum_spaces() -> dict:
156
163
  """各类型空间的声明值(模块真源取不到 → declared=None,报告标 BLINDSPOT)。"""
157
164
  from .mdcg import LAYERS
158
165
  spaces = {"layer": {"declared": sorted(LAYERS), "source": "mdcg.LAYERS"}}
159
166
 
167
+ # 生效条件:以 md_cg.<mod> 导入并 getattr(attr)(导入或取值抛异常则 val=None),把 {"declared": sorted(val) if val else None, "source": "<mod>.<attr>"} 写入片段外闭包变量 spaces 的 key 键(val 为假值时 declared=None),无返回值;
160
168
  def _grab(mod: str, attr: str, key: str):
161
169
  try:
162
170
  m = __import__(f"md_cg.{mod}", fromlist=[attr])
@@ -175,6 +183,7 @@ def enum_spaces() -> dict:
175
183
 
176
184
  # ---------------------------- 指标 ----------------------------
177
185
 
186
+ # 生效条件:对 nodes 各记录按 str(content_hash) 计数,content_hash 缺失被折成 "None",返回出现次数 >1 且哈希不为 "None"/"" 的组数、涉及节点数与前 5 大组大小;
178
187
  def _dup_metrics(nodes: dict) -> dict:
179
188
  ch = Counter(str(r.get("content_hash")) for r in nodes.values())
180
189
  groups = {h: c for h, c in ch.items() if c > 1 and h not in ("None", "")}
@@ -182,6 +191,7 @@ def _dup_metrics(nodes: dict) -> dict:
182
191
  "largest": sorted(groups.values(), reverse=True)[:5]}
183
192
 
184
193
 
194
+ # 生效条件:对 nodes 中每个节点的 "edges" 真值且为 list/tuple 的边列表,仅遍历其中 dict 边;依据 CANONICAL_EDGE_KEYS 与 "type" 统计键组合、关系类型、总边数、非规范键样本(最多 5)和悬空边,返回汇总字典。
185
195
  def _edge_metrics(nodes: dict) -> dict:
186
196
  types, key_mix = Counter(), Counter()
187
197
  total = dangling = with_edges = non_canonical = 0
@@ -210,6 +220,7 @@ def _edge_metrics(nodes: dict) -> dict:
210
220
  "non_canonical": non_canonical, "dangling": dangling, "samples": samples}
211
221
 
212
222
 
223
+ # 生效条件:遍历 nodes 的值作为记录,path 经 str(r.get("path") or "") 为空即 missing++ 并 continue,不以记录含 layer/path 为前置;仅对非空 path 执行 layer 与 head 检查(layer 非空且 head != layer 且 head in LAYER_DIRS 时 layer_mismatch++),且 check_exists 为 True 时才对非空 path 以 os.path.join(root, rel) 判断不存在并计入 missing,最终返回 missing/layer_mismatch/samples 统计;。
213
224
  def _path_metrics(root: str, nodes: dict, check_exists: bool) -> dict:
214
225
  missing = mismatch = 0
215
226
  samples = []
@@ -233,6 +244,7 @@ def _path_metrics(root: str, nodes: dict, check_exists: bool) -> dict:
233
244
  return {"missing": missing, "layer_mismatch": mismatch, "samples": samples}
234
245
 
235
246
 
247
+ # 生效条件:root 下 DECISION_LOG/INBOX_LOG 不存在时按空列表计,返回 decisions(decision 或 status 回落 "?" 的前 10 项)、decisions_total、inbox_pending=max(0, 收件数−决策数)、以及 _audit.jsonl 末尾 AUDIT_TAIL 行内 op 计数的前 6 项与尾窗元信息;
236
248
  def _gate_metrics(root: str) -> dict:
237
249
  dec = _read_jsonl(os.path.join(root, DECISION_LOG))
238
250
  inbox = _read_jsonl(os.path.join(root, INBOX_LOG))
@@ -259,6 +271,7 @@ def _gate_metrics(root: str) -> dict:
259
271
  "audit_tail_window": AUDIT_TAIL}
260
272
 
261
273
 
274
+ # 生效条件:对 root 与 nodes,若 root/ACCESS_LOG 路径不存在则返回 {"distinct": None, "ratio": None, "lines": None};否则逐行解析 JSON,当 "ids" 为 list 或 tuple 时把其中真值元素 str 后加入 seen,且当 "id"/"node_id"/"nid" 任一真值时把其 str 加入 seen,返回 distinct 为 seen 与 nodes 键交集数量、ratio 为 distinct/max(len(nodes),1)、lines 为非空行数。
262
275
  def _reach_metrics(root: str, nodes: dict) -> dict:
263
276
  p = os.path.join(root, ACCESS_LOG)
264
277
  if not os.path.exists(p):
@@ -285,6 +298,7 @@ def _reach_metrics(root: str, nodes: dict) -> dict:
285
298
  return {"distinct": hit, "ratio": hit / max(len(nodes), 1), "lines": lines}
286
299
 
287
300
 
301
+ # 生效条件:对 nodes,筛出 layer 字符串为 "knowledge" 的 kn;mixed 为 kn 中标签前缀含 doc 或 code 的数量;返回全库 nodes 数与 knowledge 层数、mixed、mixed_ratio=mixed/max(len(kn),1),以及全库口径 role_ratio/basis_ratio/evidence_ratio/state_ratio 和 knowledge 层口径 role_ratio_kn/basis_ratio_kn/evidence_ratio_kn。
288
302
  def _coverage_metrics(nodes: dict) -> dict:
289
303
  """混层口径 + 字段覆盖率(9-15 审计基线口径,逐项可复现)。
290
304
 
@@ -311,6 +325,7 @@ def _coverage_metrics(nodes: dict) -> dict:
311
325
  if _as_int(r.get("evidence_count")) > 0) / nk}
312
326
 
313
327
 
328
+ # 生效条件:int(v) 转换成功时返回该整数;抛 TypeError/ValueError(如 None、非数字串)时返回 0;
314
329
  def _as_int(v) -> int:
315
330
  try:
316
331
  return int(v)
@@ -320,6 +335,7 @@ def _as_int(v) -> int:
320
335
 
321
336
  # ---------------------------- G3 · unanalyzed 派生集合 ----------------------------
322
337
 
338
+ # 生效条件:仅当记录 _as_int(evidence_count)==0 且 verification_basis 为空且 lifecycle_state 为假值时计入 ids,返回 count、ratio=count/max(len(nodes),1) 与前 5 个 id 样本;
323
339
  def unanalyzed(nodes: dict) -> dict:
324
340
  """G3:unanalyzed **派生**判据(不新增字段、零写入)。
325
341
 
@@ -342,6 +358,7 @@ def unanalyzed(nodes: dict) -> dict:
342
358
 
343
359
  # ---------------------------- G1 · 类型空间正交性审计 ----------------------------
344
360
 
361
+ # 生效条件:调用 _usage(nodes, edges) 时 edges 形参未被源码引用,对 nodes.values() 中每个节点 r,仅当 r.get("layer")、r.get("role")、r.get("derived_relation")、r.get("lifecycle_state") 为真值时分别以 str 值计入对应 Counter,_basis_of(r) 与 _tag_prefixes(r) 展开的元素分别计入 verification_basis 与 tag_prefix,再遍历各 r 的 r.get("edges") or [] 中 isinstance(e, dict) 的项按 edge_rel(e) 计数,最终 edge_type 仅保留键为真值的计数,返回 used 字典;
345
362
  def _usage(nodes: dict, edges: dict) -> dict:
346
363
  """各类型空间的**实测**取值(与 enum_spaces 的声明值对照)。"""
347
364
  from .chain import edge_rel
@@ -364,6 +381,7 @@ def _usage(nodes: dict, edges: dict) -> dict:
364
381
  return used
365
382
 
366
383
 
384
+ # 生效条件:以 enum_spaces 的声明集与 _usage 的实测集逐空间对照(closed 仅在 declared 非 None 且无未声明已用时为 True),role 与 tag_prefix 两空间的 declared/closed 恒为 None,另附 _cross_space_overlap(字面量出现在 ≥2 空间或 layer∩tag_prefix)及 KNOWN_DIRECTION_CONFLICTS/VERIFIED_CONSISTENT/DOC_CODE_DRIFT/NEW_TYPE_TOUCHPOINTS 四个常量;
367
385
  def _g1_audit(nodes: dict, edges: dict) -> dict:
368
386
  """G1:声明值 / 实测值 / 未声明已用 / 声明未用 / 跨空间重叠 / 方向口径分歧。"""
369
387
  spaces = enum_spaces()
@@ -401,10 +419,12 @@ def _g1_audit(nodes: dict, edges: dict) -> dict:
401
419
 
402
420
  # ---------------------------- 断言集 ----------------------------
403
421
 
422
+ # 生效条件:四参齐备时返回 {"id": cid, "level": level, "ok": bool(ok), "detail": detail},ok 经 bool() 归一为布尔;
404
423
  def _ck(cid: str, level: str, ok: bool, detail: str) -> dict:
405
424
  return {"id": cid, "level": level, "ok": bool(ok), "detail": detail}
406
425
 
407
426
 
427
+ # 生效条件:调用 check(root, check_paths, baseline, strict) 时,root 原样传入 load_index 得 nodes,check_paths 原样传入 _path_metrics 并在 index.path.present 检查消息中当其为假时追加“(--no-path-check 未查盘)”,baseline 为真时追加 _regression 基线不劣化检查、为假(None 或空 dict)时跳过,n=len(nodes) 小于 THRESHOLDS["min_nodes"] 时 small 为真且使 _ratio 各项及 reach.ratio、stratum.mixed_ratio 在 val 为 None 或 small 为真时记 BLINDSPOT、否则记 WARN,最终 verdict 为 FAIL(存在 level="FAIL" 且 ok 为假)、否则 WARN(存在 level="WARN" 且 ok 为假,或 strict 为真且存在 level="BLINDSPOT")、否则 PASS,返回含 REPORT_VERSION、t、elapsed_s、root 绝对路径、verdict、nodes、checks、counts、coverage、dup、edges、paths、gate、reach、unanalyzed、g1、protocol(=protocol.audit() 静态对账,恒追加四条 FAIL 级断言:op.declared 无未登记分支 / verb.implemented live 动词均有实现分支 / verb.reserved_clean 预留动词未被静默实现 / shape.declared 形状声明完备)、thresholds 的 dict;
408
428
  def check(root: str, *, check_paths: bool = True,
409
429
  baseline: dict = None, strict: bool = False) -> dict:
410
430
  """跑一遍全部断言,返回报告 dict(零写入:不修任何数据、不落任何文件)。"""
@@ -452,6 +472,7 @@ def check(root: str, *, check_paths: bool = True,
452
472
  f" / 最大组 {dup['largest'][:3]}(最大组上限 {DUP_GROUP_MAX})"))
453
473
 
454
474
  # ---- 健康指标(只告警)----
475
+ # 生效条件:val 为 None 或片段外闭包布尔 small 为真(small、n、T 均未在本片段定义)时追加 level="BLINDSPOT" 项;否则追加 level="WARN"、ok=(val>=low) 的项;两分支均无返回值;
455
476
  def _ratio(cid, val, low, name):
456
477
  if small or val is None:
457
478
  checks.append(_ck(cid, "BLINDSPOT", True,
@@ -478,6 +499,33 @@ def check(root: str, *, check_paths: bool = True,
478
499
  f"闸门裁决样本 {gate['decisions_total']} 条 {gate['decisions']}"
479
500
  f"(下限 {T['gate_sample_min']})"))
480
501
 
502
+ # ---- 记忆动词协议 v1 静态对账(声明 ↔ MCP 面实现,纯源码事实、不连库)----
503
+ # 与 G1 的分工:G1 对账**数据值域**,这里对账**动词面**——两者都是
504
+ # 「声明了没做 / 做了没说」的前置红灯,属结构性不变量(FAIL 级,fail-closed)。
505
+ from . import protocol as _proto
506
+ pa = _proto.audit()
507
+ checks.append(_ck("protocol.op.extension_surface", "WARN", True,
508
+ f"协议 v{pa['protocol_version']} 冻结面={pa['declared']}"
509
+ f"(live {len(pa['live'])} / reserved {len(pa['reserved'])});"
510
+ f"MCP 面另有 {len(pa['extension_ops'])} 个扩展 op 不属协议面"
511
+ f"(文档一致性由 cogmap 门禁承担)"))
512
+ checks.append(_ck("protocol.verb.implemented", "FAIL",
513
+ not pa["missing_impl"],
514
+ f"声明为 live 的动词 {pa['live']} 均有实现分支"
515
+ if not pa["missing_impl"] else
516
+ f"声明为 live 却无实现分支:{pa['missing_impl']}"))
517
+ checks.append(_ck("protocol.verb.reserved_clean", "FAIL",
518
+ not pa["reserved_leaked"],
519
+ f"reserved 动词 {pa['reserved']} 未出现实现分支"
520
+ if not pa["reserved_leaked"] else
521
+ f"reserved 动词被静默实现(须先改 status=live):"
522
+ f"{pa['reserved_leaked']}"))
523
+ checks.append(_ck("protocol.shape.declared", "FAIL",
524
+ not pa["shape_errors"],
525
+ f"{len(pa['declared'])} 个动词形状声明完备"
526
+ if not pa["shape_errors"] else
527
+ f"形状声明不完整:{pa['shape_errors']}"))
528
+
481
529
  # ---- 基线不劣化(有 baseline 时才可判)----
482
530
  if baseline:
483
531
  checks += _regression({"mixed_ratio": cov["mixed_ratio"],
@@ -497,9 +545,10 @@ def check(root: str, *, check_paths: bool = True,
497
545
  "total": len(checks)},
498
546
  "coverage": cov, "dup": dup, "edges": edges, "paths": paths,
499
547
  "gate": gate, "reach": reach, "unanalyzed": un, "g1": g1,
500
- "thresholds": T}
548
+ "protocol": pa, "thresholds": T}
501
549
 
502
550
 
551
+ # 生效条件:对 cur 各键,baseline(或其 metrics)缺失、该键在基线为 None 或 cur 值为 None 时跳过;否则以 val > b+1e-9 判劣化,产出 level 恒为 "FAIL"、ok=not worse 的检查项;
503
552
  def _regression(cur: dict, baseline: dict) -> list:
504
553
  """与基线比对:单调量只准不变或改善(重复度/悬空/非规范键/混层/unanalyzed)。
505
554
 
@@ -519,6 +568,7 @@ def _regression(cur: dict, baseline: dict) -> list:
519
568
  return out
520
569
 
521
570
 
571
+ # 生效条件:rep 含 coverage/edges/dup/unanalyzed/gate/reach 子字典与 nodes 键时(全部按下标取值,缺键即抛 KeyError)返回扁平指标映射;
522
572
  def metrics_of(rep: dict) -> dict:
523
573
  """抽成可比对的扁平指标(--baseline 的写入面)。"""
524
574
  return {"mixed_ratio": rep["coverage"]["mixed_ratio"],
@@ -538,6 +588,7 @@ def metrics_of(rep: dict) -> dict:
538
588
 
539
589
  # ---------------------------- 报告 ----------------------------
540
590
 
591
+ # 生效条件:rep 各键齐备(按下标取值,任一键缺失抛 KeyError)时拼接为多行文本,reach.ratio 为 None 时渲染 "N/A",g1 中以 "_" 开头的键在遍历中被跳过;
541
592
  def render(rep: dict) -> str:
542
593
  L = []
543
594
  a = L.append
@@ -573,6 +624,17 @@ def render(rep: dict) -> str:
573
624
  f"/{rep['gate']['audit_tail_window']} 行)={rep['gate']['audit_ops']}")
574
625
  a(f" G3 unanalyzed 派生={rep['unanalyzed']['count']}"
575
626
  f" ({rep['unanalyzed']['ratio'] * 100:.1f}%)")
627
+ pr = rep.get("protocol") or {}
628
+ if pr:
629
+ a("\n-- 记忆动词协议 v1 静态对账 --")
630
+ a(f" {pr['doc']} frozen={pr['frozen']} ok={pr['ok']}")
631
+ a(f" 声明={pr['declared']}")
632
+ a(f" live={pr['live']} reserved={pr['reserved']}(未实现即如实登记,不冒充)")
633
+ a(f" MCP 面 op 分支={len(pr['actual'])} 个,其中扩展能力面"
634
+ f"(不属协议 v1){len(pr['extension_ops'])} 个")
635
+ if pr["errors"]:
636
+ for e in pr["errors"]:
637
+ a(f" !! {e}")
576
638
  a("\n-- G1 类型空间正交性 --")
577
639
  for name, spec in rep["g1"].items():
578
640
  if name.startswith("_"):
@@ -591,6 +653,7 @@ def render(rep: dict) -> str:
591
653
  return "\n".join(L)
592
654
 
593
655
 
656
+ # 生效条件:传入对象可写属性时执行赋值 sustain_module.conformance_summary = report_summary,无返回值(None);
594
657
  def register_sustain(sustain_module) -> None: # pragma: no cover
595
658
  """挂点说明(供 sustain 侧最小侵入调用)。
596
659
 
@@ -601,6 +664,7 @@ def register_sustain(sustain_module) -> None: # pragma: no cover
601
664
  sustain_module.conformance_summary = report_summary
602
665
 
603
666
 
667
+ # 生效条件:root 依次取 cg_or_root.root 属性、cg_or_root 本身、_default_root()(前者为假值即回落,故 0/空串会继续回落);check 抛异常时返回 ok=False、verdict="BLINDSPOT"、error,否则返回 ok=(verdict!="FAIL") 与 fail/warn/blindspot/failed_ids/t;
604
668
  def report_summary(cg_or_root) -> dict:
605
669
  """给常驻循环用的**轻量**结论(不渲染全文、不写盘):verdict + 计数。"""
606
670
  root = getattr(cg_or_root, "root", None) or cg_or_root or _default_root()
@@ -616,11 +680,12 @@ def report_summary(cg_or_root) -> dict:
616
680
  "t": rep["t"]}
617
681
 
618
682
 
683
+ # 生效条件:argv 为 None 时 argparse 从 sys.argv 取值,--root 缺失或为假值回落 _default_root();check 抛 OSError/ValueError(如索引不可读)时打印 BLINDSPOT 并返回 2,否则按 --write-baseline/--json 写盘后返回 0(verdict≠"FAIL")或 1("FAIL");
619
684
  def main(argv=None) -> int:
620
685
  p = argparse.ArgumentParser(prog="python -m md_cg.conformance",
621
686
  description="数据健康不变量断言集(只读,零写入)")
622
687
  p.add_argument("--root", default=None,
623
- help="认知图根(默认 MDCG_ROOT > data/paths.json > 插件仓 data/mdcg)")
688
+ help="认知图根(默认 MDCG_ROOT > paths.json > 用户级状态根 data/mdcg)")
624
689
  p.add_argument("--json", dest="json_out", default=None)
625
690
  p.add_argument("--baseline", default=None, help="基线报告 json(比对不劣化)")
626
691
  p.add_argument("--write-baseline", default=None, help="把本次指标写成新基线")
@@ -659,4 +724,4 @@ def main(argv=None) -> int:
659
724
  if __name__ == "__main__":
660
725
  if hasattr(sys.stdout, "reconfigure"):
661
726
  sys.stdout.reconfigure(encoding="utf-8", errors="replace")
662
- sys.exit(main())
727
+ sys.exit(main())
@@ -81,9 +81,11 @@ DISCIPLINE_TAGS = ("discipline", "纪律", "work_discipline", "rule", "规则",
81
81
  VERDICTS = ("ACCEPT", "REJECT", "DEFER", "BLINDSPOT")
82
82
 
83
83
 
84
+ # 生效条件:以 verdict 与 reason 构造异常(消息 `[{verdict}] {reason}`),conflicts 传假值(None/空容器等,源码 `conflicts or []`)时 self.conflicts 为 [],传真值时原样保留;
84
85
  class ConsistencyError(Exception):
85
86
  """硬冲突:写入被拒(自否定 / 违反纪律)。"""
86
87
 
88
+ # 生效条件:verdict 与 reason 必传并赋给同名属性、拼成异常消息 `[{verdict}] {reason}`;conflicts 为假值(None/空容器等,源码 `conflicts or []`)时 self.conflicts 回落到 [],为真值时原样保留;
87
89
  def __init__(self, verdict, reason, conflicts=None):
88
90
  self.verdict = verdict
89
91
  self.reason = reason
@@ -95,6 +97,7 @@ class ConsistencyError(Exception):
95
97
  # 原语:延迟导入 mdcos(避免 mdcg ← mdcos ← consistency 的循环导入)
96
98
  # --------------------------------------------------------------------------
97
99
 
100
+ # 生效条件:无 required 形参,任一调用返回 ( _ccg_field, _declared_conditions, _neg_hit, _weighted_coverage ) 四元组;
98
101
  def _prims():
99
102
  """取 md_cg 已有的条件匹配原语(条件论「反题」的既有实现)。"""
100
103
  from .mdcos import (_ccg_field, _declared_conditions, _neg_hit,
@@ -102,6 +105,7 @@ def _prims():
102
105
  return _ccg_field, _declared_conditions, _neg_hit, _weighted_coverage
103
106
 
104
107
 
108
+ # 生效条件:xs 为可迭代对象时,逐项 str(x).strip() 后仅当结果非空且未出现过才加入 out 并返回 out;xs 为 None/False/0 时在 for 处迭代失败(TypeError);
105
109
  def _dedup(xs):
106
110
  out = []
107
111
  for x in xs:
@@ -111,6 +115,7 @@ def _dedup(xs):
111
115
  return out
112
116
 
113
117
 
118
+ # 生效条件:fm 给出时,遍历 fm.get("edges")(缺键或假值按空列表)中元素,dict 元素按 to→target→node→id 取首个真值、非 dict 元素直接作候选,候选真值时 str(t) 加入 out 并返回;
114
119
  def _edge_targets(fm):
115
120
  """节点声明的出边目标(兼容 dict / str 两种形态)。"""
116
121
  out = []
@@ -124,6 +129,7 @@ def _edge_targets(fm):
124
129
  return out
125
130
 
126
131
 
132
+ # 生效条件:fm 与 node_id 给出时,若 fm.get("tags")(缺键/假值按空)小写后与 DISCIPLINE_TAGS 有交集,或 str(node_id) 以 "discipline_" 或 "work_discipline" 开头,返回 True;否则返回 False;
127
133
  def _is_discipline(fm, node_id):
128
134
  tags = {str(t).lower() for t in (fm.get("tags") or [])}
129
135
  if tags & set(DISCIPLINE_TAGS):
@@ -139,6 +145,7 @@ def _is_discipline(fm, node_id):
139
145
  SEPARATION_REL = "distinct_from"
140
146
 
141
147
 
148
+ # 生效条件:fm 给出时,仅处理 fm.get("edges")(缺键/假值按空)中的 dict 元素,relation_type 或 relation(缺省空串)小写等于 SEPARATION_REL 时,按 target→to→id 取首个真值且未重复则加入 out;
142
149
  def separation_targets(fm):
143
150
  """节点已声明的分离对象(`distinct_from` 出边),用于幂等与去重。"""
144
151
  out = []
@@ -154,6 +161,7 @@ def separation_targets(fm):
154
161
  return out
155
162
 
156
163
 
164
+ # 生效条件:fm 与 content 给出时,以 content(假值按空串)、fm.get("condition_space")、fm.get("non_applicable_conditions") 解析,返回去重后的 (set(pos), set(neg));
157
165
  def condition_terms(fm, content):
158
166
  """节点声明的(正条件, 负条件)词面集合——分离判定与重构取线索共用。"""
159
167
  pos, neg = _new_terms(content or "", fm.get("condition_space"),
@@ -161,6 +169,7 @@ def condition_terms(fm, content):
161
169
  return set(pos), set(neg)
162
170
 
163
171
 
172
+ # 生效条件:pos_a、neg_a、pos_b、neg_b 给出的条件词面小写化后,两侧并集均空时返回 overlap 0.0、distinct False;否则计算交集/并集比并返回 distinct 为 overlap <= CLASH_LOW;
164
173
  def condition_distinct(pos_a, neg_a, pos_b, neg_b):
165
174
  """两组条件是否「实质不同」:任一侧条件词面几乎不重合即为不同情境。
166
175
 
@@ -177,6 +186,7 @@ def condition_distinct(pos_a, neg_a, pos_b, neg_b):
177
186
  return {"overlap": round(overlap, 4), "distinct": overlap <= CLASH_LOW}
178
187
 
179
188
 
189
+ # 生效条件:content、condition_space、non_applicable_conditions 给出时,content 的 `生效条件` 字段与 condition_space 中键不以 `__` 开头且不为 `time_window` 的值并入 pos(list/tuple/set 展平,其他非 None/"" 值 str 化),non_applicable_conditions 各项及 content 的 `不适用条件` 字段值并入 neg,最后 _dedup 返回;
180
190
  def _new_terms(content, condition_space, non_applicable_conditions):
181
191
  """新节点声明的(正条件, 负条件)——与既有节点同口径解析。"""
182
192
  _ccg_field, _declared, _neg_hit, _cov = _prims()
@@ -198,6 +208,7 @@ def _new_terms(content, condition_space, non_applicable_conditions):
198
208
  return _dedup(pos), _dedup(neg)
199
209
 
200
210
 
211
+ # 生效条件:content 与 neg_texts 给出时,neg_texts 中任一短语去掉「不得/禁止/严禁/不能/不可/不要/勿」前缀且长度 >=2 后作为子串出现在 content(假值按空串)中则返回 True,否则 False;
201
212
  def _ban_hit(content, neg_texts):
202
213
  """纪律禁令命中:去掉「不得/禁止/…」前缀后,短语是否**整体出现**在正文中。
203
214
 
@@ -215,6 +226,7 @@ def _ban_hit(content, neg_texts):
215
226
  return False
216
227
 
217
228
 
229
+ # 生效条件:content 为真时按行过滤,跳过 strip 后以 "#" 开头且含全角或半角冒号的行,其余原行以换行连接返回;content 假值按空串返回 "";
218
230
  def _body_text(content):
219
231
  """去掉 CCG 声明行(`# 字段:值`)后的正文。
220
232
 
@@ -231,6 +243,7 @@ def _body_text(content):
231
243
  return "\n".join(out)
232
244
 
233
245
 
246
+ # 生效条件:content 给出时(假值按空串)解析 CCG 的 `# 功能名` 与 `# 子功能` 字段并返回两者;
234
247
  def _slot_text(content):
235
248
  """结论槽:CCG 声明的 `# 功能名` / `# 子功能`(结构字段,非正文词面)。
236
249
 
@@ -250,6 +263,7 @@ def _slot_text(content):
250
263
  # L0 情绪通道(信息差二阶变化)
251
264
  # --------------------------------------------------------------------------
252
265
 
266
+ # 生效条件:conflict_strength 被夹取到 [0,1](假值按 0.0),prev_strength 为 None 时 d2=0.0,否则 d2=c-夹取后的 prev_strength;按 c>=EMO_AVOID 或 d2>0.2 返回 avoiding,或 c<=EMO_APPROACH 且 d2<=0 返回 approaching,否则 stable;
253
267
  def emotional_bias(conflict_strength, prev_strength=None):
254
268
  """L0:把冲突强度映射为情绪倾向(approaching / stable / avoiding)。
255
269
 
@@ -279,6 +293,7 @@ def emotional_bias(conflict_strength, prev_strength=None):
279
293
  "note": "情绪通道独立,不参与信任/资格计算(智能论 §十一)"}
280
294
 
281
295
 
296
+ # 生效条件:cg 给出时,若 cg.root 下 LOG_FILE 可读,则逐行解析 JSON 并取 .get("conflict_strength"),返回最后一条可解析记录的该键值(缺键或值为 None 则为 None);路径不可用或 OSError 返回 None;
282
297
  def _last_strength(cg):
283
298
  """上一条留痕的冲突强度(二阶差分的基线)。流式读,不载全量。"""
284
299
  p = os.path.join(cg.root, LOG_FILE)
@@ -304,6 +319,7 @@ def _last_strength(cg):
304
319
  # L2 递归反思(受深度 / 节点数 / 循环 / 增益门槛约束)
305
320
  # --------------------------------------------------------------------------
306
321
 
322
+ # 生效条件:以 cg 节点图、seeds 中非空项为初始 frontier、tw_pos/tw_neg 词权,在 d=1..int(max_depth) 每轮先判 frontier 空返回 frontier_exhausted,再逐 nid 处理时若 nodes_visited > max_nodes 先返回 node_budget(可先于同层 discriminators),否则处理完该层后有 discriminators(非 seed_set 节点声明条件与 tw_pos/tw_neg 覆盖 >= CLASH_HIGH)返回 resolved,否则 last_gain < min_gain 返回 gain_below_threshold,循环耗尽返回 depth_exceeded;seeds 假值时 frontier 空,在 d=1 进入循环后由首判返回 frontier_exhausted(depth=0);
307
323
  def _recursive_reflect(cg, seeds, tw_pos, tw_neg, max_depth=MAX_DEPTH,
308
324
  max_nodes=MAX_NODES, min_gain=MIN_GAIN):
309
325
  """递归反思:沿关系链找「区分条件」,每层检查信息增益。
@@ -366,6 +382,7 @@ def _recursive_reflect(cg, seeds, tw_pos, tw_neg, max_depth=MAX_DEPTH,
366
382
  # 主入口:三级决策
367
383
  # --------------------------------------------------------------------------
368
384
 
385
+ # 生效条件:以 cg.index.nodes 为既有节点、content(假值按 "")经 _new_terms 得 pos/neg 并算 tw_pos/tw_neg,按循环中 hard(自否定或纪律命中)→ divergences(同条件槽且 concl < CONCLUSION_SAME)→ strength ≥ CLASH_HIGH → strength ≥ CLASH_LOW → comparable==0 且 (pos or neg) → 否则 ACCEPT 的顺序定 verdict;DEFER 且 int(depth)>0 且 emo["bias"] != "approaching" 时调 _recursive_reflect 补 recursion,auto_flywheel 且 verdict∈{REJECT,DEFER,BLINDSPOT} 时加 unresolved_id,最后 log 并返回 rec;
369
386
  def check(cg, content, layer=None, condition_space=None,
370
387
  non_applicable_conditions=None, tags=None, exclude=None,
371
388
  limit=MAX_SCAN, depth=MAX_DEPTH, auto_flywheel=False,
@@ -554,6 +571,7 @@ def check(cg, content, layer=None, condition_space=None,
554
571
  return rec
555
572
 
556
573
 
574
+ # 生效条件:missing 为假值(None/空容器/空串)返回 "";否则逐项仅取 m.get("need") 真值者,格式化为 `need(why)`(why 缺键用 "",值为 None 则格式化 "None"),以 ";" 连接;
557
575
  def _missing_text(missing):
558
576
  """结构化缺口 → 一行文本(作为飞轮的 known_clues)。
559
577
 
@@ -567,6 +585,7 @@ def _missing_text(missing):
567
585
  for m in missing if m.get("need"))
568
586
 
569
587
 
588
+ # 生效条件:cg 提供可调用 flywheel_step 时,以 query(假值按空串)前 200 字符、verdict、missing 文本(空则 reason)、conflicts 非 None 则作 detail 否则 missing 调用;结果 dict 的 unresolved_id 真值时返回它,否则回落 id;无 flywheel_step 或异常返回 None;
570
589
  def _fire_flywheel(cg, query, verdict, reason, missing, conflicts=None):
571
590
  """把冲突作为「误差」投给知识飞轮,返回 unresolved 条目 id(失败不阻塞写入)。
572
591
 
@@ -592,6 +611,7 @@ def _fire_flywheel(cg, query, verdict, reason, missing, conflicts=None):
592
611
  # 留痕 / 统计 / 自描述
593
612
  # --------------------------------------------------------------------------
594
613
 
614
+ # 生效条件:cg 与 rec 给出时,将 rec 序列化为 JSON 行追加到 cg.root 下 LOG_FILE,OSError 被吞掉,返回 rec;
595
615
  def log(cg, rec):
596
616
  """append-only 留痕:每条判定可审计。"""
597
617
  p = os.path.join(cg.root, LOG_FILE)
@@ -603,6 +623,7 @@ def log(cg, rec):
603
623
  return rec
604
624
 
605
625
 
626
+ # 生效条件:cg 给出时,若 cg.root 下 LOG_FILE 可读,则逐行解析 JSON(空行/解析失败跳过);limit 真值时返回 out[-int(limit):][::-1](int(limit)=0 时为全部倒序),limit 为假值(0/None/"")时返回 out[::-1];路径不可用或 OSError 返回 [];
606
627
  def history(cg, limit=100):
607
628
  """最近冲突判定留痕(倒序)。"""
608
629
  p = os.path.join(cg.root, LOG_FILE)
@@ -624,6 +645,7 @@ def history(cg, limit=100):
624
645
  return out[-int(limit):][::-1] if limit else out[::-1]
625
646
 
626
647
 
648
+ # 生效条件:逐行 json.loads 计数(不可解析行跳过、OSError 忽略、路径缺失则计数为 0),每行 total 加 1 并按 r.get("verdict")(缺键即 None 键)与 emotional.bias 为真值时的 b 累加,返回 {'records','by_verdict','by_bias','max_scan','max_depth','min_gain'},后三者取模块常量 MAX_SCAN/MAX_DEPTH/MIN_GAIN;
627
649
  def summary(cg):
628
650
  """冲突面汇总(流式计数,供 health 审计)。"""
629
651
  p = os.path.join(cg.root, LOG_FILE)
@@ -652,6 +674,7 @@ def summary(cg):
652
674
  "min_gain": MIN_GAIN}
653
675
 
654
676
 
677
+ # 生效条件:无 required 形参,调用即返回引用 EMO_AVOID/EMO_APPROACH、VERDICTS、CLASH_HIGH/CLASH_LOW、SAME_COND_HIGH/SLOT_HIGH/CONCLUSION_SAME、MAX_DEPTH/MAX_NODES/MIN_GAIN 等模块级常量的自描述字典;
655
678
  def catalog():
656
679
  """自描述:三级决策 + 四态 + 递归约束(供 MCP / 文档对照验证)。"""
657
680
  return {
@@ -692,4 +715,4 @@ def catalog():
692
715
  "discipline_detection": {"tags": list(DISCIPLINE_TAGS),
693
716
  "id_prefixes": ["discipline_", "work_discipline"]},
694
717
  "honest_boundary": "条件级(结构化)冲突检测,非语义蕴含证明;无法比对时 BLINDSPOT",
695
- }
718
+ }