@furongjun1999/dsh-memory 0.4.11 → 0.5.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 (580) hide show
  1. package/README.md +552 -465
  2. package/codebuddy/CODEBUDDY.md +11 -3
  3. package/codebuddy/README.md +92 -90
  4. package/codebuddy/mcp.json +27 -27
  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 -169
  6. package/docs/Pi/345/217/257/345/255/246/344/271/240/344/274/230/347/202/271_/347/201/265/346/236/242/345/244/247/350/204/221/346/224/271/350/277/233/344/272/244/346/216/245_20260915.md +144 -144
  7. package/docs/README.md +143 -111
  8. package/docs/discipline/harnesses.yaml +244 -226
  9. package/docs/discipline/templates/full.md.tmpl +61 -61
  10. package/docs/discipline/templates/rules.mdc.tmpl +68 -0
  11. package/docs/discipline/templates/skill.md.tmpl +23 -23
  12. package/docs/eval/AGI/344/270/203/347/273/264/350/257/204/345/210/206/346/212/245/345/221/212_md_cg_v1.0.md +299 -299
  13. package/docs/eval/AGI/344/270/203/347/273/264/350/257/204/345/210/206/346/212/245/345/221/212_md_cg_v2.0.md +239 -239
  14. package/docs/eval/bench_lingshu_self/bench_self.py +140 -0
  15. package/docs/eval/bench_lingshu_self/self_bench_result.json +404 -0
  16. package/docs/eval/bench_lingshu_self//347/201/265/346/236/242/350/207/252/345/272/223/347/253/257/345/210/260/347/253/257/346/243/200/347/264/242/345/256/236/346/265/213_v1.0.md +38 -0
  17. package/docs/eval//344/270/215/345/217/257/351/235/240/346/200/247/350/220/275/345/234/260_P0_v1.0.md +185 -0
  18. package/docs/eval//345/256/236/351/252/214/346/226/271/346/241/210_/345/255/246/344/271/240/351/227/255/347/216/257AB/344/270/216/346/250/252/350/257/204_v1.0.md +174 -174
  19. package/docs/eval//346/225/205/351/232/234/346/263/250/345/205/245/345/256/236/346/265/213_v1.0.md +422 -0
  20. package/docs/eval//346/250/252/350/257/204_/345/205/255/345/256/266100/351/242/230/344/270/255/350/213/261/345/217/214/346/237/245_v1.0.md +223 -223
  21. package/docs/eval//347/253/257/345/210/260/347/253/257LoCoMoQA/345/220/214/345/217/243/345/276/204/345/257/271/347/205/247_v1.0.md +100 -0
  22. package/docs/eval//347/253/257/345/210/260/347/253/257/345/271/262/346/211/260/346/261/240/350/257/204/346/265/213_/347/241/256/345/256/232/346/200/247/350/243/201/345/206/263vsLLM_judge_v1.1.md +197 -0
  23. 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_v1.md +156 -0
  24. 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_v10.md +210 -0
  25. 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_v11.md +227 -0
  26. 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_v12.md +203 -0
  27. 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_v13.md +233 -0
  28. 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_v14.md +191 -0
  29. 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_v15.md +213 -0
  30. 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_v16.md +214 -0
  31. 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_v17.md +199 -0
  32. 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_v2.md +156 -0
  33. 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_v3.md +152 -0
  34. 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_v4.md +128 -0
  35. 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_v5.md +114 -0
  36. 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_v6.md +192 -0
  37. 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_v7.md +187 -0
  38. 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_v8.md +207 -0
  39. 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_v9.md +203 -0
  40. package/docs/{mdcg → hive}//344/273/244/347/211/214/344/270/216/350/247/222/350/211/262/346/235/203/350/201/214/345/210/206/347/246/273_v0.1.md +165 -160
  41. package/docs/hive//344/273/244/347/211/214/350/257/255/344/271/211/344/277/256/346/255/243_/344/273/262/350/243/201/344/275/215_v0.1.md +76 -0
  42. package/docs/{mdcg → hive}//345/255/220/344/273/243/347/220/206/351/205/215/347/275/256/346/240/207/345/207/206_v0.5.md +236 -236
  43. package/docs/hive//345/256/211/345/205/250/345/256/241/350/256/241/345/256/236/351/224/232_v0.1.md +202 -0
  44. 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 -43
  45. package/docs/hive//346/243/200/347/264/242/347/256/227/346/263/225/345/217/243/345/276/204/345/257/271/347/205/247_v0.1.md +85 -0
  46. 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 -102
  47. 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 +47 -0
  48. 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 +519 -503
  49. package/docs/hive//350/234/202/345/267/242/345/267/245/344/275/234/350/256/260/345/277/206_/351/241/271/347/233/256/350/256/241/345/210/222.md +85 -85
  50. package/docs/hive//350/234/202/345/267/242/350/256/276/350/256/241_/347/220/206/350/256/272/345/257/271/351/275/220_v0.1.md +191 -0
  51. package/docs/hive//350/234/202/345/267/242/350/277/255/344/273/243_/345/256/217/350/247/202/344/270/216/347/276/244/344/275/223/350/260/203/345/272/246_v0.1.md +90 -0
  52. package/docs/images/lingshu-moonlight-covenant-poster-preview.jpg +0 -0
  53. package/docs/images/lingshu-moonlight-covenant-poster.png +0 -0
  54. package/docs/mdcg/D_meta_/345/267/245/347/250/213/345/214/226/346/226/271/346/241/210_v0.2.md +216 -216
  55. package/docs/mdcg/README/350/257/246/347/273/206/347/211/210_v0.4.10.md +646 -646
  56. package/docs/mdcg/lingshu_tutorial.html +14449 -14449
  57. package/docs/mdcg/release_v0.4.11.md +49 -0
  58. package/docs/mdcg/release_v0.4.5.md +55 -55
  59. package/docs/mdcg/tool_table_v0.3.0.md +117 -117
  60. 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 -600
  61. 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
  62. package/docs/mdcg//345/215/225/345/205/203/350/207/252/346/210/221/351/224/232/347/202/271_/347/263/273/347/273/237/346/217/220/347/244/272/350/257/215/346/240/207/345/207/206_v0.3.md +275 -275
  63. package/docs/mdcg//345/217/221/345/270/203/351/227/250/347/246/201/351/223/276_v0.1.md +54 -0
  64. package/docs/mdcg//346/272/220/347/240/201/347/272/247/346/236/266/346/236/204/345/256/241/350/256/241_GPT/346/211/271/350/257/204/345/257/271/347/205/247_v1.0.md +130 -130
  65. package/docs/mdcg//347/201/265/346/236/24282/345/267/245/345/205/267_/345/212/237/350/203/275/346/225/264/347/220/206/344/270/216/350/277/201/347/247/273/346/230/240/345/260/204_v0.1.md +278 -278
  66. 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 -172
  67. package/docs/mdcg//347/274/272/345/217/243/345/215/225_P0/346/224/266/345/217/243_v0.1.md +270 -270
  68. package/docs/mdcg//350/256/244/347/237/245/345/233/276_G4-G8/347/274/272/345/217/243/350/243/201/345/256/232/345/215/225_v0.1.md +491 -491
  69. package/docs/mdcg//350/256/244/347/237/245/345/233/276_/346/235/241/344/273/266/347/251/272/351/227/264/345/220/210/346/210/220/344/270/216/347/224/237/346/225/210/346/235/241/344/273/266/345/217/243/345/276/204_v0.1.md +340 -340
  70. 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 +588 -588
  71. package/docs/plans/GridWorld/346/234/200/345/260/217/351/227/255/347/216/257/350/247/204/346/240/274_v0.1.md +176 -176
  72. package/docs/plans//345/221/275/345/220/215/346/262/273/347/220/206_/351/241/271/347/233/256/350/256/241/345/210/222.md +81 -81
  73. package/docs/swarm//350/234/202/347/276/244/344/272/222/350/201/224_v0.1.md +704 -704
  74. package/docs/swarm//350/234/202/347/276/244/345/220/214/351/224/231/346/243/200/346/265/213/345/256/236/351/252/214/345/215/217/350/256/256_v0.1.md +242 -242
  75. package/docs/theory//344/270/215/345/217/257/351/235/240/345/256/232/347/220/206/344/270/216/345/244/261/346/225/210/344/274/230/345/205/210/346/241/206/346/236/266_v0.3.md +365 -0
  76. package/docs/theory//345/215/225/347/272/277/347/250/213/344/270/216/346/263/250/346/204/217/345/212/233/351/233/206/344/270/255_/346/227/240/344/272/211/350/256/256/347/220/206/350/256/272/346/226/207/346/241/243_v1.0.md +129 -129
  77. 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 +134 -134
  78. package/docs/theory//346/231/272/350/203/275/350/256/2723.4.md +5260 -5260
  79. package/docs/theory//346/246/202/345/277/265/345/210/206/345/261/202/345/257/271/351/275/220/350/241/250_v0.1.md +145 -145
  80. 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 +144 -144
  81. package/docs/theory//347/220/206/350/256/272/344/273/223/346/213/206/345/210/206/344/270/216/347/231/275/347/256/261/347/237/245/350/257/206/345/272/223/345/206/205/350/277/201_v0.1.md +198 -198
  82. 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 +221 -221
  83. package/docs/world_model//344/270/226/347/225/214/346/250/241/345/236/213_/347/245/236/347/273/217/347/275/221/347/273/234/345/272/225/345/261/202/346/236/266/346/236/204_v1.0.md +237 -237
  84. package/docs//344/270/215/345/217/257/351/235/240/346/200/247/347/220/206/350/256/272_v0.1.md +343 -0
  85. package/docs//345/217/221/345/270/203/344/273/266/345/233/236/346/272/257/350/257/264/346/230/216_v0.1.md +82 -82
  86. 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 +28 -2
  87. package/dsh/README.md +82 -82
  88. package/dsh/cordis.yml.example +139 -139
  89. package/dsh/update-lingshu.bat +11 -11
  90. package/lib/bridge.d.ts +9 -0
  91. package/lib/bridge.js +35 -0
  92. package/lib/hooks.js +36 -2
  93. package/lib/index.js +7 -1
  94. package/lib/lib/roleplay_web.js +116 -29
  95. package/lib/lib/token_store.d.ts +7 -1
  96. package/lib/lib/token_store.js +12 -3
  97. package/md_cg/__init__.py +7 -7
  98. package/md_cg/audit.py +379 -368
  99. package/md_cg/autonomy.py +287 -287
  100. package/md_cg/backfill.py +1328 -1327
  101. package/md_cg/backfill_bigdomain.py +34 -34
  102. package/md_cg/backfill_bucket_zh.py +35 -0
  103. package/md_cg/bench6_arms.py +410 -410
  104. package/md_cg/bench6_common.py +230 -230
  105. package/md_cg/bench6_competitors.py +212 -212
  106. package/md_cg/bench_axis_domain.py +257 -257
  107. package/md_cg/bench_blind_comp.py +308 -308
  108. package/md_cg/bench_e2e_judge.py +532 -0
  109. package/md_cg/bench_e2e_locomo_qa.py +368 -0
  110. package/md_cg/bench_e2e_qa.py +256 -0
  111. package/md_cg/bench_en_atoms_public.py +230 -230
  112. package/md_cg/bench_governance.py +348 -348
  113. package/md_cg/bench_lme_zh.py +410 -410
  114. package/md_cg/bench_locomo.py +121 -121
  115. package/md_cg/bench_locomo_zh.py +450 -450
  116. package/md_cg/bench_locomo_zh_public.py +147 -147
  117. package/md_cg/bench_longmem.py +112 -112
  118. package/md_cg/bench_membench.py +632 -632
  119. package/md_cg/bench_p0.py +149 -149
  120. package/md_cg/bench_progressive.py +287 -287
  121. package/md_cg/bench_role_views.py +238 -238
  122. package/md_cg/bench_task_ab.py +243 -243
  123. package/md_cg/bench_task_ab_llm.py +408 -408
  124. package/md_cg/bench_unified_en.py +204 -204
  125. package/md_cg/bench_zh_mad.py +601 -601
  126. package/md_cg/blindspot_tickets.py +123 -123
  127. package/md_cg/branches.py +301 -285
  128. package/md_cg/build_postings.py +73 -73
  129. package/md_cg/ccgc.py +1006 -948
  130. package/md_cg/census.py +132 -132
  131. package/md_cg/chain.py +315 -300
  132. package/md_cg/codeindex.py +531 -531
  133. package/md_cg/coldverify.py +292 -292
  134. package/md_cg/comment_gate.py +337 -337
  135. package/md_cg/cond_compose.py +190 -190
  136. package/md_cg/cond_facts.py +154 -154
  137. package/md_cg/cond_template.json +106 -106
  138. package/md_cg/condition_anchor.py +142 -142
  139. package/md_cg/conformance.py +726 -726
  140. package/md_cg/consistency.py +717 -717
  141. package/md_cg/consolidate.py +1537 -1439
  142. package/md_cg/corpus.py +110 -110
  143. package/md_cg/crosscheck.py +1098 -1097
  144. package/md_cg/crypto.py +3 -1
  145. package/md_cg/d_meta.py +310 -310
  146. package/md_cg/datapath.py +78 -18
  147. package/md_cg/docindex.py +473 -473
  148. package/md_cg/eval_common.py +575 -575
  149. package/md_cg/evidence.py +4 -2
  150. package/md_cg/evolution.py +477 -477
  151. package/md_cg/export.py +222 -220
  152. package/md_cg/forgetting.py +581 -581
  153. package/md_cg/fsutil.py +377 -329
  154. package/md_cg/hotcache.py +48 -7
  155. package/md_cg/hyperedge.py +251 -251
  156. package/md_cg/identity.py +390 -390
  157. package/md_cg/insight.py +500 -500
  158. package/md_cg/interop.py +338 -0
  159. package/md_cg/judgment_manifest.py +177 -0
  160. package/md_cg/lexicon/build_cedict_en_zh.py +329 -329
  161. package/md_cg/lexicon/build_standard_en.py +171 -171
  162. package/md_cg/lexicon/expand_en_zh.py +211 -211
  163. package/md_cg/lifecycle.py +272 -272
  164. package/md_cg/linkref.py +280 -280
  165. package/md_cg/links.py +140 -107
  166. package/md_cg/mcp_server.py +403 -55
  167. package/md_cg/md_whitebox.py +345 -345
  168. package/md_cg/mdcg.py +783 -233
  169. package/md_cg/mdcos.py +500 -70
  170. package/md_cg/metacognition.py +591 -591
  171. package/md_cg/migrate.py +119 -119
  172. package/md_cg/migrate_aeis.py +221 -221
  173. package/md_cg/migrate_roleplay.py +293 -293
  174. package/md_cg/migrate_wisdom_graph.py +360 -360
  175. package/md_cg/mreview/__init__.py +25 -25
  176. package/md_cg/mreview/__main__.py +110 -110
  177. package/md_cg/mreview/bundle.py +178 -178
  178. package/md_cg/mreview/candidates.py +262 -262
  179. package/md_cg/mreview/govern.py +694 -693
  180. package/md_cg/mreview/locate.py +939 -939
  181. package/md_cg/mreview/pipeline.py +728 -728
  182. package/md_cg/mreview/rules/duplication.json +21 -21
  183. package/md_cg/mreview/rules/field_coverage.json +54 -54
  184. package/md_cg/mreview/rules/source_license.json +21 -21
  185. package/md_cg/mreview/rules/template_flow.json +21 -21
  186. package/md_cg/mreview/ruleset.py +252 -252
  187. package/md_cg/nodefile.py +575 -575
  188. package/md_cg/pooling.py +484 -472
  189. package/md_cg/postings.py +300 -298
  190. package/md_cg/predict.py +1100 -1100
  191. package/md_cg/progressive.py +123 -123
  192. package/md_cg/protect.py +272 -272
  193. package/md_cg/protocol/md_cg_gate.proto +33 -33
  194. package/md_cg/protocol.py +372 -372
  195. package/md_cg/provenance.py +582 -582
  196. package/md_cg/reach.py +453 -453
  197. package/md_cg/readcache.py +143 -0
  198. package/md_cg/reconcile.py +228 -0
  199. package/md_cg/refindex.py +833 -833
  200. package/md_cg/refine.py +604 -604
  201. package/md_cg/review_cli.py +170 -0
  202. package/md_cg/roleviews.py +89 -89
  203. package/md_cg/routing.py +393 -365
  204. package/md_cg/run_tests.py +211 -0
  205. package/md_cg/scrub.py +13 -3
  206. package/md_cg/security.py +128 -18
  207. package/md_cg/self_state.py +1029 -1029
  208. package/md_cg/selfreport.py +152 -151
  209. package/md_cg/semantic/__init__.py +10 -10
  210. package/md_cg/semantic/canonical.py +122 -122
  211. package/md_cg/semantic/en_normalizer.py +364 -364
  212. package/md_cg/semantic/en_zh_map.json +28694 -0
  213. package/md_cg/semantic/export_en_zh_map.py +64 -0
  214. package/md_cg/semantic/unify.py +45 -0
  215. package/md_cg/semantic/zh_en_atoms.py +139 -139
  216. package/md_cg/signer.py +7 -4
  217. package/md_cg/sources.py +816 -582
  218. package/md_cg/statushdr.py +179 -179
  219. package/md_cg/stg.py +54 -37
  220. package/md_cg/subgraph.py +729 -729
  221. package/md_cg/sustain.py +35 -5
  222. package/md_cg/tasks.py +470 -470
  223. package/md_cg/test_access_hints.py +147 -0
  224. package/md_cg/test_action_derive.py +203 -203
  225. package/md_cg/test_audit_rotate.py +270 -270
  226. package/md_cg/test_autonomy.py +143 -143
  227. package/md_cg/test_bench_governance.py +102 -102
  228. package/md_cg/test_blindspot_tickets.py +166 -166
  229. package/md_cg/test_branch_discard_tombstone.py +136 -0
  230. package/md_cg/test_branches.py +13 -3
  231. package/md_cg/test_ccg_perturb.py +184 -184
  232. package/md_cg/test_ccgc.py +433 -433
  233. package/md_cg/test_census_prune.py +81 -81
  234. package/md_cg/test_chain_read_isolate.py +168 -0
  235. package/md_cg/test_cond_compose_anchors.py +76 -76
  236. package/md_cg/test_cond_match.py +165 -165
  237. package/md_cg/test_condition_anchor.py +81 -81
  238. package/md_cg/test_d_meta.py +412 -412
  239. package/md_cg/test_datapath_device_name.py +203 -0
  240. package/md_cg/test_datapath_root.py +199 -199
  241. package/md_cg/test_emit_negtail_cache.py +156 -0
  242. package/md_cg/test_en_pipeline.py +22 -2
  243. package/md_cg/test_gain_gate.py +212 -212
  244. package/md_cg/test_govern_directread.py +421 -0
  245. package/md_cg/test_health_scale.py +173 -173
  246. package/md_cg/test_hive_ingest.py +285 -0
  247. package/md_cg/test_hot_cold.py +215 -215
  248. package/md_cg/test_hyperedge.py +245 -245
  249. package/md_cg/test_i26_empty_first_write.py +116 -0
  250. package/md_cg/test_i27_e041_identity.py +128 -0
  251. package/md_cg/test_i28_hotcache_prodpath.py +122 -0
  252. package/md_cg/test_i32_hotcache_env_key.py +218 -0
  253. package/md_cg/test_identity_attribution.py +96 -15
  254. package/md_cg/test_index_durability.py +17 -3
  255. package/md_cg/test_interop.py +95 -0
  256. package/md_cg/test_interop_judgment.py +228 -0
  257. package/md_cg/test_issue39_utf8_stdio.py +273 -0
  258. package/md_cg/test_lifecycle.py +309 -309
  259. package/md_cg/test_linkref.py +306 -306
  260. package/md_cg/test_links_concurrent_write.py +188 -0
  261. package/md_cg/test_lock.py +43 -43
  262. package/md_cg/test_md_access_parity.py +255 -255
  263. package/md_cg/test_md_writepath.py +345 -345
  264. package/md_cg/test_mdstore_search_parity.py +160 -0
  265. package/md_cg/test_merge_upsert.py +168 -0
  266. package/md_cg/test_mr_m2.py +587 -587
  267. package/md_cg/test_mr_m3.py +710 -710
  268. package/md_cg/test_mr_m4.py +485 -485
  269. package/md_cg/test_n123_derive_expiry_chain.py +205 -0
  270. package/md_cg/test_n130_verify_falsified_protect.py +185 -0
  271. package/md_cg/test_n131_merge_gate.py +205 -0
  272. package/md_cg/test_p0.py +250 -250
  273. package/md_cg/test_p1.py +316 -316
  274. package/md_cg/test_p10_identity.py +173 -173
  275. package/md_cg/test_p11_consistency.py +233 -233
  276. package/md_cg/test_p12_metacognition.py +212 -212
  277. package/md_cg/test_p13_encryption.py +241 -241
  278. package/md_cg/test_p14_sustain.py +249 -249
  279. package/md_cg/test_p15_scrub.py +280 -280
  280. package/md_cg/test_p16_self_state.py +301 -301
  281. package/md_cg/test_p17_predict.py +354 -354
  282. package/md_cg/test_p18_whitebox.py +171 -171
  283. package/md_cg/test_p19_migrate_roleplay.py +149 -149
  284. package/md_cg/test_p1x_ref_root.py +160 -0
  285. package/md_cg/test_p20_evolution.py +315 -315
  286. package/md_cg/test_p21_tokens.py +293 -270
  287. package/md_cg/test_p22_theory.py +175 -175
  288. package/md_cg/test_p23_links.py +311 -311
  289. package/md_cg/test_p24_evidence.py +227 -227
  290. package/md_cg/test_p25_weights.py +156 -156
  291. package/md_cg/test_p26_refindex.py +416 -416
  292. package/md_cg/test_p27_docindex.py +16 -7
  293. package/md_cg/test_p28_refcheck.py +305 -305
  294. package/md_cg/test_p29_session_ingest_export.py +354 -333
  295. package/md_cg/test_p2_mcp.py +3 -0
  296. package/md_cg/test_p3.py +11 -2
  297. package/md_cg/test_p30_maintain.py +330 -330
  298. package/md_cg/test_p31_insight.py +534 -534
  299. package/md_cg/test_p32_backfill.py +7 -1
  300. package/md_cg/test_p33_ccg_wiring.py +293 -293
  301. package/md_cg/test_p34_crosscheck.py +331 -331
  302. package/md_cg/test_p35_conditioned_claim.py +252 -252
  303. package/md_cg/test_p36_kp_align.py +230 -230
  304. package/md_cg/test_p37_condition_space.py +248 -248
  305. package/md_cg/test_p38_concurrent_flush.py +102 -0
  306. package/md_cg/test_p38_contextualize.py +273 -273
  307. package/md_cg/test_p39_verify_flow.py +153 -0
  308. package/md_cg/test_p39_vision_evidence.py +369 -369
  309. package/md_cg/test_p40_refine_worklist.py +241 -241
  310. package/md_cg/test_p41_evolve_patrol.py +224 -224
  311. package/md_cg/test_p42_provenance.py +269 -269
  312. package/md_cg/test_p43_pooling.py +412 -398
  313. package/md_cg/test_p44_md_whitebox.py +231 -231
  314. package/md_cg/test_p45_session_identity.py +219 -219
  315. package/md_cg/test_p46_unit_scope.py +272 -272
  316. package/md_cg/test_p47_session_view.py +316 -0
  317. package/md_cg/test_p4_fuzzy.py +223 -223
  318. package/md_cg/test_p5_semantic.py +226 -226
  319. package/md_cg/test_p6_consolidate.py +440 -387
  320. package/md_cg/test_p7_goals_recent.py +202 -202
  321. package/md_cg/test_p8_subgraph_chain.py +200 -200
  322. package/md_cg/test_p9_forget_protect.py +231 -231
  323. package/md_cg/test_predict_beta.py +135 -135
  324. package/md_cg/test_preflight_failclosed.py +100 -100
  325. package/md_cg/test_progressive.py +146 -146
  326. package/md_cg/test_propose_tail_index.py +157 -0
  327. package/md_cg/test_protocol.py +243 -243
  328. package/md_cg/test_reach.py +378 -378
  329. package/md_cg/test_reach_keys.py +201 -201
  330. package/md_cg/test_read_clip.py +141 -141
  331. package/md_cg/test_read_scope_b27.py +277 -0
  332. package/md_cg/test_readcache_default_on.py +168 -0
  333. package/md_cg/test_readcache_precise_inval.py +270 -0
  334. package/md_cg/test_readcache_prodpath.py +203 -0
  335. package/md_cg/test_reconcile_v0.py +294 -0
  336. package/md_cg/test_retr_gates_prodpath.py +140 -0
  337. package/md_cg/test_retr_s1.py +344 -340
  338. package/md_cg/test_retr_s1b.py +276 -209
  339. package/md_cg/test_retr_s3.py +194 -194
  340. package/md_cg/test_retr_s4.py +163 -163
  341. package/md_cg/test_retr_s5.py +200 -200
  342. package/md_cg/test_retr_s6.py +157 -157
  343. package/md_cg/test_retr_s7.py +392 -384
  344. package/md_cg/test_retr_s8_time.py +369 -316
  345. package/md_cg/test_retr_s9_edges.py +286 -286
  346. package/md_cg/test_retr_s9_entity_ctx.py +9 -3
  347. package/md_cg/test_retr_score_once.py +208 -0
  348. package/md_cg/test_review_conformance.py +367 -367
  349. package/md_cg/test_review_onepass.py +170 -0
  350. package/md_cg/test_role_views.py +354 -354
  351. package/md_cg/test_rrf_graph_seed_cache.py +154 -0
  352. package/md_cg/test_security_audit.py +155 -0
  353. package/md_cg/test_security_audit_b26.py +161 -0
  354. package/md_cg/test_security_audit_v21.py +250 -0
  355. package/md_cg/test_sem_noise.py +242 -242
  356. package/md_cg/test_semantic_canonical.py +16 -2
  357. package/md_cg/test_session_isolation.py +168 -0
  358. package/md_cg/test_snapshot_autoclose.py +187 -0
  359. package/md_cg/test_subproc_encoding.py +192 -192
  360. package/md_cg/test_sustain_mutual.py +153 -153
  361. package/md_cg/test_tail_watermark_race.py +208 -0
  362. package/md_cg/test_tasks.py +409 -409
  363. package/md_cg/test_tenant_env_override_warn.py +139 -0
  364. package/md_cg/test_tenant_registry_corrupt_warn.py +151 -0
  365. package/md_cg/test_tool_face.py +189 -189
  366. package/md_cg/test_transfer.py +180 -180
  367. package/md_cg/test_trust.py +361 -361
  368. package/md_cg/test_twophase.py +286 -286
  369. package/md_cg/test_v14_fixes.py +38 -20
  370. package/md_cg/test_validity_filter.py +280 -280
  371. package/md_cg/test_verify_answer.py +138 -138
  372. package/md_cg/test_verify_dirty_reconcile.py +157 -0
  373. package/md_cg/test_wisdom_md_store.py +292 -292
  374. package/md_cg/test_writelimit.py +197 -197
  375. package/md_cg/test_writepipe.py +214 -214
  376. package/md_cg/theory.py +6 -3
  377. package/md_cg/tokens.py +85 -14
  378. package/md_cg/tool_face.py +260 -260
  379. package/md_cg/trust.py +986 -950
  380. package/md_cg/twophase.py +231 -231
  381. package/md_cg/units.py +668 -667
  382. package/md_cg/vision_evidence.py +667 -666
  383. package/md_cg/weights.py +624 -624
  384. package/md_cg/whitebox.py +527 -527
  385. package/md_cg/whitebox_kb/__init__.py +37 -37
  386. package/md_cg/whitebox_kb/aeis_core/__init__.py +42 -42
  387. package/md_cg/whitebox_kb/aeis_core/semantic.py +280 -280
  388. package/md_cg/whitebox_kb/aeis_core/textutil.py +13 -13
  389. package/md_cg/whitebox_kb/engine.py +310 -310
  390. package/md_cg/whitebox_kb/seed_knowledge//346/231/272/350/203/275/350/256/2723.4.md +5260 -5260
  391. package/md_cg/whitebox_kb/wisdom/browser_units.py +2631 -2631
  392. package/md_cg/whitebox_kb/wisdom/causal_discover.py +432 -432
  393. package/md_cg/whitebox_kb/wisdom/chat_engine.py +1506 -1506
  394. package/md_cg/whitebox_kb/wisdom/compiler_code_units.py +3033 -3033
  395. package/md_cg/whitebox_kb/wisdom/condition_algebra.py +112 -112
  396. package/md_cg/whitebox_kb/wisdom/condition_frame.py +315 -315
  397. package/md_cg/whitebox_kb/wisdom/condition_kb.py +108 -108
  398. package/md_cg/whitebox_kb/wisdom/conflict_map.json +4445 -4445
  399. package/md_cg/whitebox_kb/wisdom/core/lexer.py +512 -512
  400. package/md_cg/whitebox_kb/wisdom/core/name_checker.py +1023 -1023
  401. package/md_cg/whitebox_kb/wisdom/cspmn.py +258 -258
  402. package/md_cg/whitebox_kb/wisdom/csre.py +264 -264
  403. package/md_cg/whitebox_kb/wisdom/danmaku_audit.py +252 -252
  404. package/md_cg/whitebox_kb/wisdom/distilled_condition_units.json +6417 -6417
  405. package/md_cg/whitebox_kb/wisdom/docs/WB-EVAL-20260902b.json +1950 -1950
  406. package/md_cg/whitebox_kb/wisdom/docs/WB-EVAL-20260902c.json +1296 -1296
  407. package/md_cg/whitebox_kb/wisdom/docs/whitebox_capability_graph_demo.json +59 -59
  408. package/md_cg/whitebox_kb/wisdom/graph_db_units.py +3089 -3089
  409. package/md_cg/whitebox_kb/wisdom/knowledge_points.py +318 -318
  410. package/md_cg/whitebox_kb/wisdom/md_access.py +470 -470
  411. package/md_cg/whitebox_kb/wisdom/md_store.py +276 -251
  412. package/md_cg/whitebox_kb/wisdom/migrate_wisdom.py +476 -476
  413. package/md_cg/whitebox_kb/wisdom/multilang_ir.py +128 -124
  414. package/md_cg/whitebox_kb/wisdom/navigate.py +248 -248
  415. package/md_cg/whitebox_kb/wisdom/neural_retrieve.py +224 -224
  416. package/md_cg/whitebox_kb/wisdom/os_units.py +2735 -2735
  417. package/md_cg/whitebox_kb/wisdom/pattern_separation.py +376 -376
  418. package/md_cg/whitebox_kb/wisdom/prereq_map.json +364 -364
  419. package/md_cg/whitebox_kb/wisdom/python_code_units.py +2766 -2766
  420. package/md_cg/whitebox_kb/wisdom/role_solidified.json +7 -7
  421. package/md_cg/whitebox_kb/wisdom/route_memory.py +244 -244
  422. package/md_cg/whitebox_kb/wisdom/scene_reconstruction.py +148 -148
  423. package/md_cg/whitebox_kb/wisdom/snr_report.json +40 -40
  424. package/md_cg/whitebox_kb/wisdom/test_code_compose_domains.py +7541 -7541
  425. package/md_cg/whitebox_kb/wisdom/test_compiler_self_bootstrap.py +354 -354
  426. package/md_cg/whitebox_kb/wisdom/test_ecosystem_assembly.py +158 -158
  427. package/md_cg/whitebox_kb/wisdom/test_ecosystem_demos.py +193 -193
  428. package/md_cg/whitebox_kb/wisdom/test_graph_db.py +292 -292
  429. package/md_cg/whitebox_kb/wisdom/test_python_self_bootstrap.py +160 -160
  430. package/md_cg/whitebox_kb/wisdom/trigger_words_index.json +4366 -4366
  431. package/md_cg/whitebox_kb/wisdom/verifier.py +1297 -1297
  432. package/md_cg/writelimit.py +356 -356
  433. package/md_cg/writepipe.py +20 -8
  434. package/package.json +101 -96
  435. package/skills/plugin.json +54 -54
  436. package/skills/skills/designer-perspective/SKILL.md +158 -158
  437. package/skills/skills/designer-perspective/references/01-observation-position.md +66 -66
  438. package/skills/skills/designer-perspective/references/02-structure-recognition.md +62 -62
  439. package/skills/skills/designer-perspective/references/03-direction-judgment.md +55 -55
  440. package/skills/skills/designer-perspective/references/04-qualification-verdict.md +72 -72
  441. package/skills/skills/designer-perspective/references/05-condition-attribution.md +74 -74
  442. package/skills/skills/designer-perspective/scripts/designer.py +545 -545
  443. package/skills/skills/designer-perspective/tests/cases.jsonl +17 -17
  444. package/skills/skills/designer-perspective/tests/selftest.py +61 -61
  445. package/skills/skills/lingshu-browser/SKILL.md +60 -60
  446. package/skills/skills/lingshu-compiler/SKILL.md +56 -56
  447. package/skills/skills/lingshu-compiler/units/analyze-type-infer/SKILL.md +45 -45
  448. package/skills/skills/lingshu-compiler/units/check-name-real/SKILL.md +45 -45
  449. package/skills/skills/lingshu-compiler/units/compile-assign/SKILL.md +45 -45
  450. package/skills/skills/lingshu-compiler/units/compile-expr-tree/SKILL.md +45 -45
  451. package/skills/skills/lingshu-compiler/units/compile-full-pipeline/SKILL.md +45 -45
  452. package/skills/skills/lingshu-compiler/units/compile-func-def/SKILL.md +45 -45
  453. package/skills/skills/lingshu-compiler/units/compile-if-then/SKILL.md +45 -45
  454. package/skills/skills/lingshu-compiler/units/compile-logic-expr/SKILL.md +45 -45
  455. package/skills/skills/lingshu-compiler/units/compile-recursive/SKILL.md +45 -45
  456. package/skills/skills/lingshu-compiler/units/compile-scope/SKILL.md +45 -45
  457. package/skills/skills/lingshu-compiler/units/compile-type-check/SKILL.md +45 -45
  458. package/skills/skills/lingshu-compiler/units/compile-while/SKILL.md +45 -45
  459. package/skills/skills/lingshu-compiler/units/compiler-0010c4bf/SKILL.md +45 -45
  460. package/skills/skills/lingshu-compiler/units/compiler-0355bffb/SKILL.md +45 -45
  461. package/skills/skills/lingshu-compiler/units/compiler-0361708a/SKILL.md +45 -45
  462. package/skills/skills/lingshu-compiler/units/compiler-054a0414/SKILL.md +45 -45
  463. package/skills/skills/lingshu-compiler/units/compiler-05a1691a/SKILL.md +45 -45
  464. package/skills/skills/lingshu-compiler/units/compiler-05eeed1e/SKILL.md +45 -45
  465. package/skills/skills/lingshu-compiler/units/compiler-0622a1f6/SKILL.md +45 -45
  466. package/skills/skills/lingshu-compiler/units/compiler-08b54217/SKILL.md +45 -45
  467. package/skills/skills/lingshu-compiler/units/compiler-0a62b70c/SKILL.md +45 -45
  468. package/skills/skills/lingshu-compiler/units/compiler-0ab24d00/SKILL.md +45 -45
  469. package/skills/skills/lingshu-compiler/units/compiler-0e093688/SKILL.md +45 -45
  470. package/skills/skills/lingshu-compiler/units/compiler-0e82b966/SKILL.md +45 -45
  471. package/skills/skills/lingshu-compiler/units/compiler-0ee9b9b9/SKILL.md +45 -45
  472. package/skills/skills/lingshu-compiler/units/compiler-0f3787e6/SKILL.md +45 -45
  473. package/skills/skills/lingshu-compiler/units/compiler-1028685f/SKILL.md +45 -45
  474. package/skills/skills/lingshu-compiler/units/compiler-11897630/SKILL.md +45 -45
  475. package/skills/skills/lingshu-compiler/units/compiler-16661b9b/SKILL.md +45 -45
  476. package/skills/skills/lingshu-compiler/units/compiler-1f722303/SKILL.md +45 -45
  477. package/skills/skills/lingshu-compiler/units/compiler-25be1262/SKILL.md +45 -45
  478. package/skills/skills/lingshu-compiler/units/compiler-2a76ba07/SKILL.md +45 -45
  479. package/skills/skills/lingshu-compiler/units/compiler-2dbea54a/SKILL.md +45 -45
  480. package/skills/skills/lingshu-compiler/units/compiler-2df52f16/SKILL.md +45 -45
  481. package/skills/skills/lingshu-compiler/units/compiler-2ec2c9d2/SKILL.md +45 -45
  482. package/skills/skills/lingshu-compiler/units/compiler-2f8c8f39/SKILL.md +45 -45
  483. package/skills/skills/lingshu-compiler/units/compiler-38378ac7/SKILL.md +45 -45
  484. package/skills/skills/lingshu-compiler/units/compiler-39457d2e/SKILL.md +45 -45
  485. package/skills/skills/lingshu-compiler/units/compiler-39fb5926/SKILL.md +45 -45
  486. package/skills/skills/lingshu-compiler/units/compiler-47f4fbfa/SKILL.md +45 -45
  487. package/skills/skills/lingshu-compiler/units/compiler-4a8cd1f1/SKILL.md +45 -45
  488. package/skills/skills/lingshu-compiler/units/compiler-4b230b6b/SKILL.md +45 -45
  489. package/skills/skills/lingshu-compiler/units/compiler-4cbbda95/SKILL.md +45 -45
  490. package/skills/skills/lingshu-compiler/units/compiler-4d5a68ff/SKILL.md +45 -45
  491. package/skills/skills/lingshu-compiler/units/compiler-56dc9bdd/SKILL.md +45 -45
  492. package/skills/skills/lingshu-compiler/units/compiler-57e76ebe/SKILL.md +45 -45
  493. package/skills/skills/lingshu-compiler/units/compiler-61ff016b/SKILL.md +45 -45
  494. package/skills/skills/lingshu-compiler/units/compiler-63eae588/SKILL.md +45 -45
  495. package/skills/skills/lingshu-compiler/units/compiler-64c4224b/SKILL.md +45 -45
  496. package/skills/skills/lingshu-compiler/units/compiler-66377955/SKILL.md +45 -45
  497. package/skills/skills/lingshu-compiler/units/compiler-674ab4f6/SKILL.md +45 -45
  498. package/skills/skills/lingshu-compiler/units/compiler-6af76fd6/SKILL.md +45 -45
  499. package/skills/skills/lingshu-compiler/units/compiler-6d979db2/SKILL.md +45 -45
  500. package/skills/skills/lingshu-compiler/units/compiler-6de326c0/SKILL.md +45 -45
  501. package/skills/skills/lingshu-compiler/units/compiler-6f1c0eea/SKILL.md +45 -45
  502. package/skills/skills/lingshu-compiler/units/compiler-724d6c9b/SKILL.md +45 -45
  503. package/skills/skills/lingshu-compiler/units/compiler-7690177f/SKILL.md +45 -45
  504. package/skills/skills/lingshu-compiler/units/compiler-7b229a7d/SKILL.md +45 -45
  505. package/skills/skills/lingshu-compiler/units/compiler-7f471b3a/SKILL.md +45 -45
  506. package/skills/skills/lingshu-compiler/units/compiler-815cad08/SKILL.md +45 -45
  507. package/skills/skills/lingshu-compiler/units/compiler-83c61634/SKILL.md +45 -45
  508. package/skills/skills/lingshu-compiler/units/compiler-8c798c81/SKILL.md +45 -45
  509. package/skills/skills/lingshu-compiler/units/compiler-8dd747ac/SKILL.md +45 -45
  510. package/skills/skills/lingshu-compiler/units/compiler-90324d0a/SKILL.md +45 -45
  511. package/skills/skills/lingshu-compiler/units/compiler-94b8d72d/SKILL.md +45 -45
  512. package/skills/skills/lingshu-compiler/units/compiler-94f12231/SKILL.md +45 -45
  513. package/skills/skills/lingshu-compiler/units/compiler-95937c16/SKILL.md +45 -45
  514. package/skills/skills/lingshu-compiler/units/compiler-98a5625b/SKILL.md +45 -45
  515. package/skills/skills/lingshu-compiler/units/compiler-98b4c42f/SKILL.md +45 -45
  516. package/skills/skills/lingshu-compiler/units/compiler-98e3894a/SKILL.md +45 -45
  517. package/skills/skills/lingshu-compiler/units/compiler-9bdfe4b8/SKILL.md +45 -45
  518. package/skills/skills/lingshu-compiler/units/compiler-9d9b4e83/SKILL.md +45 -45
  519. package/skills/skills/lingshu-compiler/units/compiler-9f7be5ad/SKILL.md +45 -45
  520. package/skills/skills/lingshu-compiler/units/compiler-a6daf076/SKILL.md +45 -45
  521. package/skills/skills/lingshu-compiler/units/compiler-a7995a0c/SKILL.md +45 -45
  522. package/skills/skills/lingshu-compiler/units/compiler-a8399248/SKILL.md +45 -45
  523. package/skills/skills/lingshu-compiler/units/compiler-aabbd099/SKILL.md +45 -45
  524. package/skills/skills/lingshu-compiler/units/compiler-afe169d8/SKILL.md +45 -45
  525. package/skills/skills/lingshu-compiler/units/compiler-b0678bda/SKILL.md +45 -45
  526. package/skills/skills/lingshu-compiler/units/compiler-b09bd196/SKILL.md +45 -45
  527. package/skills/skills/lingshu-compiler/units/compiler-b1396e23/SKILL.md +45 -45
  528. package/skills/skills/lingshu-compiler/units/compiler-b9b31ce0/SKILL.md +45 -45
  529. package/skills/skills/lingshu-compiler/units/compiler-c10264a7/SKILL.md +45 -45
  530. package/skills/skills/lingshu-compiler/units/compiler-cb1e8e4b/SKILL.md +45 -45
  531. package/skills/skills/lingshu-compiler/units/compiler-ccafd438/SKILL.md +45 -45
  532. package/skills/skills/lingshu-compiler/units/compiler-cda9c262/SKILL.md +45 -45
  533. package/skills/skills/lingshu-compiler/units/compiler-ce648068/SKILL.md +45 -45
  534. package/skills/skills/lingshu-compiler/units/compiler-cf5776a4/SKILL.md +45 -45
  535. package/skills/skills/lingshu-compiler/units/compiler-d974e5d3/SKILL.md +45 -45
  536. package/skills/skills/lingshu-compiler/units/compiler-e3979fd3/SKILL.md +45 -45
  537. package/skills/skills/lingshu-compiler/units/compiler-eb1cf2b5/SKILL.md +45 -45
  538. package/skills/skills/lingshu-compiler/units/compiler-ecb30d5b/SKILL.md +45 -45
  539. package/skills/skills/lingshu-compiler/units/compiler-f8c8b24b/SKILL.md +45 -45
  540. package/skills/skills/lingshu-compiler/units/compiler-f99fedbe/SKILL.md +45 -45
  541. package/skills/skills/lingshu-compiler/units/compiler-fa8ff5f7/SKILL.md +45 -45
  542. package/skills/skills/lingshu-compiler/units/compiler-fe1b058d/SKILL.md +45 -45
  543. package/skills/skills/lingshu-compiler/units/lex-chinese-program/SKILL.md +45 -45
  544. package/skills/skills/lingshu-compiler/units/lex-dao-de-jing/SKILL.md +45 -45
  545. package/skills/skills/lingshu-compiler/units/lex-nine-chapters/SKILL.md +45 -45
  546. package/skills/skills/lingshu-compiler/units/vm-arithmetic/SKILL.md +45 -45
  547. package/skills/skills/lingshu-compiler/units/vm-array-ops/SKILL.md +45 -45
  548. package/skills/skills/lingshu-compiler/units/vm-closure-call/SKILL.md +45 -45
  549. package/skills/skills/lingshu-compiler/units/vm-closure-create/SKILL.md +45 -45
  550. package/skills/skills/lingshu-compiler/units/vm-compare/SKILL.md +45 -45
  551. package/skills/skills/lingshu-compiler/units/vm-cond-jump/SKILL.md +45 -45
  552. package/skills/skills/lingshu-compiler/units/vm-cond-space/SKILL.md +45 -45
  553. package/skills/skills/lingshu-compiler/units/vm-exception/SKILL.md +45 -45
  554. package/skills/skills/lingshu-compiler/units/vm-func-call/SKILL.md +45 -45
  555. package/skills/skills/lingshu-compiler/units/vm-loop-run/SKILL.md +45 -45
  556. package/skills/skills/lingshu-compiler/units/vm-profiling/SKILL.md +45 -45
  557. package/skills/skills/lingshu-compiler/units/vm-refcount/SKILL.md +45 -45
  558. package/skills/skills/lingshu-compiler/units/vm-run-loop/SKILL.md +45 -45
  559. package/skills/skills/lingshu-compiler/units/vm-short-circuit/SKILL.md +45 -45
  560. package/skills/skills/lingshu-compiler/units/vm-stack-guard/SKILL.md +45 -45
  561. package/skills/skills/lingshu-compiler/units/vm-stack-ops/SKILL.md +45 -45
  562. package/skills/skills/lingshu-compiler/units/vm-trust-accum/SKILL.md +45 -45
  563. package/skills/skills/lingshu-graph/SKILL.md +63 -63
  564. package/skills/skills/lingshu-net/SKILL.md +48 -48
  565. package/skills/skills/lingshu-os/SKILL.md +64 -64
  566. package/skills/skills/lingshu-pylang/SKILL.md +71 -71
  567. package/src/bridge.ts +33 -0
  568. package/src/hooks.ts +38 -2
  569. package/src/index.ts +526 -518
  570. package/src/lib/datapath.ts +326 -326
  571. package/src/lib/mdcg_client.ts +413 -413
  572. package/src/lib/mutual.ts +428 -428
  573. package/src/lib/prompt_safety.ts +62 -62
  574. package/src/lib/python_path.ts +71 -71
  575. package/src/lib/roleplay_web.ts +116 -29
  576. package/src/lib/token_store.ts +13 -3
  577. package/src/tools.ts +212 -212
  578. package/zcode/AGENTS.md +11 -3
  579. package/zcode/README.md +41 -41
  580. /package/docs/{mdcg → hive}//344/270/273/344/273/243/347/220/206/345/255/220/344/273/243/347/220/206/350/256/260/345/277/206/346/236/266/346/236/204/350/256/276/350/256/241.md" +0 -0
@@ -1,718 +1,718 @@
1
- # -*- coding: utf-8 -*-
2
- """md_cg · 节点间自动冲突检测(三级决策:情绪 → 反思 → 递归反思)
3
-
4
- 理论出处(本仓原文,非外部知识):
5
-
6
- · **情绪 = 信息差的二阶变化 d²D/dt²**(`docs/theory/智能的公理化基石.md` §十一,:412-510)
7
- 工程端对应 `emotional_bias`(approaching / avoiding / stable),并且原文明确:
8
- 「情绪通道**独立、不参与信任计算**」。故本模块 L0 只做**流程调度**,
9
- 绝不改动 confidence / 资格判定——避免把情绪混进事实判断。
10
-
11
- · **反题 = 预测与事实冲突**(同文档 :529,条件论七操作之一)
12
- —— L1 一次冲突检测的理论名:新信息与既有条件的「反题」关系。
13
-
14
- · **递归必须受约束**(同文档 :273):
15
- 「递归必须受到深度、查询次数、节点数、循环检测和信息增益门槛约束。
16
- 若递归没有减少候选空间,就不应继续搜索。」
17
- —— L2 递归反思的硬约束(本模块按此实现,而非无限展开)。
18
-
19
- · **四态路由** ACCEPT / REJECT / DEFER / BLINDSPOT(同文档 :721;
20
- `md_cg/mdcg.py` 四态定义)—— L1 的输出语义。
21
-
22
- · **知识飞轮:误差 → 补条件 → 结构更新**(同文档 :725;`mdcg.py:1001`
23
- `flywheel_step`)—— 冲突**自动**落 unresolved,误差是结构更新的输入。
24
-
25
- · **纪律四要素同构**(`docs/工作纪律_认知图条目_v1.1.json`;
26
- `consolidate.py` CCG 四要素)—— 「不能违反纪律」= 新内容不得命中纪律节点的
27
- `negative.reject`(不适用条件)。纪律节点用 tag/前缀识别,不硬编码具体条目。
28
-
29
- 三级决策(顺序即语义,越靠前越廉价):
30
-
31
- L0 情绪 :二阶信号,快速方向调整(approaching / stable / avoiding)——不裁决资格
32
- L1 反思 :一次条件级冲突检测(自否定 / 纪律违反 / 条件互斥)→ 四态
33
- L2 递归反思:L1 未决 → 沿关系链递归找「区分条件」;受深度/节点数/循环/增益门槛约束
34
-
35
- 冲突自动触发飞轮:verdict ∈ {REJECT, DEFER, BLINDSPOT} 且 auto_flywheel →
36
- `cg.flywheel_step({query, expected_state:"ACCEPT", actual_state:verdict, missing})`
37
-
38
- 留痕 `_consistency.jsonl`(append-only):每条判定可审计「为什么冲突 / 为什么放行」。
39
-
40
- 诚实边界:本模块是**条件级(结构化)**冲突检测,不是语义蕴含证明。它判断的是
41
- 「声明的适用/不适用条件是否互相覆盖」,而非「两句话在逻辑上是否矛盾」。
42
- 无法建立可比对路径时返回 BLINDSPOT,不假装确定。
43
- """
44
- from __future__ import annotations
45
-
46
- import json
47
- import os
48
- import time
49
-
50
- from .mdcg import expand_query_terms_weighted
51
-
52
- # --------------------------------------------------------------------------
53
- # 常量(全部可审计、可调)
54
- # --------------------------------------------------------------------------
55
-
56
- LOG_FILE = "_consistency.jsonl"
57
-
58
- MAX_SCAN = 200 # 单次检测最多比对的既有节点数(防 O(N) 爆炸)
59
- MAX_DEPTH = 3 # L2 递归深度上限(对齐 :273)
60
- MAX_NODES = 60 # L2 递归展开节点上限(对齐 :273)
61
- MIN_GAIN = 0.15 # 信息增益门槛:候选空间减少比例低于此值即停(对齐 :273)
62
-
63
- CLASH_HIGH = 0.6 # 条件互相覆盖阈值 → 明确互斥
64
- CLASH_LOW = 0.35 # 条件部分覆盖阈值 → 待定
65
- SELF_NEGATION = 0.5 # 自否定阈值:负条件被自身正文强命中
66
-
67
- # —— 第二类关系(同条件空间 · 结论槽取值分歧)阈值;均由探针实测标定 ——
68
- # run_probe_samecond.py 实测:同条件 same_cond=1.0 / 异条件空间 0.328 → 0.75 可分
69
- SAME_COND_HIGH = 0.75 # 同侧条件重合 → 判定「同一条件空间」
70
- # 实测结论槽(子功能):同槽 1.0 / 同主语异属性 0.14 / 异主题 0.0 → 0.6 可分
71
- SLOT_HIGH = 0.6 # 结论槽(功能名/子功能)重合 → 判定「同一件事」
72
- # 实测:逐字重复 1.0 / 同槽不同值 0.61 / 同值异措辞 0.74 → 0.95 只排除逐字重复
73
- CONCLUSION_SAME = 0.95 # 正文几乎逐字相同 → 属「重复」(该合并),不算冲突
74
-
75
- EMO_AVOID = 0.70 # 冲突强度 ≥ 此值 → avoiding
76
- EMO_APPROACH = 0.30 # 冲突强度 ≤ 此值 → approaching
77
-
78
- # 纪律节点识别(tag 或 id 前缀;不硬编码具体纪律条目)
79
- DISCIPLINE_TAGS = ("discipline", "纪律", "work_discipline", "rule", "规则", "戒律")
80
-
81
- VERDICTS = ("ACCEPT", "REJECT", "DEFER", "BLINDSPOT")
82
-
83
-
84
- # 生效条件:以 verdict 与 reason 构造异常(消息 `[{verdict}] {reason}`),conflicts 传假值(None/空容器等,源码 `conflicts or []`)时 self.conflicts 为 [],传真值时原样保留;
85
- class ConsistencyError(Exception):
86
- """硬冲突:写入被拒(自否定 / 违反纪律)。"""
87
-
88
- # 生效条件:verdict 与 reason 必传并赋给同名属性、拼成异常消息 `[{verdict}] {reason}`;conflicts 为假值(None/空容器等,源码 `conflicts or []`)时 self.conflicts 回落到 [],为真值时原样保留;
89
- def __init__(self, verdict, reason, conflicts=None):
90
- self.verdict = verdict
91
- self.reason = reason
92
- self.conflicts = conflicts or []
93
- super().__init__(f"[{verdict}] {reason}")
94
-
95
-
96
- # --------------------------------------------------------------------------
97
- # 原语:延迟导入 mdcos(避免 mdcg ← mdcos ← consistency 的循环导入)
98
- # --------------------------------------------------------------------------
99
-
100
- # 生效条件:无 required 形参,任一调用返回 ( _ccg_field, _declared_conditions, _neg_hit, _weighted_coverage ) 四元组;
101
- def _prims():
102
- """取 md_cg 已有的条件匹配原语(条件论「反题」的既有实现)。"""
103
- from .mdcos import (_ccg_field, _declared_conditions, _neg_hit,
104
- _weighted_coverage)
105
- return _ccg_field, _declared_conditions, _neg_hit, _weighted_coverage
106
-
107
-
108
- # 生效条件:xs 为可迭代对象时,逐项 str(x).strip() 后仅当结果非空且未出现过才加入 out 并返回 out;xs 为 None/False/0 时在 for 处迭代失败(TypeError);
109
- def _dedup(xs):
110
- out = []
111
- for x in xs:
112
- s = str(x).strip()
113
- if s and s not in out:
114
- out.append(s)
115
- return out
116
-
117
-
118
- # 生效条件:fm 给出时,遍历 fm.get("edges")(缺键或假值按空列表)中元素,dict 元素按 to→target→node→id 取首个真值、非 dict 元素直接作候选,候选真值时 str(t) 加入 out 并返回;
119
- def _edge_targets(fm):
120
- """节点声明的出边目标(兼容 dict / str 两种形态)。"""
121
- out = []
122
- for e in (fm.get("edges") or []):
123
- if isinstance(e, dict):
124
- t = e.get("to") or e.get("target") or e.get("node") or e.get("id")
125
- else:
126
- t = e
127
- if t:
128
- out.append(str(t))
129
- return out
130
-
131
-
132
- # 生效条件:fm 与 node_id 给出时,若 fm.get("tags")(缺键/假值按空)小写后与 DISCIPLINE_TAGS 有交集,或 str(node_id) 以 "discipline_" 或 "work_discipline" 开头,返回 True;否则返回 False;
133
- def _is_discipline(fm, node_id):
134
- tags = {str(t).lower() for t in (fm.get("tags") or [])}
135
- if tags & set(DISCIPLINE_TAGS):
136
- return True
137
- return str(node_id).startswith(("discipline_", "work_discipline"))
138
-
139
-
140
- # --------------------------------------------------------------------------
141
- # 模式分离的支持件(maintain.separate 复用;也供 insight.reconstruct 取线索)
142
- # --------------------------------------------------------------------------
143
-
144
- #: 分离边的关系名:两个节点「看起来像,但条件不同,互为不同情境」
145
- SEPARATION_REL = "distinct_from"
146
-
147
-
148
- # 生效条件:fm 给出时,仅处理 fm.get("edges")(缺键/假值按空)中的 dict 元素,relation_type 或 relation(缺省空串)小写等于 SEPARATION_REL 时,按 target→to→id 取首个真值且未重复则加入 out;
149
- def separation_targets(fm):
150
- """节点已声明的分离对象(`distinct_from` 出边),用于幂等与去重。"""
151
- out = []
152
- for e in (fm.get("edges") or []):
153
- if not isinstance(e, dict):
154
- continue
155
- rel = str(e.get("relation_type") or e.get("relation") or "").strip().lower()
156
- if rel != SEPARATION_REL:
157
- continue
158
- t = e.get("target") or e.get("to") or e.get("id")
159
- if t and str(t) not in out:
160
- out.append(str(t))
161
- return out
162
-
163
-
164
- # 生效条件:fm 与 content 给出时,以 content(假值按空串)、fm.get("condition_space")、fm.get("non_applicable_conditions") 解析,返回去重后的 (set(pos), set(neg));
165
- def condition_terms(fm, content):
166
- """节点声明的(正条件, 负条件)词面集合——分离判定与重构取线索共用。"""
167
- pos, neg = _new_terms(content or "", fm.get("condition_space"),
168
- fm.get("non_applicable_conditions"))
169
- return set(pos), set(neg)
170
-
171
-
172
- # 生效条件:pos_a、neg_a、pos_b、neg_b 给出的条件词面小写化后,两侧并集均空时返回 overlap 0.0、distinct False;否则计算交集/并集比并返回 distinct 为 overlap <= CLASH_LOW;
173
- def condition_distinct(pos_a, neg_a, pos_b, neg_b):
174
- """两组条件是否「实质不同」:任一侧条件词面几乎不重合即为不同情境。
175
-
176
- 返回 {'overlap': 覆盖率, 'distinct': bool}。overlap 高 = 条件相同(是重复,
177
- 该合并);overlap 低 = 条件不同(该分离,避免混为一体)。
178
- """
179
- a = {str(x).lower() for x in (set(pos_a) | set(neg_a))}
180
- b = {str(x).lower() for x in (set(pos_b) | set(neg_b))}
181
- if not a and not b:
182
- return {"overlap": 0.0, "distinct": False,
183
- "note": "双方均未声明条件:无从判定分离(不假装确定)"}
184
- union = a | b
185
- overlap = (len(a & b) / float(len(union))) if union else 0.0
186
- return {"overlap": round(overlap, 4), "distinct": overlap <= CLASH_LOW}
187
-
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 返回;
190
- def _new_terms(content, condition_space, non_applicable_conditions):
191
- """新节点声明的(正条件, 负条件)——与既有节点同口径解析。"""
192
- _ccg_field, _declared, _neg_hit, _cov = _prims()
193
- pos, neg = [], []
194
- v = _ccg_field(content or "", "生效条件")
195
- if v:
196
- pos.append(v)
197
- for k, val in (condition_space or {}).items():
198
- if str(k).startswith("__") or k == "time_window":
199
- continue
200
- if isinstance(val, (list, tuple, set)):
201
- pos.extend(str(x) for x in val)
202
- elif val not in (None, ""):
203
- pos.append(str(val))
204
- neg.extend(str(x) for x in (non_applicable_conditions or []))
205
- v2 = _ccg_field(content or "", "不适用条件")
206
- if v2:
207
- neg.append(v2)
208
- return _dedup(pos), _dedup(neg)
209
-
210
-
211
- # 生效条件:content 与 neg_texts 给出时,neg_texts 中任一短语去掉「不得/禁止/严禁/不能/不可/不要/勿」前缀且长度 >=2 后作为子串出现在 content(假值按空串)中则返回 True,否则 False;
212
- def _ban_hit(content, neg_texts):
213
- """纪律禁令命中:去掉「不得/禁止/…」前缀后,短语是否**整体出现**在正文中。
214
-
215
- 比词袋匹配更严格——纪律条目通常是精确的禁令短语,用子串命中可避免
216
- 「生产」这类子词把无关内容误判为违纪(假阳性会毁掉纪律的可信度)。
217
- """
218
- for x in (neg_texts or []):
219
- s = str(x).strip()
220
- for p in ("不得", "禁止", "严禁", "不能", "不可", "不要", "勿"):
221
- if s.startswith(p):
222
- s = s[len(p):].strip()
223
- break
224
- if len(s) >= 2 and s in (content or ""):
225
- return True
226
- return False
227
-
228
-
229
- # 生效条件:content 为真时按行过滤,跳过 strip 后以 "#" 开头且含全角或半角冒号的行,其余原行以换行连接返回;content 假值按空串返回 "";
230
- def _body_text(content):
231
- """去掉 CCG 声明行(`# 字段:值`)后的正文。
232
-
233
- 自否定看的是「正文/生效条件是否与不适用条件矛盾」,不能把节点自己声明的
234
- `# 不适用条件:X` 当成 X 出现在正文里——否则**每个**声明了不适用条件的
235
- 正常节点都会被误判为自相矛盾(真实 CCG 条目普遍带该字段)。
236
- """
237
- out = []
238
- for line in (content or "").splitlines():
239
- s = line.strip()
240
- if s.startswith("#") and (":" in s or ":" in s):
241
- continue
242
- out.append(line)
243
- return "\n".join(out)
244
-
245
-
246
- # 生效条件:content 给出时(假值按空串)解析 CCG 的 `# 功能名` 与 `# 子功能` 字段并返回两者;
247
- def _slot_text(content):
248
- """结论槽:CCG 声明的 `# 功能名` / `# 子功能`(结构字段,非正文词面)。
249
-
250
- 为什么需要它:L1-c 要判「两条记忆是不是在讲同一件事」,但**词面覆盖率测不了**
251
- ——探针实测「同属性不同值」0.612 vs「异属性」0.623(负样本反超),
252
- 因为两者与旧正文的通用词重合度相当。CCG 的槽位字段才是「在讲哪件事」的
253
- 结构化代理,与 L1-b 用 condition_space 判「在什么条件下」同构。
254
-
255
- 字段缺失 → 返回空串 → L1-c 不触发(保守:无从比对时不假装确定)。
256
- """
257
- _ccg_field, _declared, _neg_hit, _cov = _prims()
258
- return (_ccg_field(content or "", "功能名"),
259
- _ccg_field(content or "", "子功能"))
260
-
261
-
262
- # --------------------------------------------------------------------------
263
- # L0 情绪通道(信息差二阶变化)
264
- # --------------------------------------------------------------------------
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;
267
- def emotional_bias(conflict_strength, prev_strength=None):
268
- """L0:把冲突强度映射为情绪倾向(approaching / stable / avoiding)。
269
-
270
- 对齐 `智能的公理化基石.md` §十一:情绪是**信息差的二阶变化** d²D/dt²。
271
- 这里的工程代理:
272
- · 一阶 d1 = conflict_strength(新信息与既有结构的相斥程度)
273
- · 二阶 d2 = 本次 d1 − 上次 d1(用留痕里的上一条强度作基线)
274
- · d2 > 0 → 信息差在**扩大**(越来越不顺)→ avoiding
275
- · d2 < 0 → 信息差在**收敛**(越来越顺) → approaching
276
-
277
- 重要(原文强制):情绪通道**独立、不参与信任/资格计算**,
278
- 只影响「是否升级到递归反思」的调度决策。
279
- """
280
- c = max(0.0, min(1.0, float(conflict_strength or 0.0)))
281
- if prev_strength is None:
282
- d2 = 0.0
283
- else:
284
- d2 = c - max(0.0, min(1.0, float(prev_strength)))
285
- if c >= EMO_AVOID or d2 > 0.2:
286
- bias = "avoiding"
287
- elif c <= EMO_APPROACH and d2 <= 0:
288
- bias = "approaching"
289
- else:
290
- bias = "stable"
291
- return {"bias": bias, "conflict_strength": round(c, 4),
292
- "d2": round(d2, 4),
293
- "note": "情绪通道独立,不参与信任/资格计算(智能论 §十一)"}
294
-
295
-
296
- # 生效条件:cg 给出时,若 cg.root 下 LOG_FILE 可读,则逐行解析 JSON 并取 .get("conflict_strength"),返回最后一条可解析记录的该键值(缺键或值为 None 则为 None);路径不可用或 OSError 返回 None;
297
- def _last_strength(cg):
298
- """上一条留痕的冲突强度(二阶差分的基线)。流式读,不载全量。"""
299
- p = os.path.join(cg.root, LOG_FILE)
300
- if not os.path.exists(p):
301
- return None
302
- last = None
303
- try:
304
- with open(p, "r", encoding="utf-8") as f:
305
- for line in f:
306
- line = line.strip()
307
- if not line:
308
- continue
309
- try:
310
- last = json.loads(line).get("conflict_strength")
311
- except (ValueError, TypeError):
312
- pass
313
- except OSError:
314
- return None
315
- return last
316
-
317
-
318
- # --------------------------------------------------------------------------
319
- # L2 递归反思(受深度 / 节点数 / 循环 / 增益门槛约束)
320
- # --------------------------------------------------------------------------
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);
323
- def _recursive_reflect(cg, seeds, tw_pos, tw_neg, max_depth=MAX_DEPTH,
324
- max_nodes=MAX_NODES, min_gain=MIN_GAIN):
325
- """递归反思:沿关系链找「区分条件」,每层检查信息增益。
326
-
327
- 收敛条件(任一):
328
- · 找到区分节点(其负条件排除新节点 / 其正条件与新负条件互斥)→ resolved
329
- · 增益 < min_gain(候选空间没变少,继续搜没意义,对齐 :273)→ 停
330
- · 超深度 / 超节点预算 / 循环检测命中 → 停
331
- """
332
- _ccg_field, _declared, _neg_hit, _cov = _prims()
333
- seed_set = {s for s in (seeds or []) if s}
334
- visited, frontier, trace = set(), [s for s in (seeds or []) if s], []
335
- nodes_visited, last_gain = 0, 0.0
336
- for d in range(1, int(max_depth) + 1):
337
- if not frontier:
338
- return {"resolved_by": [], "depth": d - 1, "nodes_visited": nodes_visited,
339
- "gain": last_gain, "stopped_by": "frontier_exhausted",
340
- "trace": trace}
341
- nxt, discriminators = [], []
342
- for nid in frontier:
343
- if nid in visited:
344
- continue
345
- visited.add(nid)
346
- nodes_visited += 1
347
- if nodes_visited > max_nodes:
348
- return {"resolved_by": [], "depth": d,
349
- "nodes_visited": nodes_visited, "gain": last_gain,
350
- "stopped_by": "node_budget", "trace": trace}
351
- node = cg.get(nid) or {}
352
- fm = node.get("frontmatter") or {}
353
- body = node.get("content") or ""
354
- e_pos, e_neg = _declared(fm, body)
355
- # 冲突源(seed)自身不是区分条件:它与新节点的关系正是待分辨的
356
- # 冲突本身,把它当作「已分辨」会制造假的 resolved(增益虚高 1.0)。
357
- if nid not in seed_set:
358
- if e_neg and tw_pos and _cov(tw_pos, " ".join(e_neg)) >= CLASH_HIGH:
359
- discriminators.append(nid)
360
- elif e_pos and tw_neg and _cov(tw_neg, " ".join(e_pos)) >= CLASH_HIGH:
361
- discriminators.append(nid)
362
- nxt.extend(_edge_targets(fm))
363
- if discriminators:
364
- return {"resolved_by": discriminators, "depth": d,
365
- "nodes_visited": nodes_visited, "gain": 1.0,
366
- "stopped_by": "resolved", "trace": trace}
367
- fresh = [x for x in dict.fromkeys(nxt) if x not in visited]
368
- last_gain = len(fresh) / float(max(1, len(frontier)))
369
- trace.append({"depth": d, "frontier": len(frontier),
370
- "fresh": len(fresh), "gain": round(last_gain, 4)})
371
- if last_gain < min_gain:
372
- return {"resolved_by": [], "depth": d, "nodes_visited": nodes_visited,
373
- "gain": round(last_gain, 4),
374
- "stopped_by": "gain_below_threshold", "trace": trace}
375
- frontier = fresh
376
- return {"resolved_by": [], "depth": int(max_depth),
377
- "nodes_visited": nodes_visited, "gain": round(last_gain, 4),
378
- "stopped_by": "depth_exceeded", "trace": trace}
379
-
380
-
381
- # --------------------------------------------------------------------------
382
- # 主入口:三级决策
383
- # --------------------------------------------------------------------------
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;
386
- def check(cg, content, layer=None, condition_space=None,
387
- non_applicable_conditions=None, tags=None, exclude=None,
388
- limit=MAX_SCAN, depth=MAX_DEPTH, auto_flywheel=False,
389
- query=None):
390
- """节点间自动冲突检测(L0 情绪 → L1 反思 → L2 递归反思)。
391
-
392
- 返回完整判据(可审计):
393
- verdict / reason / conflict_strength / emotional / conflicts[] /
394
- recursion{} / missing[] / unresolved_id(若触发飞轮)
395
-
396
- verdict:
397
- ACCEPT 无冲突,或新节点未声明条件(无从冲突)
398
- REJECT 硬冲突:自否定 / 违反纪律
399
- DEFER 条件互斥但可能可分辨(交给 L2 递归或飞轮)
400
- BLINDSPOT 有条件声明,但既有节点全无声明 → 无法建立比对路径(不假装确定)
401
- """
402
- _ccg_field, _declared, _neg_hit, _cov = _prims()
403
- content = content or ""
404
- pos, neg = _new_terms(content, condition_space, non_applicable_conditions)
405
- tw_pos = expand_query_terms_weighted(" ".join(pos)) if pos else {}
406
- tw_neg = expand_query_terms_weighted(" ".join(neg)) if neg else {}
407
- # 结论文本词权(结论比对专用):**去 CCG 声明行**。模板行(`# 功能名` /
408
- # `# 子功能` 等)在两条节点间逐字相同,若混入会稀释结论覆盖率——实测
409
- # 逐字重复仅 0.32、同槽不同值 0.61,与异属性(0.62)不可分。去模板才可判。
410
- tw_content = (expand_query_terms_weighted(_body_text(content))
411
- if content else {})
412
- # 结论槽(CCG 结构字段)——「是否同一件事」的代理,见 _slot_text 说明
413
- n_fn, n_sb = _slot_text(content)
414
-
415
- conflicts, hard, divergences = [], [], []
416
- strength, scanned, comparable = 0.0, 0, 0
417
-
418
- # ---- L1-a 自否定:自己的负条件排除自己的生效条件/正文 ----
419
- body = _body_text(content)
420
- if neg and (_ban_hit(" ".join(pos), neg) or _ban_hit(body, neg)):
421
- hard.append({"type": "self_negation", "with": None,
422
- "detail": "不适用条件命中自身生效条件/正文:条件自相矛盾",
423
- "score": 1.0})
424
- strength = 1.0
425
- elif neg and _cov(tw_neg, body) >= SELF_NEGATION:
426
- hard.append({"type": "self_negation", "with": None,
427
- "detail": "负条件与正文强相关:条件与结论互斥",
428
- "score": round(_cov(tw_neg, body), 4)})
429
- strength = max(strength, round(_cov(tw_neg, body), 4))
430
-
431
- # ---- L1-b 与既有节点的条件级比对(反题) ----
432
- nodes = ((getattr(cg, "index", None) or {}).get("nodes") or {})
433
- seeds = []
434
- for nid, e in nodes.items():
435
- if exclude and nid == exclude:
436
- continue
437
- if layer and e.get("layer") != layer:
438
- continue
439
- if scanned >= int(limit):
440
- break
441
- scanned += 1
442
- node = cg.get(nid) or {}
443
- fm = node.get("frontmatter") or {}
444
- body = node.get("content") or ""
445
- e_pos, e_neg = _declared(fm, body)
446
- if not e_pos and not e_neg:
447
- continue
448
- comparable += 1
449
- # 纪律违反:**正文行为**命中纪律节点的不适用条件(negative.reject)
450
- # —— 这是「不能违反纪律」,与「条件互斥」是两回事:前者看做了什么,
451
- # 后者看声明的条件是否互相覆盖。
452
- if _is_discipline(fm, nid) and e_neg and _ban_hit(content, e_neg):
453
- hard.append({"type": "discipline", "with": nid,
454
- "with_layer": e.get("layer"),
455
- "detail": "命中纪律节点的不适用条件(negative.reject)",
456
- "score": 1.0})
457
- seeds.append(nid)
458
- strength = 1.0
459
- continue
460
- c1 = _cov(tw_pos, " ".join(e_neg)) if (tw_pos and e_neg) else 0.0
461
- c2 = _cov(tw_neg, " ".join(e_pos)) if (tw_neg and e_pos) else 0.0
462
- c = max(c1, c2)
463
- # ---- L1-c 同侧比对:第二类关系(同条件空间 · 结论槽取值分歧) ----
464
- # 与 L1-b 是**两类不同关系**:L1-b 判「条件互斥」(反题),L1-c 判
465
- # 「同条件 + 同结论槽 + 取值不同」(矛盾)。后者在词面不可判——探针实测
466
- # 「同属性不同值」正文覆盖率 0.612 反而低于「异属性」0.623,故改用
467
- # CCG 槽位字段代理「是否同一件事」。
468
- # 注:必须是独立 if 而非 else——同条件时 c=0,若并入 L1-b 分支会被
469
- # `c < CLASH_LOW: continue` 提前跳过,L1-c 永不执行。
470
- c3 = _cov(tw_pos, " ".join(e_pos)) if (tw_pos and e_pos) else 0.0
471
- c4 = _cov(tw_neg, " ".join(e_neg)) if (tw_neg and e_neg) else 0.0
472
- same_cond = max(c3, c4)
473
- if same_cond >= SAME_COND_HIGH:
474
- e_fn, e_sb = _slot_text(body)
475
- slot = max(
476
- _cov(expand_query_terms_weighted(n_fn), e_fn)
477
- if (n_fn and e_fn) else 0.0,
478
- _cov(expand_query_terms_weighted(n_sb), e_sb)
479
- if (n_sb and e_sb) else 0.0)
480
- if slot >= SLOT_HIGH:
481
- # 同口径比对(正文↔正文);tw_content 已去模板行,见上文
482
- concl = _cov(tw_content, _body_text(body)) if tw_content else 0.0
483
- if concl < CONCLUSION_SAME:
484
- divergences.append({
485
- "type": "same_condition_divergence", "with": nid,
486
- "with_layer": e.get("layer"),
487
- "detail": ("适用条件与结论槽均重合但取值不同:"
488
- "可能是更正(应覆盖旧值)或分歧(应补区分条件)"),
489
- "same_condition": round(same_cond, 4),
490
- "slot_overlap": round(slot, 4),
491
- "conclusion_overlap": round(concl, 4),
492
- "slot": (n_fn or n_sb)[:40]})
493
- if c < CLASH_LOW:
494
- continue
495
- conflicts.append({
496
- "type": "condition_clash", "with": nid,
497
- "with_layer": e.get("layer"),
498
- "detail": ("新节点适用条件落在既有节点不适用区"
499
- if c1 >= c2 else "新节点不适用条件覆盖既有节点适用区"),
500
- "score": round(c, 4),
501
- "pos_side": _dedup(pos)[:6], "neg_side": _dedup(neg)[:6]})
502
- seeds.append(nid)
503
- strength = max(strength, c)
504
-
505
- # ---- L0 情绪(独立通道,只调度不裁决) ----
506
- emo = emotional_bias(strength, _last_strength(cg))
507
-
508
- # ---- L1 四态判定 ----
509
- recursion = None
510
- missing = []
511
- allc = hard + conflicts + divergences
512
- if hard:
513
- verdict = "REJECT"
514
- reason = ";".join(h["detail"] for h in hard)
515
- elif divergences:
516
- verdict = "DEFER"
517
- reason = "同条件空间下结论槽取值不一致:需裁决是更正还是分支"
518
- # 两类关系可能同时成立(同条件分歧 vs 某节点 / 条件互斥 vs 另节点)——
519
- # 若只报 L1-c,跨侧互斥的线索会被掩盖,飞轮收到的缺口就不完整。
520
- if conflicts:
521
- reason += f';另有 {len(conflicts)} 处条件互斥(见 conflicts[])'
522
- elif strength >= CLASH_HIGH:
523
- verdict = "DEFER"
524
- reason = "条件互斥:需补区分条件后才能判定"
525
- elif strength >= CLASH_LOW:
526
- verdict = "DEFER"
527
- reason = "条件部分覆盖:待确认是否互斥"
528
- elif comparable == 0 and (pos or neg):
529
- verdict = "BLINDSPOT"
530
- reason = "既有节点均未声明条件:无法建立比对路径"
531
- else:
532
- verdict = "ACCEPT"
533
- reason = "无冲突"
534
-
535
- # ---- L2 递归反思(仅对 DEFER,且情绪未指向 approaching 的快速放行) ----
536
- if verdict == "DEFER" and int(depth) > 0 and emo["bias"] != "approaching":
537
- recursion = _recursive_reflect(cg, seeds, tw_pos, tw_neg,
538
- max_depth=int(depth))
539
- if recursion["stopped_by"] == "resolved":
540
- # 找到区分条件 → 冲突可分辨,降级为待定(不直接放行,留人工/飞轮)
541
- reason = f'{reason};已找到区分条件 {recursion["resolved_by"][:3]}'
542
- else:
543
- missing.append({
544
- "need": "区分条件",
545
- "why": f'递归反思停止于 {recursion["stopped_by"]}'
546
- f'(深度 {recursion["depth"]},增益 {recursion["gain"]})',
547
- "conflicts": [c["with"] for c in allc if c.get("with")][:5]})
548
-
549
- # 同条件取值分歧:L2 递归提供不了「区分条件」(条件本就相同),故缺口是
550
- # 「取值裁决」而非「补条件」。必须显式落 missing——否则 strength=0 →
551
- # 情绪 approaching → L2 被跳过 → missing 为空 → 飞轮收不到任何信号。
552
- if divergences and not missing:
553
- missing.append({
554
- "need": "取值裁决(同条件·同结论槽存在多条不同取值)",
555
- "why": "适用条件与结论槽均重合而正文取值不同:"
556
- "需确认是「更正」(应覆盖旧值)还是「分支」(应补区分条件)",
557
- "conflicts": [c["with"] for c in allc if c.get("with")][:5]})
558
-
559
- rec = {"t": time.time(), "layer": layer, "verdict": verdict,
560
- "reason": reason, "conflict_strength": round(strength, 4),
561
- "emotional": emo, "conflicts": allc, "recursion": recursion,
562
- "missing": missing, "scanned": scanned, "comparable": comparable,
563
- "pos": pos[:6], "neg": neg[:6],
564
- "actor": getattr(cg, "actor", "unknown")}
565
-
566
- # ---- 冲突自动触发飞轮(误差 → 补条件 → 结构更新) ----
567
- if auto_flywheel and verdict in ("REJECT", "DEFER", "BLINDSPOT"):
568
- rec["unresolved_id"] = _fire_flywheel(cg, query or content, verdict,
569
- reason, missing, allc)
570
- log(cg, rec)
571
- return rec
572
-
573
-
574
- # 生效条件:missing 为假值(None/空容器/空串)返回 "";否则逐项仅取 m.get("need") 真值者,格式化为 `need(why)`(why 缺键用 "",值为 None 则格式化 "None"),以 ";" 连接;
575
- def _missing_text(missing):
576
- """结构化缺口 → 一行文本(作为飞轮的 known_clues)。
577
-
578
- 修断链:原先 `missing=reason` 把「人话」塞进 missing 字段,而判定器产出的
579
- [{need, why, conflicts}] 落在 detail 里被 flywheel_step 忽略——上游产出了
580
- 结构,却送不到下游。
581
- """
582
- if not missing:
583
- return ""
584
- return ";".join(f'{m.get("need", "")}({m.get("why", "")})'
585
- for m in missing if m.get("need"))
586
-
587
-
588
- # 生效条件:cg 提供可调用 flywheel_step 时,以 query(假值按空串)前 200 字符、verdict、missing 文本(空则 reason)、conflicts 非 None 则作 detail 否则 missing 调用;结果 dict 的 unresolved_id 真值时返回它,否则回落 id;无 flywheel_step 或异常返回 None;
589
- def _fire_flywheel(cg, query, verdict, reason, missing, conflicts=None):
590
- """把冲突作为「误差」投给知识飞轮,返回 unresolved 条目 id(失败不阻塞写入)。
591
-
592
- conflicts:冲突现场(含 type / with / same_condition 等数值),供下游生成
593
- `# 现场:`;missing 只描述「缺什么」,不带现场,故两者都要送。
594
- """
595
- step = getattr(cg, "flywheel_step", None)
596
- if step is None:
597
- return None
598
- try:
599
- r = step({"query": (query or "")[:200], "expected_state": "ACCEPT",
600
- "actual_state": verdict,
601
- "missing": _missing_text(missing) or reason,
602
- "detail": conflicts if conflicts is not None else missing})
603
- if isinstance(r, dict):
604
- return r.get("unresolved_id") or r.get("id")
605
- except Exception:
606
- return None
607
- return None
608
-
609
-
610
- # --------------------------------------------------------------------------
611
- # 留痕 / 统计 / 自描述
612
- # --------------------------------------------------------------------------
613
-
614
- # 生效条件:cg 与 rec 给出时,将 rec 序列化为 JSON 行追加到 cg.root 下 LOG_FILE,OSError 被吞掉,返回 rec;
615
- def log(cg, rec):
616
- """append-only 留痕:每条判定可审计。"""
617
- p = os.path.join(cg.root, LOG_FILE)
618
- try:
619
- with open(p, "a", encoding="utf-8") as f:
620
- f.write(json.dumps(rec, ensure_ascii=False) + "\n")
621
- except OSError:
622
- pass
623
- return rec
624
-
625
-
626
- # 生效条件:cg 给出时,若 cg.root 下 LOG_FILE 可读,则逐行解析 JSON(空行/解析失败跳过);limit 真值时返回 out[-int(limit):][::-1](int(limit)=0 时为全部倒序),limit 为假值(0/None/"")时返回 out[::-1];路径不可用或 OSError 返回 [];
627
- def history(cg, limit=100):
628
- """最近冲突判定留痕(倒序)。"""
629
- p = os.path.join(cg.root, LOG_FILE)
630
- if not os.path.exists(p):
631
- return []
632
- out = []
633
- try:
634
- with open(p, "r", encoding="utf-8") as f:
635
- for line in f:
636
- line = line.strip()
637
- if not line:
638
- continue
639
- try:
640
- out.append(json.loads(line))
641
- except (ValueError, TypeError):
642
- pass
643
- except OSError:
644
- return []
645
- return out[-int(limit):][::-1] if limit else out[::-1]
646
-
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;
649
- def summary(cg):
650
- """冲突面汇总(流式计数,供 health 审计)。"""
651
- p = os.path.join(cg.root, LOG_FILE)
652
- by_verdict, by_bias, total = {}, {}, 0
653
- if os.path.exists(p):
654
- try:
655
- with open(p, "r", encoding="utf-8") as f:
656
- for line in f:
657
- line = line.strip()
658
- if not line:
659
- continue
660
- try:
661
- r = json.loads(line)
662
- except (ValueError, TypeError):
663
- continue
664
- total += 1
665
- v = r.get("verdict")
666
- by_verdict[v] = by_verdict.get(v, 0) + 1
667
- b = (r.get("emotional") or {}).get("bias")
668
- if b:
669
- by_bias[b] = by_bias.get(b, 0) + 1
670
- except OSError:
671
- pass
672
- return {"records": total, "by_verdict": by_verdict, "by_bias": by_bias,
673
- "max_scan": MAX_SCAN, "max_depth": MAX_DEPTH,
674
- "min_gain": MIN_GAIN}
675
-
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 等模块级常量的自描述字典;
678
- def catalog():
679
- """自描述:三级决策 + 四态 + 递归约束(供 MCP / 文档对照验证)。"""
680
- return {
681
- "levels": {
682
- "L0_emotion": {
683
- "theory": "情绪 = 信息差二阶变化 d²D/dt²(智能论 §十一)",
684
- "outputs": ["approaching", "stable", "avoiding"],
685
- "constraint": "独立通道,不参与信任/资格计算,只做流程调度",
686
- "thresholds": {"avoid": EMO_AVOID, "approach": EMO_APPROACH},
687
- },
688
- "L1_reflect": {
689
- "theory": "反题 = 预测与事实冲突(条件论七操作)",
690
- "checks": ["self_negation", "discipline", "condition_clash",
691
- "same_condition_divergence"],
692
- "relations": {
693
- "condition_clash": "跨侧比对(新正↔旧负 / 新负↔旧正)= 条件互斥(反题)",
694
- "same_condition_divergence": "同侧比对(新正↔旧正 / 新负↔旧负)"
695
- "+ CCG 结论槽 = 同条件空间内的取值分歧(矛盾)",
696
- },
697
- "verdicts": list(VERDICTS),
698
- "thresholds": {"high": CLASH_HIGH, "low": CLASH_LOW,
699
- "same_condition": SAME_COND_HIGH, "slot": SLOT_HIGH,
700
- "conclusion_same": CONCLUSION_SAME},
701
- },
702
- "L2_recursive_reflect": {
703
- "theory": "递归受深度/节点数/循环/信息增益门槛约束(智能论 :273)",
704
- "max_depth": MAX_DEPTH, "max_nodes": MAX_NODES,
705
- "min_gain": MIN_GAIN,
706
- "stop_reasons": ["resolved", "gain_below_threshold",
707
- "depth_exceeded", "node_budget",
708
- "frontier_exhausted"],
709
- },
710
- },
711
- "auto_flywheel": {
712
- "theory": "知识飞轮:误差 → 补条件 → 结构更新(智能论 :725)",
713
- "triggers_on": ["REJECT", "DEFER", "BLINDSPOT"],
714
- },
715
- "discipline_detection": {"tags": list(DISCIPLINE_TAGS),
716
- "id_prefixes": ["discipline_", "work_discipline"]},
717
- "honest_boundary": "条件级(结构化)冲突检测,非语义蕴含证明;无法比对时 BLINDSPOT",
1
+ # -*- coding: utf-8 -*-
2
+ """md_cg · 节点间自动冲突检测(三级决策:情绪 → 反思 → 递归反思)
3
+
4
+ 理论出处(本仓原文,非外部知识):
5
+
6
+ · **情绪 = 信息差的二阶变化 d²D/dt²**(`docs/theory/智能的公理化基石.md` §十一,:412-510)
7
+ 工程端对应 `emotional_bias`(approaching / avoiding / stable),并且原文明确:
8
+ 「情绪通道**独立、不参与信任计算**」。故本模块 L0 只做**流程调度**,
9
+ 绝不改动 confidence / 资格判定——避免把情绪混进事实判断。
10
+
11
+ · **反题 = 预测与事实冲突**(同文档 :529,条件论七操作之一)
12
+ —— L1 一次冲突检测的理论名:新信息与既有条件的「反题」关系。
13
+
14
+ · **递归必须受约束**(同文档 :273):
15
+ 「递归必须受到深度、查询次数、节点数、循环检测和信息增益门槛约束。
16
+ 若递归没有减少候选空间,就不应继续搜索。」
17
+ —— L2 递归反思的硬约束(本模块按此实现,而非无限展开)。
18
+
19
+ · **四态路由** ACCEPT / REJECT / DEFER / BLINDSPOT(同文档 :721;
20
+ `md_cg/mdcg.py` 四态定义)—— L1 的输出语义。
21
+
22
+ · **知识飞轮:误差 → 补条件 → 结构更新**(同文档 :725;`mdcg.py:1001`
23
+ `flywheel_step`)—— 冲突**自动**落 unresolved,误差是结构更新的输入。
24
+
25
+ · **纪律四要素同构**(`docs/工作纪律_认知图条目_v1.1.json`;
26
+ `consolidate.py` CCG 四要素)—— 「不能违反纪律」= 新内容不得命中纪律节点的
27
+ `negative.reject`(不适用条件)。纪律节点用 tag/前缀识别,不硬编码具体条目。
28
+
29
+ 三级决策(顺序即语义,越靠前越廉价):
30
+
31
+ L0 情绪 :二阶信号,快速方向调整(approaching / stable / avoiding)——不裁决资格
32
+ L1 反思 :一次条件级冲突检测(自否定 / 纪律违反 / 条件互斥)→ 四态
33
+ L2 递归反思:L1 未决 → 沿关系链递归找「区分条件」;受深度/节点数/循环/增益门槛约束
34
+
35
+ 冲突自动触发飞轮:verdict ∈ {REJECT, DEFER, BLINDSPOT} 且 auto_flywheel →
36
+ `cg.flywheel_step({query, expected_state:"ACCEPT", actual_state:verdict, missing})`
37
+
38
+ 留痕 `_consistency.jsonl`(append-only):每条判定可审计「为什么冲突 / 为什么放行」。
39
+
40
+ 诚实边界:本模块是**条件级(结构化)**冲突检测,不是语义蕴含证明。它判断的是
41
+ 「声明的适用/不适用条件是否互相覆盖」,而非「两句话在逻辑上是否矛盾」。
42
+ 无法建立可比对路径时返回 BLINDSPOT,不假装确定。
43
+ """
44
+ from __future__ import annotations
45
+
46
+ import json
47
+ import os
48
+ import time
49
+
50
+ from .mdcg import expand_query_terms_weighted
51
+
52
+ # --------------------------------------------------------------------------
53
+ # 常量(全部可审计、可调)
54
+ # --------------------------------------------------------------------------
55
+
56
+ LOG_FILE = "_consistency.jsonl"
57
+
58
+ MAX_SCAN = 200 # 单次检测最多比对的既有节点数(防 O(N) 爆炸)
59
+ MAX_DEPTH = 3 # L2 递归深度上限(对齐 :273)
60
+ MAX_NODES = 60 # L2 递归展开节点上限(对齐 :273)
61
+ MIN_GAIN = 0.15 # 信息增益门槛:候选空间减少比例低于此值即停(对齐 :273)
62
+
63
+ CLASH_HIGH = 0.6 # 条件互相覆盖阈值 → 明确互斥
64
+ CLASH_LOW = 0.35 # 条件部分覆盖阈值 → 待定
65
+ SELF_NEGATION = 0.5 # 自否定阈值:负条件被自身正文强命中
66
+
67
+ # —— 第二类关系(同条件空间 · 结论槽取值分歧)阈值;均由探针实测标定 ——
68
+ # run_probe_samecond.py 实测:同条件 same_cond=1.0 / 异条件空间 0.328 → 0.75 可分
69
+ SAME_COND_HIGH = 0.75 # 同侧条件重合 → 判定「同一条件空间」
70
+ # 实测结论槽(子功能):同槽 1.0 / 同主语异属性 0.14 / 异主题 0.0 → 0.6 可分
71
+ SLOT_HIGH = 0.6 # 结论槽(功能名/子功能)重合 → 判定「同一件事」
72
+ # 实测:逐字重复 1.0 / 同槽不同值 0.61 / 同值异措辞 0.74 → 0.95 只排除逐字重复
73
+ CONCLUSION_SAME = 0.95 # 正文几乎逐字相同 → 属「重复」(该合并),不算冲突
74
+
75
+ EMO_AVOID = 0.70 # 冲突强度 ≥ 此值 → avoiding
76
+ EMO_APPROACH = 0.30 # 冲突强度 ≤ 此值 → approaching
77
+
78
+ # 纪律节点识别(tag 或 id 前缀;不硬编码具体纪律条目)
79
+ DISCIPLINE_TAGS = ("discipline", "纪律", "work_discipline", "rule", "规则", "戒律")
80
+
81
+ VERDICTS = ("ACCEPT", "REJECT", "DEFER", "BLINDSPOT")
82
+
83
+
84
+ # 生效条件:以 verdict 与 reason 构造异常(消息 `[{verdict}] {reason}`),conflicts 传假值(None/空容器等,源码 `conflicts or []`)时 self.conflicts 为 [],传真值时原样保留;
85
+ class ConsistencyError(Exception):
86
+ """硬冲突:写入被拒(自否定 / 违反纪律)。"""
87
+
88
+ # 生效条件:verdict 与 reason 必传并赋给同名属性、拼成异常消息 `[{verdict}] {reason}`;conflicts 为假值(None/空容器等,源码 `conflicts or []`)时 self.conflicts 回落到 [],为真值时原样保留;
89
+ def __init__(self, verdict, reason, conflicts=None):
90
+ self.verdict = verdict
91
+ self.reason = reason
92
+ self.conflicts = conflicts or []
93
+ super().__init__(f"[{verdict}] {reason}")
94
+
95
+
96
+ # --------------------------------------------------------------------------
97
+ # 原语:延迟导入 mdcos(避免 mdcg ← mdcos ← consistency 的循环导入)
98
+ # --------------------------------------------------------------------------
99
+
100
+ # 生效条件:无 required 形参,任一调用返回 ( _ccg_field, _declared_conditions, _neg_hit, _weighted_coverage ) 四元组;
101
+ def _prims():
102
+ """取 md_cg 已有的条件匹配原语(条件论「反题」的既有实现)。"""
103
+ from .mdcos import (_ccg_field, _declared_conditions, _neg_hit,
104
+ _weighted_coverage)
105
+ return _ccg_field, _declared_conditions, _neg_hit, _weighted_coverage
106
+
107
+
108
+ # 生效条件:xs 为可迭代对象时,逐项 str(x).strip() 后仅当结果非空且未出现过才加入 out 并返回 out;xs 为 None/False/0 时在 for 处迭代失败(TypeError);
109
+ def _dedup(xs):
110
+ out = []
111
+ for x in xs:
112
+ s = str(x).strip()
113
+ if s and s not in out:
114
+ out.append(s)
115
+ return out
116
+
117
+
118
+ # 生效条件:fm 给出时,遍历 fm.get("edges")(缺键或假值按空列表)中元素,dict 元素按 to→target→node→id 取首个真值、非 dict 元素直接作候选,候选真值时 str(t) 加入 out 并返回;
119
+ def _edge_targets(fm):
120
+ """节点声明的出边目标(兼容 dict / str 两种形态)。"""
121
+ out = []
122
+ for e in (fm.get("edges") or []):
123
+ if isinstance(e, dict):
124
+ t = e.get("to") or e.get("target") or e.get("node") or e.get("id")
125
+ else:
126
+ t = e
127
+ if t:
128
+ out.append(str(t))
129
+ return out
130
+
131
+
132
+ # 生效条件:fm 与 node_id 给出时,若 fm.get("tags")(缺键/假值按空)小写后与 DISCIPLINE_TAGS 有交集,或 str(node_id) 以 "discipline_" 或 "work_discipline" 开头,返回 True;否则返回 False;
133
+ def _is_discipline(fm, node_id):
134
+ tags = {str(t).lower() for t in (fm.get("tags") or [])}
135
+ if tags & set(DISCIPLINE_TAGS):
136
+ return True
137
+ return str(node_id).startswith(("discipline_", "work_discipline"))
138
+
139
+
140
+ # --------------------------------------------------------------------------
141
+ # 模式分离的支持件(maintain.separate 复用;也供 insight.reconstruct 取线索)
142
+ # --------------------------------------------------------------------------
143
+
144
+ #: 分离边的关系名:两个节点「看起来像,但条件不同,互为不同情境」
145
+ SEPARATION_REL = "distinct_from"
146
+
147
+
148
+ # 生效条件:fm 给出时,仅处理 fm.get("edges")(缺键/假值按空)中的 dict 元素,relation_type 或 relation(缺省空串)小写等于 SEPARATION_REL 时,按 target→to→id 取首个真值且未重复则加入 out;
149
+ def separation_targets(fm):
150
+ """节点已声明的分离对象(`distinct_from` 出边),用于幂等与去重。"""
151
+ out = []
152
+ for e in (fm.get("edges") or []):
153
+ if not isinstance(e, dict):
154
+ continue
155
+ rel = str(e.get("relation_type") or e.get("relation") or "").strip().lower()
156
+ if rel != SEPARATION_REL:
157
+ continue
158
+ t = e.get("target") or e.get("to") or e.get("id")
159
+ if t and str(t) not in out:
160
+ out.append(str(t))
161
+ return out
162
+
163
+
164
+ # 生效条件:fm 与 content 给出时,以 content(假值按空串)、fm.get("condition_space")、fm.get("non_applicable_conditions") 解析,返回去重后的 (set(pos), set(neg));
165
+ def condition_terms(fm, content):
166
+ """节点声明的(正条件, 负条件)词面集合——分离判定与重构取线索共用。"""
167
+ pos, neg = _new_terms(content or "", fm.get("condition_space"),
168
+ fm.get("non_applicable_conditions"))
169
+ return set(pos), set(neg)
170
+
171
+
172
+ # 生效条件:pos_a、neg_a、pos_b、neg_b 给出的条件词面小写化后,两侧并集均空时返回 overlap 0.0、distinct False;否则计算交集/并集比并返回 distinct 为 overlap <= CLASH_LOW;
173
+ def condition_distinct(pos_a, neg_a, pos_b, neg_b):
174
+ """两组条件是否「实质不同」:任一侧条件词面几乎不重合即为不同情境。
175
+
176
+ 返回 {'overlap': 覆盖率, 'distinct': bool}。overlap 高 = 条件相同(是重复,
177
+ 该合并);overlap 低 = 条件不同(该分离,避免混为一体)。
178
+ """
179
+ a = {str(x).lower() for x in (set(pos_a) | set(neg_a))}
180
+ b = {str(x).lower() for x in (set(pos_b) | set(neg_b))}
181
+ if not a and not b:
182
+ return {"overlap": 0.0, "distinct": False,
183
+ "note": "双方均未声明条件:无从判定分离(不假装确定)"}
184
+ union = a | b
185
+ overlap = (len(a & b) / float(len(union))) if union else 0.0
186
+ return {"overlap": round(overlap, 4), "distinct": overlap <= CLASH_LOW}
187
+
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 返回;
190
+ def _new_terms(content, condition_space, non_applicable_conditions):
191
+ """新节点声明的(正条件, 负条件)——与既有节点同口径解析。"""
192
+ _ccg_field, _declared, _neg_hit, _cov = _prims()
193
+ pos, neg = [], []
194
+ v = _ccg_field(content or "", "生效条件")
195
+ if v:
196
+ pos.append(v)
197
+ for k, val in (condition_space or {}).items():
198
+ if str(k).startswith("__") or k == "time_window":
199
+ continue
200
+ if isinstance(val, (list, tuple, set)):
201
+ pos.extend(str(x) for x in val)
202
+ elif val not in (None, ""):
203
+ pos.append(str(val))
204
+ neg.extend(str(x) for x in (non_applicable_conditions or []))
205
+ v2 = _ccg_field(content or "", "不适用条件")
206
+ if v2:
207
+ neg.append(v2)
208
+ return _dedup(pos), _dedup(neg)
209
+
210
+
211
+ # 生效条件:content 与 neg_texts 给出时,neg_texts 中任一短语去掉「不得/禁止/严禁/不能/不可/不要/勿」前缀且长度 >=2 后作为子串出现在 content(假值按空串)中则返回 True,否则 False;
212
+ def _ban_hit(content, neg_texts):
213
+ """纪律禁令命中:去掉「不得/禁止/…」前缀后,短语是否**整体出现**在正文中。
214
+
215
+ 比词袋匹配更严格——纪律条目通常是精确的禁令短语,用子串命中可避免
216
+ 「生产」这类子词把无关内容误判为违纪(假阳性会毁掉纪律的可信度)。
217
+ """
218
+ for x in (neg_texts or []):
219
+ s = str(x).strip()
220
+ for p in ("不得", "禁止", "严禁", "不能", "不可", "不要", "勿"):
221
+ if s.startswith(p):
222
+ s = s[len(p):].strip()
223
+ break
224
+ if len(s) >= 2 and s in (content or ""):
225
+ return True
226
+ return False
227
+
228
+
229
+ # 生效条件:content 为真时按行过滤,跳过 strip 后以 "#" 开头且含全角或半角冒号的行,其余原行以换行连接返回;content 假值按空串返回 "";
230
+ def _body_text(content):
231
+ """去掉 CCG 声明行(`# 字段:值`)后的正文。
232
+
233
+ 自否定看的是「正文/生效条件是否与不适用条件矛盾」,不能把节点自己声明的
234
+ `# 不适用条件:X` 当成 X 出现在正文里——否则**每个**声明了不适用条件的
235
+ 正常节点都会被误判为自相矛盾(真实 CCG 条目普遍带该字段)。
236
+ """
237
+ out = []
238
+ for line in (content or "").splitlines():
239
+ s = line.strip()
240
+ if s.startswith("#") and (":" in s or ":" in s):
241
+ continue
242
+ out.append(line)
243
+ return "\n".join(out)
244
+
245
+
246
+ # 生效条件:content 给出时(假值按空串)解析 CCG 的 `# 功能名` 与 `# 子功能` 字段并返回两者;
247
+ def _slot_text(content):
248
+ """结论槽:CCG 声明的 `# 功能名` / `# 子功能`(结构字段,非正文词面)。
249
+
250
+ 为什么需要它:L1-c 要判「两条记忆是不是在讲同一件事」,但**词面覆盖率测不了**
251
+ ——探针实测「同属性不同值」0.612 vs「异属性」0.623(负样本反超),
252
+ 因为两者与旧正文的通用词重合度相当。CCG 的槽位字段才是「在讲哪件事」的
253
+ 结构化代理,与 L1-b 用 condition_space 判「在什么条件下」同构。
254
+
255
+ 字段缺失 → 返回空串 → L1-c 不触发(保守:无从比对时不假装确定)。
256
+ """
257
+ _ccg_field, _declared, _neg_hit, _cov = _prims()
258
+ return (_ccg_field(content or "", "功能名"),
259
+ _ccg_field(content or "", "子功能"))
260
+
261
+
262
+ # --------------------------------------------------------------------------
263
+ # L0 情绪通道(信息差二阶变化)
264
+ # --------------------------------------------------------------------------
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;
267
+ def emotional_bias(conflict_strength, prev_strength=None):
268
+ """L0:把冲突强度映射为情绪倾向(approaching / stable / avoiding)。
269
+
270
+ 对齐 `智能的公理化基石.md` §十一:情绪是**信息差的二阶变化** d²D/dt²。
271
+ 这里的工程代理:
272
+ · 一阶 d1 = conflict_strength(新信息与既有结构的相斥程度)
273
+ · 二阶 d2 = 本次 d1 − 上次 d1(用留痕里的上一条强度作基线)
274
+ · d2 > 0 → 信息差在**扩大**(越来越不顺)→ avoiding
275
+ · d2 < 0 → 信息差在**收敛**(越来越顺) → approaching
276
+
277
+ 重要(原文强制):情绪通道**独立、不参与信任/资格计算**,
278
+ 只影响「是否升级到递归反思」的调度决策。
279
+ """
280
+ c = max(0.0, min(1.0, float(conflict_strength or 0.0)))
281
+ if prev_strength is None:
282
+ d2 = 0.0
283
+ else:
284
+ d2 = c - max(0.0, min(1.0, float(prev_strength)))
285
+ if c >= EMO_AVOID or d2 > 0.2:
286
+ bias = "avoiding"
287
+ elif c <= EMO_APPROACH and d2 <= 0:
288
+ bias = "approaching"
289
+ else:
290
+ bias = "stable"
291
+ return {"bias": bias, "conflict_strength": round(c, 4),
292
+ "d2": round(d2, 4),
293
+ "note": "情绪通道独立,不参与信任/资格计算(智能论 §十一)"}
294
+
295
+
296
+ # 生效条件:cg 给出时,若 cg.root 下 LOG_FILE 可读,则逐行解析 JSON 并取 .get("conflict_strength"),返回最后一条可解析记录的该键值(缺键或值为 None 则为 None);路径不可用或 OSError 返回 None;
297
+ def _last_strength(cg):
298
+ """上一条留痕的冲突强度(二阶差分的基线)。流式读,不载全量。"""
299
+ p = os.path.join(cg.root, LOG_FILE)
300
+ if not os.path.exists(p):
301
+ return None
302
+ last = None
303
+ try:
304
+ with open(p, "r", encoding="utf-8") as f:
305
+ for line in f:
306
+ line = line.strip()
307
+ if not line:
308
+ continue
309
+ try:
310
+ last = json.loads(line).get("conflict_strength")
311
+ except (ValueError, TypeError):
312
+ pass
313
+ except OSError:
314
+ return None
315
+ return last
316
+
317
+
318
+ # --------------------------------------------------------------------------
319
+ # L2 递归反思(受深度 / 节点数 / 循环 / 增益门槛约束)
320
+ # --------------------------------------------------------------------------
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);
323
+ def _recursive_reflect(cg, seeds, tw_pos, tw_neg, max_depth=MAX_DEPTH,
324
+ max_nodes=MAX_NODES, min_gain=MIN_GAIN):
325
+ """递归反思:沿关系链找「区分条件」,每层检查信息增益。
326
+
327
+ 收敛条件(任一):
328
+ · 找到区分节点(其负条件排除新节点 / 其正条件与新负条件互斥)→ resolved
329
+ · 增益 < min_gain(候选空间没变少,继续搜没意义,对齐 :273)→ 停
330
+ · 超深度 / 超节点预算 / 循环检测命中 → 停
331
+ """
332
+ _ccg_field, _declared, _neg_hit, _cov = _prims()
333
+ seed_set = {s for s in (seeds or []) if s}
334
+ visited, frontier, trace = set(), [s for s in (seeds or []) if s], []
335
+ nodes_visited, last_gain = 0, 0.0
336
+ for d in range(1, int(max_depth) + 1):
337
+ if not frontier:
338
+ return {"resolved_by": [], "depth": d - 1, "nodes_visited": nodes_visited,
339
+ "gain": last_gain, "stopped_by": "frontier_exhausted",
340
+ "trace": trace}
341
+ nxt, discriminators = [], []
342
+ for nid in frontier:
343
+ if nid in visited:
344
+ continue
345
+ visited.add(nid)
346
+ nodes_visited += 1
347
+ if nodes_visited > max_nodes:
348
+ return {"resolved_by": [], "depth": d,
349
+ "nodes_visited": nodes_visited, "gain": last_gain,
350
+ "stopped_by": "node_budget", "trace": trace}
351
+ node = cg.get(nid) or {}
352
+ fm = node.get("frontmatter") or {}
353
+ body = node.get("content") or ""
354
+ e_pos, e_neg = _declared(fm, body)
355
+ # 冲突源(seed)自身不是区分条件:它与新节点的关系正是待分辨的
356
+ # 冲突本身,把它当作「已分辨」会制造假的 resolved(增益虚高 1.0)。
357
+ if nid not in seed_set:
358
+ if e_neg and tw_pos and _cov(tw_pos, " ".join(e_neg)) >= CLASH_HIGH:
359
+ discriminators.append(nid)
360
+ elif e_pos and tw_neg and _cov(tw_neg, " ".join(e_pos)) >= CLASH_HIGH:
361
+ discriminators.append(nid)
362
+ nxt.extend(_edge_targets(fm))
363
+ if discriminators:
364
+ return {"resolved_by": discriminators, "depth": d,
365
+ "nodes_visited": nodes_visited, "gain": 1.0,
366
+ "stopped_by": "resolved", "trace": trace}
367
+ fresh = [x for x in dict.fromkeys(nxt) if x not in visited]
368
+ last_gain = len(fresh) / float(max(1, len(frontier)))
369
+ trace.append({"depth": d, "frontier": len(frontier),
370
+ "fresh": len(fresh), "gain": round(last_gain, 4)})
371
+ if last_gain < min_gain:
372
+ return {"resolved_by": [], "depth": d, "nodes_visited": nodes_visited,
373
+ "gain": round(last_gain, 4),
374
+ "stopped_by": "gain_below_threshold", "trace": trace}
375
+ frontier = fresh
376
+ return {"resolved_by": [], "depth": int(max_depth),
377
+ "nodes_visited": nodes_visited, "gain": round(last_gain, 4),
378
+ "stopped_by": "depth_exceeded", "trace": trace}
379
+
380
+
381
+ # --------------------------------------------------------------------------
382
+ # 主入口:三级决策
383
+ # --------------------------------------------------------------------------
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;
386
+ def check(cg, content, layer=None, condition_space=None,
387
+ non_applicable_conditions=None, tags=None, exclude=None,
388
+ limit=MAX_SCAN, depth=MAX_DEPTH, auto_flywheel=False,
389
+ query=None):
390
+ """节点间自动冲突检测(L0 情绪 → L1 反思 → L2 递归反思)。
391
+
392
+ 返回完整判据(可审计):
393
+ verdict / reason / conflict_strength / emotional / conflicts[] /
394
+ recursion{} / missing[] / unresolved_id(若触发飞轮)
395
+
396
+ verdict:
397
+ ACCEPT 无冲突,或新节点未声明条件(无从冲突)
398
+ REJECT 硬冲突:自否定 / 违反纪律
399
+ DEFER 条件互斥但可能可分辨(交给 L2 递归或飞轮)
400
+ BLINDSPOT 有条件声明,但既有节点全无声明 → 无法建立比对路径(不假装确定)
401
+ """
402
+ _ccg_field, _declared, _neg_hit, _cov = _prims()
403
+ content = content or ""
404
+ pos, neg = _new_terms(content, condition_space, non_applicable_conditions)
405
+ tw_pos = expand_query_terms_weighted(" ".join(pos)) if pos else {}
406
+ tw_neg = expand_query_terms_weighted(" ".join(neg)) if neg else {}
407
+ # 结论文本词权(结论比对专用):**去 CCG 声明行**。模板行(`# 功能名` /
408
+ # `# 子功能` 等)在两条节点间逐字相同,若混入会稀释结论覆盖率——实测
409
+ # 逐字重复仅 0.32、同槽不同值 0.61,与异属性(0.62)不可分。去模板才可判。
410
+ tw_content = (expand_query_terms_weighted(_body_text(content))
411
+ if content else {})
412
+ # 结论槽(CCG 结构字段)——「是否同一件事」的代理,见 _slot_text 说明
413
+ n_fn, n_sb = _slot_text(content)
414
+
415
+ conflicts, hard, divergences = [], [], []
416
+ strength, scanned, comparable = 0.0, 0, 0
417
+
418
+ # ---- L1-a 自否定:自己的负条件排除自己的生效条件/正文 ----
419
+ body = _body_text(content)
420
+ if neg and (_ban_hit(" ".join(pos), neg) or _ban_hit(body, neg)):
421
+ hard.append({"type": "self_negation", "with": None,
422
+ "detail": "不适用条件命中自身生效条件/正文:条件自相矛盾",
423
+ "score": 1.0})
424
+ strength = 1.0
425
+ elif neg and _cov(tw_neg, body) >= SELF_NEGATION:
426
+ hard.append({"type": "self_negation", "with": None,
427
+ "detail": "负条件与正文强相关:条件与结论互斥",
428
+ "score": round(_cov(tw_neg, body), 4)})
429
+ strength = max(strength, round(_cov(tw_neg, body), 4))
430
+
431
+ # ---- L1-b 与既有节点的条件级比对(反题) ----
432
+ nodes = ((getattr(cg, "index", None) or {}).get("nodes") or {})
433
+ seeds = []
434
+ for nid, e in nodes.items():
435
+ if exclude and nid == exclude:
436
+ continue
437
+ if layer and e.get("layer") != layer:
438
+ continue
439
+ if scanned >= int(limit):
440
+ break
441
+ scanned += 1
442
+ node = cg.get(nid) or {}
443
+ fm = node.get("frontmatter") or {}
444
+ body = node.get("content") or ""
445
+ e_pos, e_neg = _declared(fm, body)
446
+ if not e_pos and not e_neg:
447
+ continue
448
+ comparable += 1
449
+ # 纪律违反:**正文行为**命中纪律节点的不适用条件(negative.reject)
450
+ # —— 这是「不能违反纪律」,与「条件互斥」是两回事:前者看做了什么,
451
+ # 后者看声明的条件是否互相覆盖。
452
+ if _is_discipline(fm, nid) and e_neg and _ban_hit(content, e_neg):
453
+ hard.append({"type": "discipline", "with": nid,
454
+ "with_layer": e.get("layer"),
455
+ "detail": "命中纪律节点的不适用条件(negative.reject)",
456
+ "score": 1.0})
457
+ seeds.append(nid)
458
+ strength = 1.0
459
+ continue
460
+ c1 = _cov(tw_pos, " ".join(e_neg)) if (tw_pos and e_neg) else 0.0
461
+ c2 = _cov(tw_neg, " ".join(e_pos)) if (tw_neg and e_pos) else 0.0
462
+ c = max(c1, c2)
463
+ # ---- L1-c 同侧比对:第二类关系(同条件空间 · 结论槽取值分歧) ----
464
+ # 与 L1-b 是**两类不同关系**:L1-b 判「条件互斥」(反题),L1-c 判
465
+ # 「同条件 + 同结论槽 + 取值不同」(矛盾)。后者在词面不可判——探针实测
466
+ # 「同属性不同值」正文覆盖率 0.612 反而低于「异属性」0.623,故改用
467
+ # CCG 槽位字段代理「是否同一件事」。
468
+ # 注:必须是独立 if 而非 else——同条件时 c=0,若并入 L1-b 分支会被
469
+ # `c < CLASH_LOW: continue` 提前跳过,L1-c 永不执行。
470
+ c3 = _cov(tw_pos, " ".join(e_pos)) if (tw_pos and e_pos) else 0.0
471
+ c4 = _cov(tw_neg, " ".join(e_neg)) if (tw_neg and e_neg) else 0.0
472
+ same_cond = max(c3, c4)
473
+ if same_cond >= SAME_COND_HIGH:
474
+ e_fn, e_sb = _slot_text(body)
475
+ slot = max(
476
+ _cov(expand_query_terms_weighted(n_fn), e_fn)
477
+ if (n_fn and e_fn) else 0.0,
478
+ _cov(expand_query_terms_weighted(n_sb), e_sb)
479
+ if (n_sb and e_sb) else 0.0)
480
+ if slot >= SLOT_HIGH:
481
+ # 同口径比对(正文↔正文);tw_content 已去模板行,见上文
482
+ concl = _cov(tw_content, _body_text(body)) if tw_content else 0.0
483
+ if concl < CONCLUSION_SAME:
484
+ divergences.append({
485
+ "type": "same_condition_divergence", "with": nid,
486
+ "with_layer": e.get("layer"),
487
+ "detail": ("适用条件与结论槽均重合但取值不同:"
488
+ "可能是更正(应覆盖旧值)或分歧(应补区分条件)"),
489
+ "same_condition": round(same_cond, 4),
490
+ "slot_overlap": round(slot, 4),
491
+ "conclusion_overlap": round(concl, 4),
492
+ "slot": (n_fn or n_sb)[:40]})
493
+ if c < CLASH_LOW:
494
+ continue
495
+ conflicts.append({
496
+ "type": "condition_clash", "with": nid,
497
+ "with_layer": e.get("layer"),
498
+ "detail": ("新节点适用条件落在既有节点不适用区"
499
+ if c1 >= c2 else "新节点不适用条件覆盖既有节点适用区"),
500
+ "score": round(c, 4),
501
+ "pos_side": _dedup(pos)[:6], "neg_side": _dedup(neg)[:6]})
502
+ seeds.append(nid)
503
+ strength = max(strength, c)
504
+
505
+ # ---- L0 情绪(独立通道,只调度不裁决) ----
506
+ emo = emotional_bias(strength, _last_strength(cg))
507
+
508
+ # ---- L1 四态判定 ----
509
+ recursion = None
510
+ missing = []
511
+ allc = hard + conflicts + divergences
512
+ if hard:
513
+ verdict = "REJECT"
514
+ reason = ";".join(h["detail"] for h in hard)
515
+ elif divergences:
516
+ verdict = "DEFER"
517
+ reason = "同条件空间下结论槽取值不一致:需裁决是更正还是分支"
518
+ # 两类关系可能同时成立(同条件分歧 vs 某节点 / 条件互斥 vs 另节点)——
519
+ # 若只报 L1-c,跨侧互斥的线索会被掩盖,飞轮收到的缺口就不完整。
520
+ if conflicts:
521
+ reason += f';另有 {len(conflicts)} 处条件互斥(见 conflicts[])'
522
+ elif strength >= CLASH_HIGH:
523
+ verdict = "DEFER"
524
+ reason = "条件互斥:需补区分条件后才能判定"
525
+ elif strength >= CLASH_LOW:
526
+ verdict = "DEFER"
527
+ reason = "条件部分覆盖:待确认是否互斥"
528
+ elif comparable == 0 and (pos or neg):
529
+ verdict = "BLINDSPOT"
530
+ reason = "既有节点均未声明条件:无法建立比对路径"
531
+ else:
532
+ verdict = "ACCEPT"
533
+ reason = "无冲突"
534
+
535
+ # ---- L2 递归反思(仅对 DEFER,且情绪未指向 approaching 的快速放行) ----
536
+ if verdict == "DEFER" and int(depth) > 0 and emo["bias"] != "approaching":
537
+ recursion = _recursive_reflect(cg, seeds, tw_pos, tw_neg,
538
+ max_depth=int(depth))
539
+ if recursion["stopped_by"] == "resolved":
540
+ # 找到区分条件 → 冲突可分辨,降级为待定(不直接放行,留人工/飞轮)
541
+ reason = f'{reason};已找到区分条件 {recursion["resolved_by"][:3]}'
542
+ else:
543
+ missing.append({
544
+ "need": "区分条件",
545
+ "why": f'递归反思停止于 {recursion["stopped_by"]}'
546
+ f'(深度 {recursion["depth"]},增益 {recursion["gain"]})',
547
+ "conflicts": [c["with"] for c in allc if c.get("with")][:5]})
548
+
549
+ # 同条件取值分歧:L2 递归提供不了「区分条件」(条件本就相同),故缺口是
550
+ # 「取值裁决」而非「补条件」。必须显式落 missing——否则 strength=0 →
551
+ # 情绪 approaching → L2 被跳过 → missing 为空 → 飞轮收不到任何信号。
552
+ if divergences and not missing:
553
+ missing.append({
554
+ "need": "取值裁决(同条件·同结论槽存在多条不同取值)",
555
+ "why": "适用条件与结论槽均重合而正文取值不同:"
556
+ "需确认是「更正」(应覆盖旧值)还是「分支」(应补区分条件)",
557
+ "conflicts": [c["with"] for c in allc if c.get("with")][:5]})
558
+
559
+ rec = {"t": time.time(), "layer": layer, "verdict": verdict,
560
+ "reason": reason, "conflict_strength": round(strength, 4),
561
+ "emotional": emo, "conflicts": allc, "recursion": recursion,
562
+ "missing": missing, "scanned": scanned, "comparable": comparable,
563
+ "pos": pos[:6], "neg": neg[:6],
564
+ "actor": getattr(cg, "actor", "unknown")}
565
+
566
+ # ---- 冲突自动触发飞轮(误差 → 补条件 → 结构更新) ----
567
+ if auto_flywheel and verdict in ("REJECT", "DEFER", "BLINDSPOT"):
568
+ rec["unresolved_id"] = _fire_flywheel(cg, query or content, verdict,
569
+ reason, missing, allc)
570
+ log(cg, rec)
571
+ return rec
572
+
573
+
574
+ # 生效条件:missing 为假值(None/空容器/空串)返回 "";否则逐项仅取 m.get("need") 真值者,格式化为 `need(why)`(why 缺键用 "",值为 None 则格式化 "None"),以 ";" 连接;
575
+ def _missing_text(missing):
576
+ """结构化缺口 → 一行文本(作为飞轮的 known_clues)。
577
+
578
+ 修断链:原先 `missing=reason` 把「人话」塞进 missing 字段,而判定器产出的
579
+ [{need, why, conflicts}] 落在 detail 里被 flywheel_step 忽略——上游产出了
580
+ 结构,却送不到下游。
581
+ """
582
+ if not missing:
583
+ return ""
584
+ return ";".join(f'{m.get("need", "")}({m.get("why", "")})'
585
+ for m in missing if m.get("need"))
586
+
587
+
588
+ # 生效条件:cg 提供可调用 flywheel_step 时,以 query(假值按空串)前 200 字符、verdict、missing 文本(空则 reason)、conflicts 非 None 则作 detail 否则 missing 调用;结果 dict 的 unresolved_id 真值时返回它,否则回落 id;无 flywheel_step 或异常返回 None;
589
+ def _fire_flywheel(cg, query, verdict, reason, missing, conflicts=None):
590
+ """把冲突作为「误差」投给知识飞轮,返回 unresolved 条目 id(失败不阻塞写入)。
591
+
592
+ conflicts:冲突现场(含 type / with / same_condition 等数值),供下游生成
593
+ `# 现场:`;missing 只描述「缺什么」,不带现场,故两者都要送。
594
+ """
595
+ step = getattr(cg, "flywheel_step", None)
596
+ if step is None:
597
+ return None
598
+ try:
599
+ r = step({"query": (query or "")[:200], "expected_state": "ACCEPT",
600
+ "actual_state": verdict,
601
+ "missing": _missing_text(missing) or reason,
602
+ "detail": conflicts if conflicts is not None else missing})
603
+ if isinstance(r, dict):
604
+ return r.get("unresolved_id") or r.get("id")
605
+ except Exception:
606
+ return None
607
+ return None
608
+
609
+
610
+ # --------------------------------------------------------------------------
611
+ # 留痕 / 统计 / 自描述
612
+ # --------------------------------------------------------------------------
613
+
614
+ # 生效条件:cg 与 rec 给出时,将 rec 序列化为 JSON 行追加到 cg.root 下 LOG_FILE,OSError 被吞掉,返回 rec;
615
+ def log(cg, rec):
616
+ """append-only 留痕:每条判定可审计。"""
617
+ p = os.path.join(cg.root, LOG_FILE)
618
+ try:
619
+ with open(p, "a", encoding="utf-8") as f:
620
+ f.write(json.dumps(rec, ensure_ascii=False) + "\n")
621
+ except OSError:
622
+ pass
623
+ return rec
624
+
625
+
626
+ # 生效条件:cg 给出时,若 cg.root 下 LOG_FILE 可读,则逐行解析 JSON(空行/解析失败跳过);limit 真值时返回 out[-int(limit):][::-1](int(limit)=0 时为全部倒序),limit 为假值(0/None/"")时返回 out[::-1];路径不可用或 OSError 返回 [];
627
+ def history(cg, limit=100):
628
+ """最近冲突判定留痕(倒序)。"""
629
+ p = os.path.join(cg.root, LOG_FILE)
630
+ if not os.path.exists(p):
631
+ return []
632
+ out = []
633
+ try:
634
+ with open(p, "r", encoding="utf-8") as f:
635
+ for line in f:
636
+ line = line.strip()
637
+ if not line:
638
+ continue
639
+ try:
640
+ out.append(json.loads(line))
641
+ except (ValueError, TypeError):
642
+ pass
643
+ except OSError:
644
+ return []
645
+ return out[-int(limit):][::-1] if limit else out[::-1]
646
+
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;
649
+ def summary(cg):
650
+ """冲突面汇总(流式计数,供 health 审计)。"""
651
+ p = os.path.join(cg.root, LOG_FILE)
652
+ by_verdict, by_bias, total = {}, {}, 0
653
+ if os.path.exists(p):
654
+ try:
655
+ with open(p, "r", encoding="utf-8") as f:
656
+ for line in f:
657
+ line = line.strip()
658
+ if not line:
659
+ continue
660
+ try:
661
+ r = json.loads(line)
662
+ except (ValueError, TypeError):
663
+ continue
664
+ total += 1
665
+ v = r.get("verdict")
666
+ by_verdict[v] = by_verdict.get(v, 0) + 1
667
+ b = (r.get("emotional") or {}).get("bias")
668
+ if b:
669
+ by_bias[b] = by_bias.get(b, 0) + 1
670
+ except OSError:
671
+ pass
672
+ return {"records": total, "by_verdict": by_verdict, "by_bias": by_bias,
673
+ "max_scan": MAX_SCAN, "max_depth": MAX_DEPTH,
674
+ "min_gain": MIN_GAIN}
675
+
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 等模块级常量的自描述字典;
678
+ def catalog():
679
+ """自描述:三级决策 + 四态 + 递归约束(供 MCP / 文档对照验证)。"""
680
+ return {
681
+ "levels": {
682
+ "L0_emotion": {
683
+ "theory": "情绪 = 信息差二阶变化 d²D/dt²(智能论 §十一)",
684
+ "outputs": ["approaching", "stable", "avoiding"],
685
+ "constraint": "独立通道,不参与信任/资格计算,只做流程调度",
686
+ "thresholds": {"avoid": EMO_AVOID, "approach": EMO_APPROACH},
687
+ },
688
+ "L1_reflect": {
689
+ "theory": "反题 = 预测与事实冲突(条件论七操作)",
690
+ "checks": ["self_negation", "discipline", "condition_clash",
691
+ "same_condition_divergence"],
692
+ "relations": {
693
+ "condition_clash": "跨侧比对(新正↔旧负 / 新负↔旧正)= 条件互斥(反题)",
694
+ "same_condition_divergence": "同侧比对(新正↔旧正 / 新负↔旧负)"
695
+ "+ CCG 结论槽 = 同条件空间内的取值分歧(矛盾)",
696
+ },
697
+ "verdicts": list(VERDICTS),
698
+ "thresholds": {"high": CLASH_HIGH, "low": CLASH_LOW,
699
+ "same_condition": SAME_COND_HIGH, "slot": SLOT_HIGH,
700
+ "conclusion_same": CONCLUSION_SAME},
701
+ },
702
+ "L2_recursive_reflect": {
703
+ "theory": "递归受深度/节点数/循环/信息增益门槛约束(智能论 :273)",
704
+ "max_depth": MAX_DEPTH, "max_nodes": MAX_NODES,
705
+ "min_gain": MIN_GAIN,
706
+ "stop_reasons": ["resolved", "gain_below_threshold",
707
+ "depth_exceeded", "node_budget",
708
+ "frontier_exhausted"],
709
+ },
710
+ },
711
+ "auto_flywheel": {
712
+ "theory": "知识飞轮:误差 → 补条件 → 结构更新(智能论 :725)",
713
+ "triggers_on": ["REJECT", "DEFER", "BLINDSPOT"],
714
+ },
715
+ "discipline_detection": {"tags": list(DISCIPLINE_TAGS),
716
+ "id_prefixes": ["discipline_", "work_discipline"]},
717
+ "honest_boundary": "条件级(结构化)冲突检测,非语义蕴含证明;无法比对时 BLINDSPOT",
718
718
  }