@furongjun1999/dsh-memory 0.4.7 → 0.4.9

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 (325) hide show
  1. package/README.md +130 -47
  2. package/codebuddy/CODEBUDDY.md +21 -10
  3. package/codebuddy/README.md +13 -1
  4. package/codebuddy/mcp.json +9 -0
  5. package/docs/GBrain/345/217/257/345/200/237/351/211/264/347/202/271_/347/201/265/346/236/242/350/220/275/347/202/271/344/272/244/346/216/245_20260919.md +169 -0
  6. package/docs/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 -0
  7. package/docs/README.md +111 -0
  8. package/docs/discipline/harnesses.yaml +226 -152
  9. package/docs/discipline/templates/full.md.tmpl +61 -58
  10. package/docs/eval//347/254/254/344/270/211/346/226/271/351/252/214/350/257/201/346/212/245/345/221/212_LoCoMo_/347/201/265/346/236/242_.md +127 -0
  11. package/docs/eval//347/254/254/344/270/211/346/226/271/351/252/214/350/257/201/346/212/245/345/221/212_LoCoMo_/347/201/265/346/236/242_.png +0 -0
  12. package/docs/eval//347/254/254/344/270/211/346/226/271/351/252/214/350/257/201/346/212/245/345/221/212_/347/201/265/346/236/242_vs_dejavu_/347/273/237/344/270/200/350/257/204/345/210/206_v7.md +202 -0
  13. package/docs/experiments/linkref_backfill/candidates_20260917.json +726 -0
  14. package/docs/experiments/linkref_backfill/candidates_internal_20260917.json +602 -0
  15. package/docs/experiments/linkref_backfill/candidates_internal_v2.json +603 -0
  16. package/docs/experiments/linkref_backfill/candidates_secret_20260917.json +884 -0
  17. package/docs/experiments/linkref_backfill/candidates_secret_v2.json +789 -0
  18. package/docs/experiments/m4-role-probe/census.py +86 -0
  19. package/docs/experiments/m4-role-probe/probe.py +89 -0
  20. package/docs/experiments/mapped_confidence/bench_mapped_conf.py +216 -0
  21. package/docs/experiments/mapped_confidence/post_check.py +75 -0
  22. package/docs/experiments/mapped_confidence/repro_thirdparty_tol.py +83 -0
  23. package/docs/experiments/mapped_confidence/result_mapped_conf.json +215 -0
  24. package/docs/hive//345/244/232/347/253/257harness/351/200/232/344/277/241/345/245/221/347/272/246_v0.1.md +42 -0
  25. package/docs/hive//346/243/200/347/264/242/346/224/266/346/225/233/345/256/236/346/265/213/344/270/216S1b/350/256/276/350/256/241_v0.1.md +43 -0
  26. package/docs/hive//346/243/200/347/264/242/350/267/257/345/276/204/344/270/216/350/256/244/347/237/245/347/273/223/346/236/204/345/245/221/347/272/246_v0.1.md +102 -0
  27. package/docs/hive//347/234/237/345/256/236/345/272/223/347/253/257/345/210/260/347/253/257/345/256/236/346/265/213_S1b/344/270/216/345/217/254/345/233/236/346/235/203/350/241/241_v0.1.md +57 -0
  28. package/docs/hive//347/234/237/345/256/236/345/272/223/347/253/257/345/210/260/347/253/257/345/256/236/346/265/213_S7/345/200/222/346/216/222/345/200/231/351/200/211/345/261/202_v0.1.md +126 -0
  29. package/docs/hive//350/234/202/345/267/242/345/217/214/345/256/236/344/276/213/344/272/222/351/252/214_/350/256/276/350/256/241/345/256/232/347/250/277.md +503 -0
  30. 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 +14 -2
  31. package/docs/mdcg/D_meta_/345/267/245/347/250/213/345/214/226/346/226/271/346/241/210_v0.2.md +216 -0
  32. package/docs/{README → mdcg/README}/350/257/246/347/273/206/347/211/210_v0.4.5.md +631 -625
  33. package/docs/{ → mdcg/}/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 +1 -1
  34. package/docs/mdcg//344/273/243/347/240/201/350/257/204/345/256/241/344/270/216/346/235/241/344/273/266/345/214/226/346/263/250/351/207/212_/345/245/221/347/272/246_v0.1.md +82 -0
  35. package/docs/mdcg//345/205/250/345/272/223/344/273/243/347/240/201/350/257/204/345/256/241/344/270/216/346/235/241/344/273/266/345/214/226/346/263/250/351/207/212_/350/256/241/345/210/222_v0.1.md +600 -0
  36. 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 +263 -0
  37. 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 +1 -1
  38. package/docs/mdcg//345/255/220/344/273/243/347/220/206/351/205/215/347/275/256/346/240/207/345/207/206_v0.5.md +236 -0
  39. 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 +2 -2
  40. package/docs/{ → mdcg/}/347/201/265/346/236/242/344/270/211/345/261/202/346/213/206/345/210/206/350/247/204/345/210/222_v0.1.md +181 -181
  41. package/docs/mdcg//347/201/265/346/236/242/350/256/260/345/277/206/345/212/250/350/257/215/345/215/217/350/256/256_v1.0-draft.md +172 -0
  42. package/docs/mdcg//347/216/257/344/272/214_/347/231/275/347/256/261/345/241/253/345/205/205/346/265/201/346/260/264/347/272/277_v0.1.md +31 -0
  43. package/docs/{ → mdcg/}/347/274/272/345/217/243/345/215/225_P0/346/224/266/345/217/243_v0.1.md +2 -2
  44. 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 +1 -1
  45. 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 +4 -4
  46. package/docs/{ → mdcg/}/350/256/260/345/277/206/346/223/215/344/275/234/347/263/273/347/273/237_MdCGOS/344/270/216MCP/346/216/245/345/205/245_v0.1.md +2 -2
  47. package/docs/mdcg//350/267/250/347/253/257/351/252/214/350/257/201/344/270/216/345/220/214/346/255/245/345/215/217/350/256/256_v0.1.md +93 -0
  48. 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 -0
  49. package/docs/{ → swarm/}/350/234/202/347/276/244/344/272/222/350/201/224_v0.1.md +3 -3
  50. 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 -0
  51. 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 -0
  52. 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 -0
  53. 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 -0
  54. 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 +6 -6
  55. 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 +1 -1
  56. 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 -0
  57. 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 -0
  58. package/docs//345/255/230/347/256/227/344/270/200/344/275/223/346/236/266/346/236/204/345/255/246/344/271/240_/345/244/226/351/203/250/347/220/206/350/256/272/345/257/271/347/205/247/344/270/216/350/267/257/347/272/277/344/272/244/346/216/245_20260916.md +98 -0
  59. package/docs//345/255/230/347/256/227/344/270/200/344/275/223/347/245/236/347/273/217/347/275/221/347/273/234/346/236/266/346/236/204_/346/200/273/347/272/262/344/270/216/344/272/244/346/216/245_20260916.md +109 -0
  60. package/docs//345/267/245/344/275/234/347/272/252/345/276/213_/350/256/244/347/237/245/345/233/276/346/235/241/347/233/256_v1.1.json +434 -405
  61. package/docs//347/201/265/346/236/242/350/207/252/346/210/221/346/224/271/350/277/233/345/267/245/344/275/234/350/256/241/345/210/222_/345/244/226/351/203/250/347/240/224/347/251/266/347/263/273/345/210/227/345/220/270/346/224/266_v1_20260919.md +286 -0
  62. package/docs//347/231/275/347/256/261/345/214/226/347/272/262/351/242/206_/344/270/211/351/241/271/347/233/256/345/255/246/344/271/240/346/200/273/346/236/266/346/236/204/344/270/216Ghidra/344/272/244/346/216/245_20260916.md +104 -0
  63. package/docs//350/256/260/345/277/206/350/257/204/345/256/241/347/263/273/347/273/237_/347/253/213/351/241/271/350/256/276/350/256/241/344/270/216/346/226/275/345/267/245/344/272/244/346/216/245_20260915.md +183 -0
  64. package/dsh/README.md +34 -1
  65. package/dsh/cordis.yml.example +13 -7
  66. package/dsh/hive-mcp-probe.mjs +94 -0
  67. package/dsh/hive-mcp.example.yml +62 -0
  68. package/dsh/update-lingshu.bat +11 -0
  69. package/dsh/update-lingshu.ps1 +337 -0
  70. package/lib/hooks.d.ts +8 -0
  71. package/lib/hooks.js +27 -24
  72. package/lib/index.d.ts +4 -2
  73. package/lib/index.js +29 -6
  74. package/lib/lib/datapath.d.ts +76 -1
  75. package/lib/lib/datapath.js +199 -13
  76. package/lib/lib/mdcg_client.d.ts +6 -3
  77. package/lib/lib/mdcg_client.js +6 -5
  78. package/lib/lib/mutual.js +4 -4
  79. package/lib/lib/prompt_safety.d.ts +48 -0
  80. package/lib/lib/prompt_safety.js +62 -0
  81. package/lib/lib/token_store.js +4 -5
  82. package/md_cg/audit.py +91 -2
  83. package/md_cg/autonomy.py +86 -15
  84. package/md_cg/backfill.py +36 -1
  85. package/md_cg/backfill_bigdomain.py +34 -0
  86. package/md_cg/bench6_arms.py +28 -1
  87. package/md_cg/bench6_common.py +10 -1
  88. package/md_cg/bench6_competitors.py +6 -1
  89. package/md_cg/bench_axis_domain.py +9 -1
  90. package/md_cg/bench_blind_comp.py +7 -1
  91. package/md_cg/bench_en_atoms_public.py +9 -0
  92. package/md_cg/bench_governance.py +348 -0
  93. package/md_cg/bench_lme_zh.py +16 -1
  94. package/md_cg/bench_locomo.py +2 -1
  95. package/md_cg/bench_locomo_zh.py +16 -1
  96. package/md_cg/bench_locomo_zh_public.py +4 -1
  97. package/md_cg/bench_longmem.py +2 -1
  98. package/md_cg/bench_membench.py +37 -6
  99. package/md_cg/bench_p0.py +4 -1
  100. package/md_cg/bench_progressive.py +61 -10
  101. package/md_cg/bench_role_views.py +238 -0
  102. package/md_cg/bench_task_ab.py +8 -1
  103. package/md_cg/bench_task_ab_llm.py +13 -1
  104. package/md_cg/bench_unified_en.py +6 -1
  105. package/md_cg/bench_zh_mad.py +20 -1
  106. package/md_cg/blindspot_tickets.py +123 -0
  107. package/md_cg/branches.py +286 -0
  108. package/md_cg/build_postings.py +73 -0
  109. package/md_cg/ccgc.py +949 -0
  110. package/md_cg/census.py +5 -1
  111. package/md_cg/chain.py +24 -3
  112. package/md_cg/codeindex.py +134 -17
  113. package/md_cg/coldverify.py +265 -0
  114. package/md_cg/comment_gate.py +338 -0
  115. package/md_cg/cond_compose.py +190 -0
  116. package/md_cg/cond_facts.py +155 -0
  117. package/md_cg/cond_template.json +107 -0
  118. package/md_cg/condition_anchor.py +143 -0
  119. package/md_cg/conformance.py +727 -0
  120. package/md_cg/consistency.py +25 -2
  121. package/md_cg/consolidate.py +53 -2
  122. package/md_cg/corpus.py +5 -1
  123. package/md_cg/crosscheck.py +42 -2
  124. package/md_cg/crypto.py +35 -1
  125. package/md_cg/d_meta.py +310 -0
  126. package/md_cg/datapath.py +201 -26
  127. package/md_cg/docindex.py +122 -1
  128. package/md_cg/eval_common.py +35 -6
  129. package/md_cg/evidence.py +27 -1
  130. package/md_cg/evolution.py +21 -1
  131. package/md_cg/export.py +11 -1
  132. package/md_cg/forgetting.py +34 -4
  133. package/md_cg/fsutil.py +81 -2
  134. package/md_cg/hotcache.py +214 -0
  135. package/md_cg/hyperedge.py +251 -0
  136. package/md_cg/identity.py +20 -3
  137. package/md_cg/insight.py +19 -3
  138. package/md_cg/lexicon/build_cedict_en_zh.py +9 -0
  139. package/md_cg/lexicon/build_standard_en.py +171 -168
  140. package/md_cg/lexicon/expand_en_zh.py +6 -0
  141. package/md_cg/lifecycle.py +273 -0
  142. package/md_cg/linkref.py +281 -0
  143. package/md_cg/links.py +29 -1
  144. package/md_cg/mcp_server.py +894 -168
  145. package/md_cg/md_whitebox.py +53 -1
  146. package/md_cg/mdcg.py +1205 -41
  147. package/md_cg/mdcos.py +1060 -76
  148. package/md_cg/metacognition.py +39 -4
  149. package/md_cg/migrate.py +4 -0
  150. package/md_cg/migrate_aeis.py +221 -213
  151. package/md_cg/migrate_roleplay.py +8 -0
  152. package/md_cg/migrate_wisdom_graph.py +14 -1
  153. package/md_cg/mreview/__init__.py +25 -0
  154. package/md_cg/mreview/__main__.py +110 -0
  155. package/md_cg/mreview/bundle.py +178 -0
  156. package/md_cg/mreview/candidates.py +262 -0
  157. package/md_cg/mreview/govern.py +694 -0
  158. package/md_cg/mreview/locate.py +939 -0
  159. package/md_cg/mreview/pipeline.py +729 -0
  160. package/md_cg/mreview/rules/duplication.json +21 -0
  161. package/md_cg/mreview/rules/field_coverage.json +54 -0
  162. package/md_cg/mreview/rules/source_license.json +21 -0
  163. package/md_cg/mreview/rules/template_flow.json +21 -0
  164. package/md_cg/mreview/ruleset.py +253 -0
  165. package/md_cg/nodefile.py +269 -1
  166. package/md_cg/pooling.py +23 -1
  167. package/md_cg/postings.py +298 -0
  168. package/md_cg/predict.py +113 -15
  169. package/md_cg/progressive.py +3 -0
  170. package/md_cg/protect.py +18 -5
  171. package/md_cg/protocol.py +372 -0
  172. package/md_cg/provenance.py +262 -0
  173. package/md_cg/reach.py +453 -0
  174. package/md_cg/refindex.py +88 -4
  175. package/md_cg/refine.py +20 -1
  176. package/md_cg/roleviews.py +89 -0
  177. package/md_cg/routing.py +76 -0
  178. package/md_cg/scrub.py +68 -3
  179. package/md_cg/security.py +26 -1
  180. package/md_cg/self_state.py +65 -2
  181. package/md_cg/selfreport.py +151 -0
  182. package/md_cg/semantic/canonical.py +5 -0
  183. package/md_cg/semantic/en_normalizer.py +364 -295
  184. package/md_cg/semantic/zh_en_atoms.py +139 -136
  185. package/md_cg/signer.py +41 -1
  186. package/md_cg/sources.py +583 -547
  187. package/md_cg/statushdr.py +179 -0
  188. package/md_cg/stg.py +59 -18
  189. package/md_cg/subgraph.py +23 -0
  190. package/md_cg/sustain.py +74 -1
  191. package/md_cg/tasks.py +471 -0
  192. package/md_cg/test_action_derive.py +203 -0
  193. package/md_cg/test_audit_rotate.py +270 -0
  194. package/md_cg/test_autonomy.py +26 -0
  195. package/md_cg/test_bench_governance.py +102 -0
  196. package/md_cg/test_blindspot_tickets.py +166 -0
  197. package/md_cg/test_branches.py +249 -0
  198. package/md_cg/test_ccg_perturb.py +1 -1
  199. package/md_cg/test_ccgc.py +433 -0
  200. package/md_cg/test_codeindex.py +338 -0
  201. package/md_cg/test_comment_gate.py +187 -0
  202. package/md_cg/test_cond_compose_anchors.py +76 -0
  203. package/md_cg/test_condition_anchor.py +82 -0
  204. package/md_cg/test_conformance.py +343 -0
  205. package/md_cg/test_d_meta.py +412 -0
  206. package/md_cg/test_datapath_root.py +188 -0
  207. package/md_cg/test_en_pipeline.py +24 -0
  208. package/md_cg/test_gain_gate.py +47 -1
  209. package/md_cg/test_health_scale.py +173 -0
  210. package/md_cg/test_hot_cold.py +187 -0
  211. package/md_cg/test_hyperedge.py +245 -0
  212. package/md_cg/test_identity_attribution.py +6 -0
  213. package/md_cg/test_index_durability.py +224 -0
  214. package/md_cg/test_lifecycle.py +309 -0
  215. package/md_cg/test_linkref.py +306 -0
  216. package/md_cg/test_md_access_parity.py +15 -3
  217. package/md_cg/test_mr_m1.py +769 -0
  218. package/md_cg/test_mr_m2.py +587 -0
  219. package/md_cg/test_mr_m3.py +710 -0
  220. package/md_cg/test_mr_m4.py +485 -0
  221. package/md_cg/test_p1.py +1 -1
  222. package/md_cg/test_p11_consistency.py +1 -1
  223. package/md_cg/test_p12_metacognition.py +2 -2
  224. package/md_cg/test_p16_self_state.py +1 -1
  225. package/md_cg/test_p26_refindex.py +50 -21
  226. package/md_cg/test_p27_docindex.py +258 -4
  227. package/md_cg/test_p28_refcheck.py +1 -1
  228. package/md_cg/test_p29_session_ingest_export.py +1 -1
  229. package/md_cg/test_p2_mcp.py +7 -1
  230. package/md_cg/test_p31_insight.py +24 -0
  231. package/md_cg/test_p38_contextualize.py +1 -1
  232. package/md_cg/test_p39_vision_evidence.py +1 -1
  233. package/md_cg/test_p40_refine_worklist.py +1 -1
  234. package/md_cg/test_p41_evolve_patrol.py +1 -1
  235. package/md_cg/test_p42_provenance.py +1 -1
  236. package/md_cg/test_p43_pooling.py +41 -13
  237. package/md_cg/test_p44_md_whitebox.py +14 -1
  238. package/md_cg/test_protocol.py +243 -0
  239. package/md_cg/test_reach.py +378 -0
  240. package/md_cg/test_reach_keys.py +201 -0
  241. package/md_cg/test_reach_meta_exits.py +145 -0
  242. package/md_cg/test_read_clip.py +141 -0
  243. package/md_cg/test_retr_s1.py +340 -0
  244. package/md_cg/test_retr_s1b.py +209 -0
  245. package/md_cg/test_retr_s3.py +194 -0
  246. package/md_cg/test_retr_s4.py +163 -0
  247. package/md_cg/test_retr_s5.py +200 -0
  248. package/md_cg/test_retr_s6.py +157 -0
  249. package/md_cg/test_retr_s7.py +385 -0
  250. package/md_cg/test_retr_s8_time.py +316 -0
  251. package/md_cg/test_retr_s9_edges.py +286 -0
  252. package/md_cg/test_retr_s9_entity_ctx.py +175 -0
  253. package/md_cg/test_review_conformance.py +367 -0
  254. package/md_cg/test_role_views.py +354 -0
  255. package/md_cg/test_tasks.py +409 -0
  256. package/md_cg/test_tool_face.py +189 -0
  257. package/md_cg/test_trust.py +361 -0
  258. package/md_cg/test_twophase.py +286 -0
  259. package/md_cg/test_units_poll.py +71 -0
  260. package/md_cg/test_v14_fixes.py +397 -0
  261. package/md_cg/test_validity_filter.py +280 -0
  262. package/md_cg/test_wisdom_md_store.py +7 -3
  263. package/md_cg/test_writepipe.py +214 -0
  264. package/md_cg/theory.py +17 -2
  265. package/md_cg/tokens.py +134 -12
  266. package/md_cg/tool_face.py +261 -0
  267. package/md_cg/trust.py +943 -0
  268. package/md_cg/twophase.py +232 -0
  269. package/md_cg/units.py +665 -0
  270. package/md_cg/vision_evidence.py +25 -2
  271. package/md_cg/weights.py +25 -2
  272. package/md_cg/whitebox.py +33 -2
  273. package/md_cg/whitebox_kb/data/verify_cache.json +21233 -360
  274. package/md_cg/whitebox_kb/data/verify_savings.jsonl +5124 -0
  275. package/md_cg/whitebox_kb/wisdom/audit_log/chain_heat.json +10 -10
  276. package/md_cg/whitebox_kb/wisdom/code_compose.py +113 -6
  277. package/md_cg/whitebox_kb/wisdom/code_solidified.json +1 -1
  278. package/md_cg/whitebox_kb/wisdom/verifier.py +340 -55
  279. package/md_cg/whitebox_kb/wisdom/wisdom-book-cloud.db +0 -0
  280. package/md_cg/writelimit.py +37 -7
  281. package/md_cg/writepipe.py +543 -0
  282. package/package.json +2 -2
  283. package/skills/skills/designer-perspective/scripts/__pycache__/designer.cpython-310.pyc +0 -0
  284. package/skills/skills/designer-perspective/scripts/designer.py +17 -1
  285. package/skills/skills/designer-perspective/tests/selftest.py +3 -1
  286. package/src/hooks.ts +27 -22
  287. package/src/index.ts +33 -6
  288. package/src/lib/datapath.ts +211 -13
  289. package/src/lib/mdcg_client.ts +12 -8
  290. package/src/lib/mutual.ts +411 -411
  291. package/src/lib/prompt_safety.ts +62 -0
  292. package/src/lib/token_store.ts +4 -5
  293. package/zcode/AGENTS.md +21 -10
  294. package/zcode/README.md +4 -0
  295. package/docs//345/212/237/350/203/275/350/260/203/347/224/250/346/230/240/345/260/204/350/241/250_v0.1.md +0 -221
  296. /package/docs/{AGI → 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" +0 -0
  297. /package/docs/{AGI → 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" +0 -0
  298. /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" +0 -0
  299. /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" +0 -0
  300. /package/docs/{guardrail-charter.md → mdcg/guardrail-charter.md} +0 -0
  301. /package/docs/{lingshu_tutorial.html → mdcg/lingshu_tutorial.html} +0 -0
  302. /package/docs/{memory-assessment.html → mdcg/memory-assessment.html} +0 -0
  303. /package/docs/{memory_score.html → mdcg/memory_score.html} +0 -0
  304. /package/docs/{memory_score.png → mdcg/memory_score.png} +0 -0
  305. /package/docs/{release_v0.3.0.md → mdcg/release_v0.3.0.md} +0 -0
  306. /package/docs/{release_v0.4.5.md → mdcg/release_v0.4.5.md} +0 -0
  307. /package/docs/{tool_table_v0.3.0.md → mdcg/tool_table_v0.3.0.md} +0 -0
  308. /package/docs/{ → mdcg/}/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" +0 -0
  309. /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" +0 -0
  310. /package/docs/{ → mdcg/}/347/201/265/346/236/242MCP/345/267/245/345/205/267/346/200/273/350/241/250_v3.4.md" +0 -0
  311. /package/docs/{ → mdcg/}/347/201/265/346/236/242_/350/207/252/346/210/221/345/261/202/345/256/232/344/271/211.md" +0 -0
  312. /package/docs/{ → mdcg/}/350/256/244/347/237/245/345/233/276_MD/347/233/256/345/275/225/346/226/271/346/241/210_v0.1.md" +0 -0
  313. /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" +0 -0
  314. /package/docs/{ → mdcg/}/350/256/244/347/237/245/345/233/276/344/275/234/344/270/272/350/256/260/345/277/206/346/223/215/344/275/234/347/263/273/347/273/237_/350/257/204/344/274/260/344/270/216/350/267/257/347/272/277_v0.1.md" +0 -0
  315. /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" +0 -0
  316. /package/docs/{ → plans/}/347/237/245/350/257/206/345/233/276/350/260/261/351/241/271/347/233/256_/344/272/244/346/216/245/346/226/207/346/241/243_20260914.md" +0 -0
  317. /package/docs/{ → swarm/}/350/234/202/347/276/244/345/244/232/346/231/272/350/203/275/344/275/223_/344/275/277/347/224/250/346/212/245/345/221/212_20260913.md" +0 -0
  318. /package/docs/{ → swarm/}/350/234/202/347/276/244/345/244/232/346/231/272/350/203/275/344/275/223_/345/212/237/350/203/275/350/257/264/346/230/216_v0.6.md" +0 -0
  319. /package/docs/{ → swarm/}/350/234/202/347/276/244/347/233/262/345/214/272/346/240/207/350/256/260_v1.0.md" +0 -0
  320. /package/docs/{ → theory/}/344/277/241/346/201/257/345/267/256/344/270/272/344/273/200/344/271/210/345/277/205/347/204/266/345/255/230/345/234/250/344/270/224/350/207/252/347/204/266/346/211/251/345/244/247.md" +0 -0
  321. /package/docs/{ → theory/}/346/231/272/350/203/275/347/232/204/345/205/254/347/220/206/345/214/226/345/237/272/347/237/263.md" +0 -0
  322. /package/docs/{ → theory/}/346/231/272/350/203/275/347/232/204/350/256/244/347/237/245/350/277/207/347/250/213.md" +0 -0
  323. /package/docs/{ → theory/}/346/231/272/350/203/275/350/256/2723.4.md" +0 -0
  324. /package/docs/{ → theory/}/347/231/275/347/256/261/346/231/272/350/203/275/346/230/257/344/273/200/344/271/210/357/274/237.md" +0 -0
  325. /package/docs/{ → theory/}/347/231/275/347/256/261/346/231/272/350/203/275/347/263/273/345/210/227/302/267/347/254/254/344/272/224/347/257/207/357/274/232/350/256/251AI/347/234/237/346/255/243/350/243/205/344/270/212/350/256/260/345/277/206.md" +0 -0
@@ -0,0 +1,309 @@
1
+ # -*- coding: utf-8 -*-
2
+ """test_lifecycle · 节点显式生命周期状态机(Pi 可学习优点 ②)
3
+
4
+ 背景:状态散落四处各说各话——`writelimit` 用 tag `tidy:converged`、`forgetting`
5
+ 用 importance 升降、`protect` 用 `fm.protected`、`_evolution/ledger.md` 记账本;
6
+ **没有任何地方校验「这一步迁移合不合法」**。本测试覆盖唯一真源 `lifecycle.py`
7
+ 及其四条收口路径。
8
+
9
+ 覆盖:① 纯函数裁决 ② 索引快照带状态(内存+磁盘) ③ 合法逐级迁移(历史+审计)
10
+ ④ 非法迁移拒绝(负路由,且拒绝不留痕) ⑤ 幂等 ⑥ 受保护豁免降级 / 回升不受限
11
+ ⑦ add 全量重建 fm 时的状态继承 ⑧ 存量回填 backfill ⑨ 收口点(writelimit /
12
+ forgetting) ⑩ 边界(节点不存在 / 永不删除)
13
+
14
+ 运行:python -m md_cg.test_lifecycle
15
+ """
16
+ from __future__ import annotations
17
+
18
+ import json
19
+ import os
20
+ import shutil
21
+ import tempfile
22
+
23
+ from . import lifecycle, writelimit
24
+ from .forgetting import reinforce
25
+ from .mdcos import MdCGOS
26
+
27
+ _root = tempfile.mkdtemp(prefix="mdcg_life_")
28
+ cg = MdCGOS(_root)
29
+
30
+ _ok = 0
31
+ _fail = []
32
+
33
+
34
+ def check(name, cond, detail=""):
35
+ global _ok
36
+ if cond:
37
+ _ok += 1
38
+ print("[ok] " + name)
39
+ else:
40
+ _fail.append(name)
41
+ print("[FAIL] %s · %s" % (name, str(detail)[:240]))
42
+
43
+
44
+ def _fm(nid):
45
+ return ((cg.get(nid) or {}).get("frontmatter")) or {}
46
+
47
+
48
+ def _idx(nid):
49
+ return (cg.index.get("nodes") or {}).get(nid) or {}
50
+
51
+
52
+ def _disk_index():
53
+ """读**磁盘** `_index.json`——「免读文件可查」的硬验收面。"""
54
+ p = os.path.join(cg.root, "_index.json")
55
+ if not os.path.exists(p):
56
+ return {}
57
+ with open(p, encoding="utf-8", errors="replace") as f:
58
+ return json.load(f)
59
+
60
+
61
+ def _index_log():
62
+ """读**增量索引日志**(ShardedLog 分片)——`flush()` 的实际落点(`_index.json`
63
+ 是快照,由 `rebuild_index()` 写)。"""
64
+ out = []
65
+ d = os.path.join(cg.root, "_index_log")
66
+ if not os.path.isdir(d):
67
+ return out
68
+ for fn in sorted(os.listdir(d)):
69
+ p = os.path.join(d, fn)
70
+ if not os.path.isfile(p):
71
+ continue
72
+ with open(p, encoding="utf-8", errors="replace") as f:
73
+ for line in f:
74
+ if not line.strip():
75
+ continue
76
+ try:
77
+ out.append(json.loads(line))
78
+ except Exception:
79
+ pass
80
+ return out
81
+
82
+
83
+ def _audit():
84
+ p = os.path.join(cg.root, lifecycle.AUDIT_FILE)
85
+ out = []
86
+ if os.path.exists(p):
87
+ with open(p, encoding="utf-8", errors="replace") as f:
88
+ for line in f:
89
+ if line.strip():
90
+ out.append(json.loads(line))
91
+ return out
92
+
93
+
94
+ # ------------------------------------------------------------ ① 纯函数裁决
95
+ check("①1 缺字段 / 未知值 / 非 dict → active(存量兼容,不猜测)",
96
+ lifecycle.state_of({}) == "active"
97
+ and lifecycle.state_of({lifecycle.STATE_FIELD: "frozen"}) == "active"
98
+ and lifecycle.state_of(None) == "active")
99
+ check("①2 状态集内值原样返回",
100
+ all(lifecycle.state_of({lifecycle.STATE_FIELD: s}) == s
101
+ for s in lifecycle.STATES))
102
+ check("①3 合法迁移对可通行(逐级降级 + 逐级回升)",
103
+ lifecycle.can_transition("active", "converged")
104
+ and lifecycle.can_transition("converged", "demoted")
105
+ and lifecycle.can_transition("demoted", "archived")
106
+ and lifecycle.can_transition("archived", "active"))
107
+ check("①4 跳级迁移一律不通",
108
+ not lifecycle.can_transition("active", "archived")
109
+ and not lifecycle.can_transition("converged", "archived")
110
+ and not lifecycle.can_transition("archived", "demoted"))
111
+ check("①5 同状态幂等可通行", lifecycle.can_transition("demoted", "demoted"))
112
+ check("①6 check 未知状态 → unknown_state",
113
+ lifecycle.check("active", "frozen")[:2] == (False, "unknown_state"))
114
+ check("①7 check 同状态 → noop",
115
+ lifecycle.check("active", "active")[:2] == (True, "noop"))
116
+ check("①8 check 跳级 → illegal_transition",
117
+ lifecycle.check("active", "archived")[:2]
118
+ == (False, "illegal_transition"))
119
+ check("①9 check 受保护降级 → protected;override 放行",
120
+ lifecycle.check("active", "demoted", protected=True)[:2]
121
+ == (False, "protected")
122
+ and lifecycle.check("active", "demoted", protected=True,
123
+ override=True)[:2] == (True, "ok"))
124
+ check("①10 is_downgrade 方向正确(回升不算降级)",
125
+ lifecycle.is_downgrade("active", "archived")
126
+ and not lifecycle.is_downgrade("archived", "active"))
127
+
128
+ # ------------------------------------------------------------ ② 新节点与索引
129
+ n1 = cg.add("lc_n1", "生命周期状态机测试节点甲", layer="knowledge")
130
+ check("②1 新节点**显式**落 active(不再依赖缺省约定)",
131
+ _fm(n1).get(lifecycle.STATE_FIELD) == "active", _fm(n1))
132
+ check("②2 索引快照带状态(内存)",
133
+ _idx(n1).get(lifecycle.STATE_FIELD) == "active", _idx(n1))
134
+ cg.flush()
135
+ check("②3 增量日志(_index_log)带状态",
136
+ any((r.get("e") or {}).get(lifecycle.STATE_FIELD) == "active"
137
+ for r in _index_log() if r.get("id") == n1), _index_log())
138
+ cg.rebuild_index()
139
+ check("②4 磁盘 _index.json 快照带状态(免读文件即可回答「这节点什么状态」)",
140
+ ((_disk_index().get("nodes") or {}).get(n1) or {}
141
+ ).get(lifecycle.STATE_FIELD) == "active")
142
+
143
+ # ------------------------------------------------------------ ③ 合法逐级迁移
144
+ r1 = cg.set_state(n1, "converged", reason="同构聚合定型", actor="test")
145
+ check("③1 active→converged 通过且落盘",
146
+ r1.get("ok") and r1.get("changed")
147
+ and _fm(n1).get(lifecycle.STATE_FIELD) == "converged", r1)
148
+ check("③2 索引同步到 converged",
149
+ _idx(n1).get(lifecycle.STATE_FIELD) == "converged")
150
+ r2 = cg.set_state(n1, "demoted", reason="弱化", actor="test")
151
+ r3 = cg.set_state(n1, "archived", reason="归档", actor="test")
152
+ check("③3 converged→demoted→archived 逐级通过",
153
+ r2.get("ok") and r3.get("ok")
154
+ and _fm(n1).get(lifecycle.STATE_FIELD) == "archived")
155
+ h1 = _fm(n1).get(lifecycle.HISTORY_FIELD) or []
156
+ check("③4 迁移历史逐条留痕(from/to/reason/actor)",
157
+ len(h1) == 3 and h1[-1]["from"] == "demoted"
158
+ and h1[-1]["to"] == "archived" and h1[-1]["reason"] == "归档"
159
+ and h1[-1]["actor"] == "test", h1)
160
+ check("③5 审计 _lifecycle.jsonl 留痕 3 条",
161
+ len([a for a in _audit() if a.get("node_id") == n1]) == 3, _audit())
162
+ check("③6 永不删除:archived 后节点文件仍在且可读",
163
+ bool(cg.get(n1))
164
+ and os.path.exists(os.path.join(cg.root, cg.get(n1)["path"])))
165
+
166
+ # ------------------------------------------------------------ ④ 非法迁移拒绝
167
+ n2 = cg.add("lc_n2", "生命周期状态机测试节点乙", layer="knowledge")
168
+ bad = cg.set_state(n2, "archived", reason="跳级归档", actor="test")
169
+ check("④1 跳级 active→archived 被拒(负路由,不抛)",
170
+ (not bad.get("ok")) and bad.get("error") == "illegal_transition", bad)
171
+ check("④2 拒绝后 frontmatter 未被改动(不留痕)",
172
+ _fm(n2).get(lifecycle.STATE_FIELD) in (None, "active")
173
+ and not (_fm(n2).get(lifecycle.HISTORY_FIELD) or []), _fm(n2))
174
+ check("④3 拒绝后索引未被改动",
175
+ _idx(n2).get(lifecycle.STATE_FIELD) in (None, "active"))
176
+ check("④4 未知状态被拒",
177
+ cg.set_state(n2, "frozen").get("error") == "unknown_state")
178
+ check("④5 节点不存在如实上报(不静默创建)",
179
+ cg.set_state("lc_missing", "converged").get("error") == "node_not_found")
180
+ try:
181
+ lifecycle.require_transition("active", "archived")
182
+ _hard = False
183
+ except lifecycle.TransitionError:
184
+ _hard = True
185
+ check("④6 require_transition 硬拒(写路径抛异常)", _hard)
186
+ try:
187
+ cg.add("lc_n2", "覆写并试图跳级归档", state="archived")
188
+ _add_hard = False
189
+ except lifecycle.TransitionError:
190
+ _add_hard = True
191
+ check("④7 add 覆写显式非法状态 → 硬拒", _add_hard)
192
+ check("④8 硬拒后磁盘内容未被改写(校验先于写盘)",
193
+ _fm(n2).get(lifecycle.STATE_FIELD) in (None, "active"))
194
+
195
+ # ------------------------------------------------------------ ⑤ 幂等
196
+ h_before = len(_fm(n1).get(lifecycle.HISTORY_FIELD) or [])
197
+ aud_before = len([a for a in _audit() if a.get("node_id") == n1])
198
+ same = cg.set_state(n1, "archived", reason="重复归档", actor="test")
199
+ check("⑤1 同状态幂等(noop / changed=False)",
200
+ same.get("ok") and same.get("changed") is False
201
+ and same.get("code") == "noop", same)
202
+ check("⑤2 幂等不刷历史",
203
+ len(_fm(n1).get(lifecycle.HISTORY_FIELD) or []) == h_before)
204
+ check("⑤3 幂等不写审计",
205
+ len([a for a in _audit() if a.get("node_id") == n1]) == aud_before)
206
+
207
+ # ------------------------------------------------------------ ⑥ 受保护豁免降级
208
+ n3 = cg.add("lc_n3", "受保护节点丙", layer="knowledge", importance=0.8)
209
+ check("⑥0 新建即自动受保护(importance≥0.7)",
210
+ _fm(n3).get("protected") is True, _fm(n3))
211
+ prot = cg.set_state(n3, "demoted", reason="试图降级", actor="test")
212
+ check("⑥1 受保护节点降级被拒 → protected 码",
213
+ (not prot.get("ok")) and prot.get("error") == "protected", prot)
214
+ check("⑥2 拒绝后状态未变(保护 = 不可遗忘)",
215
+ _fm(n3).get(lifecycle.STATE_FIELD) == "active")
216
+ ov = cg.set_state(n3, "demoted", reason="显式放行", actor="test",
217
+ override=True)
218
+ check("⑥3 override=True 放行降级",
219
+ ov.get("ok") and _fm(n3).get(lifecycle.STATE_FIELD) == "demoted", ov)
220
+ up = cg.set_state(n3, "active", reason="回升不受限", actor="test")
221
+ check("⑥4 回升不受保护限制(无需 override)",
222
+ up.get("ok") and _fm(n3).get(lifecycle.STATE_FIELD) == "active")
223
+
224
+ # ------------------------------------------------------------ ⑦ add 状态继承
225
+ n4 = cg.add("lc_n4", "状态继承节点丁", layer="knowledge")
226
+ cg.set_state(n4, "converged", reason="定型", actor="test")
227
+ cg.add("lc_n4", "覆写内容(裸 add,不带状态)", layer="knowledge")
228
+ check("⑦1 裸 add 覆写**继承**旧状态(不静默打回 active)",
229
+ _fm(n4).get(lifecycle.STATE_FIELD) == "converged", _fm(n4))
230
+ check("⑦2 继承后索引仍是 converged",
231
+ _idx(n4).get(lifecycle.STATE_FIELD) == "converged")
232
+ cg.add("lc_n4", "覆写并合法回升", layer="knowledge", state="active")
233
+ check("⑦3 显式合法迁移放行(converged→active)",
234
+ _fm(n4).get(lifecycle.STATE_FIELD) == "active")
235
+ check("⑦4 新节点显式状态从 active 起算(active→converged 合法)",
236
+ _fm(cg.add("lc_n5", "显式定型节点", layer="knowledge",
237
+ state="converged")).get(lifecycle.STATE_FIELD) == "converged")
238
+
239
+ # ------------------------------------------------------------ ⑧ 存量回填
240
+ node4 = cg.get(n4)
241
+ fm4 = dict(node4["frontmatter"])
242
+ fm4.pop(lifecycle.STATE_FIELD, None) # 造「旧库缺字段」形态
243
+ cg._write_node(n4, os.path.join(cg.root, node4["path"]), fm4,
244
+ node4.get("content") or "")
245
+ _idx(n4).pop(lifecycle.STATE_FIELD, None)
246
+ check("⑧0 旧库形态成立(字段已摘除)",
247
+ lifecycle.STATE_FIELD not in _fm(n4))
248
+ pre = lifecycle.backfill(cg, apply=False)
249
+ check("⑧1 dry-run 盘点缺失且不改盘",
250
+ pre.get("dry_run") and n4 in (pre.get("planned") or [])
251
+ and lifecycle.STATE_FIELD not in _fm(n4), pre)
252
+ app = lifecycle.backfill(cg, apply=True)
253
+ check("⑧2 apply 回填为显式 active",
254
+ app.get("backfilled", 0) >= 1
255
+ and _fm(n4).get(lifecycle.STATE_FIELD) == "active", app)
256
+ check("⑧3 回填幂等(第二次无缺失)",
257
+ lifecycle.backfill(cg, apply=True).get("backfilled") == 0)
258
+ check("⑧4 缺字段节点在回填前也按 active 工作(state_of 缺省)",
259
+ lifecycle.state_of({}) == "active")
260
+
261
+ # ------------------------------------------------------------ ⑨ 收口点
262
+ n6 = cg.add("lc_n6", "同构聚合成员节点", layer="contextual", importance=0.4)
263
+ node6 = cg.get(n6)
264
+ e6 = {"id": n6, "path": node6["path"],
265
+ "frontmatter": dict(node6["frontmatter"]),
266
+ "content": node6.get("content") or ""}
267
+ writelimit._demote(cg, e6)
268
+ check("⑨1 tidy 降权 → lifecycle_state=converged",
269
+ _fm(n6).get(lifecycle.STATE_FIELD) == "converged", _fm(n6))
270
+ check("⑨2 兼容别名 tidy:converged tag 保留(历史数据不破)",
271
+ "tidy:converged" in (_fm(n6).get("tags") or []))
272
+ check("⑨3 降权后索引同步 converged",
273
+ _idx(n6).get(lifecycle.STATE_FIELD) == "converged")
274
+ _imp_before = float(_fm(n6).get("importance") or 0)
275
+ reinforce(cg, n6)
276
+ check("⑨4 MERGE 强化 → 逐级回升 active",
277
+ _fm(n6).get(lifecycle.STATE_FIELD) == "active", _fm(n6))
278
+ check("⑨5 回升与 importance 强化同批落盘(+delta)",
279
+ float(_fm(n6).get("importance") or 0) > _imp_before)
280
+ h6 = _fm(n6).get(lifecycle.HISTORY_FIELD) or []
281
+ check("⑨6 两处收口共用同一迁移历史(active→converged→active 两条,同一 actor 面)",
282
+ len(h6) == 2 and (h6[0]["from"], h6[0]["to"]) == ("active", "converged")
283
+ and (h6[1]["from"], h6[1]["to"]) == ("converged", "active")
284
+ and h6[0]["actor"] == "sustain_tidy"
285
+ and h6[1]["actor"] == "forgetting:reinforce", h6)
286
+ n7 = cg.add("lc_n7", "普通节点庚", layer="knowledge")
287
+ h7_before = len(_fm(n7).get(lifecycle.HISTORY_FIELD) or [])
288
+ reinforce(cg, n7)
289
+ check("⑨7 强化 active 节点:状态幂等(不刷历史、不误降)",
290
+ _fm(n7).get(lifecycle.STATE_FIELD) == "active"
291
+ and len(_fm(n7).get(lifecycle.HISTORY_FIELD) or []) == h7_before)
292
+
293
+ # ------------------------------------------------------------ ⑩ 边界
294
+ check("⑩1 状态字段名与裁决四态 state 不撞名(消歧)",
295
+ lifecycle.STATE_FIELD == "lifecycle_state"
296
+ and lifecycle.STATE_FIELD != "state")
297
+ check("⑩2 set_state 返回体恒带 from/to/code(可审计)",
298
+ set(("from", "to", "code")) <= set(cg.set_state(n7, "active").keys()))
299
+ check("⑩3 迁移历史滚动上限(不无界膨胀)",
300
+ lifecycle.HISTORY_KEEP > 0
301
+ and len(_fm(n1).get(lifecycle.HISTORY_FIELD) or [])
302
+ <= lifecycle.HISTORY_KEEP)
303
+
304
+ print("=" * 58)
305
+ print("test_lifecycle: %d 通过 / %d 失败" % (_ok, len(_fail)))
306
+ if _fail:
307
+ print("失败项:" + "、".join(_fail))
308
+ shutil.rmtree(cg.root, ignore_errors=True)
309
+ raise SystemExit(1 if _fail else 0)
@@ -0,0 +1,306 @@
1
+ # -*- coding: utf-8 -*-
2
+ """linkref(正文裸 id 引用 → reference 边)验收:R-L1b,2026-09-17。
3
+
4
+ 验收口径:抽取语义(命中/去重保序/排除自身/白名单防幽灵边/真实前缀域全覆盖/
5
+ 脏键与含中文 id 排除/id 紧邻汉字不漏抽/YAML 键名不误匹配)|端到端建边与幂等|
6
+ 覆写后「边跟随新正文」|负路由(REJECT/DEFER/gated 短路不建边)|韧性
7
+ (append_edge 抛异常不阻断写入)|opt-out|源判据(self 层留痕与脏键不作源)|
8
+ 链权重 reference=0.50 且不入因果链默认集|**读隔离两面**:密级面(known_ids 经
9
+ _readable 过滤,与库读面同口径)+ 密钥面(live_targets 实读校验,剔除密级够但
10
+ 无 DEK 的死目标,免生悬空边)。
11
+ """
12
+
13
+ import json
14
+ import os
15
+ import shutil
16
+ import sys
17
+ import tempfile
18
+ import traceback
19
+
20
+ from . import chain, linkref, writepipe
21
+ from .mdcos import MdCGSecure
22
+ from .security import Principal
23
+
24
+ _ok = 0
25
+ _bad = []
26
+ _seq = [0]
27
+
28
+
29
+ def _check(name, cond, detail=""):
30
+ global _ok
31
+ if cond:
32
+ _ok += 1
33
+ print(" ok %s" % name)
34
+ else:
35
+ _bad.append("%s %s" % (name, detail))
36
+ print(" FAIL %s %s" % (name, detail))
37
+
38
+
39
+ def _mk_cg(tmp, role="designer"):
40
+ _seq[0] += 1
41
+ root = os.path.join(tmp, "root_%02d_%s" % (_seq[0], role))
42
+ p = Principal(tenant="default", actor="t_" + role, role=role,
43
+ can_write=(role != "guest"),
44
+ can_admin=(role == "designer"))
45
+ return MdCGSecure(root, principal=p)
46
+
47
+
48
+ def _policy(tmp, required=("PASSED",), forbidden=("FORBIDDEN_WORD",)):
49
+ path = os.path.join(tmp, "policy.json")
50
+ with open(path, "w", encoding="utf-8") as f:
51
+ json.dump({"forbidden": list(forbidden),
52
+ "required": list(required)}, f)
53
+ os.environ["MDCG_POLICY_FILE"] = path
54
+ return path
55
+
56
+
57
+ def _fm(cg, nid):
58
+ got = cg._edge_node(nid)
59
+ return got[0] if got else {}
60
+
61
+
62
+ def _ref_edges(cg, nid):
63
+ fm = _fm(cg, nid)
64
+ return [e for e in (fm.get("edges") or [])
65
+ if isinstance(e, dict) and e.get("relation_type") == linkref.RELATION]
66
+
67
+
68
+ def main():
69
+ tmp = tempfile.mkdtemp(prefix="linkref_")
70
+ old_policy = os.environ.pop("MDCG_POLICY_FILE", None)
71
+ try:
72
+ _run(tmp)
73
+ except Exception:
74
+ traceback.print_exc()
75
+ _bad.append("未捕获异常")
76
+ finally:
77
+ shutil.rmtree(tmp, ignore_errors=True)
78
+ if old_policy is not None:
79
+ os.environ["MDCG_POLICY_FILE"] = old_policy
80
+ else:
81
+ os.environ.pop("MDCG_POLICY_FILE", None)
82
+ print("\nlinkref 验收:%d 通过%s" % (
83
+ _ok, (",%d 失败:%s" % (len(_bad), "; ".join(_bad))) if _bad else ""))
84
+ return 1 if _bad else 0
85
+
86
+
87
+ def _run(tmp):
88
+ policy = _policy(tmp)
89
+ pipe = writepipe.install_default_gates(writepipe.WritePipeline())
90
+ cg = _mk_cg(tmp)
91
+ A, B, TGT = "mem_1789125080573", "concept_d8eee20f2f", "mem_tgt0001"
92
+
93
+ # ---------- 1 纯函数:抽取语义 ----------
94
+ txt = "见 %s 与 %s;再一次 %s;%s" % (A, B, A, A)
95
+ got = linkref.extract_refs(txt, known={A, B})
96
+ _check("裸id命中", got == [A, B], repr(got))
97
+ _check("去重保序", got.count(A) == 1, repr(got))
98
+ _check("排除自身", linkref.extract_refs(txt, known={A, B}, exclude=A) == [B])
99
+ _check("白名单过滤未知id(防幽灵边)",
100
+ linkref.extract_refs(txt + " 幽灵 mem_9999999999999", known={A, B})
101
+ == [A, B])
102
+ _check("known=None按形态放行", linkref.extract_refs(txt, known=None) == [A, B])
103
+ _check("真实前缀域命中(kp/note/vpipe/arc)",
104
+ linkref.extract_refs(
105
+ "见 kp_GIL_1787752832598 与 note_abc123 与 vpipe_0001 与 arc_0001",
106
+ known={"kp_GIL_1787752832598", "note_abc123", "vpipe_0001",
107
+ "arc_0001"})
108
+ == ["kp_GIL_1787752832598", "note_abc123", "vpipe_0001", "arc_0001"])
109
+ _check("非库内id被白名单拦截(rev/prop/sess)",
110
+ linkref.extract_refs("rev_483f03ac3558_r1 prop_1a2b3c sess_abc",
111
+ known=set()) == [])
112
+ _check("YAML式键名被白名单拦截",
113
+ linkref.extract_refs("part_of: x derived_from: y relation_type: z",
114
+ known={A}) == [])
115
+ _check("id紧邻汉字仍命中(非词边界误判)",
116
+ linkref.extract_refs("见%s的说明" % A, known={A}) == [A])
117
+ _check("形态近似但更长的id不误命中",
118
+ linkref.extract_refs(A + "9", known={A}) == [])
119
+ _check("形态判据放行规范id",
120
+ linkref.is_linkable_id(A) and linkref.is_linkable_id("kp_GIL_1787"))
121
+ _check("形态判据排除脏键(None/中文标题式/含中文混合id)",
122
+ not linkref.is_linkable_id("None")
123
+ and not linkref.is_linkable_id("README中英差距报告节(94.6vs52)")
124
+ and not linkref.is_linkable_id("kp_GIL全局解_1787752832598"))
125
+ _check("空文本", linkref.extract_refs("", known={A}) == [])
126
+
127
+ # ---------- 2 边形态与链权重 ----------
128
+ e = linkref.make_edge(A)
129
+ _check("边形态", e["target"] == A and e["relation_type"] == "reference"
130
+ and e["verified"] is False and "created_at" in e, repr(e))
131
+ _check("边权重=0.50", chain.edge_weight(e) == 0.5, repr(chain.edge_weight(e)))
132
+ _check("reference已登记权重表", chain.EDGE_WEIGHTS.get("reference") == 0.50)
133
+ _check("reference不入因果链默认集",
134
+ "reference" not in chain.CHAIN_TYPES_DEFAULT)
135
+ _check("reference不入因果类型集", "reference" not in chain.CAUSAL_TYPES)
136
+
137
+ # ---------- 2b 源节点层/形态判据(审计留痕与脏键不作源) ----------
138
+ _check("self层不作源", linkref.is_linkable_source(A, "self") is False)
139
+ _check("记忆本体层可作源",
140
+ all(linkref.is_linkable_source(A, x)
141
+ for x in ("knowledge", "contextual", "rejected", "", None)))
142
+ _check("脏键id不作源", linkref.is_linkable_source("None", "knowledge") is False)
143
+ _check("self层排除常量", linkref.SOURCE_EXCLUDE_LAYERS == ("self",))
144
+
145
+ # ---------- 2c 白名单读隔离一致性(known_ids 与库读面同口径) ----------
146
+ class _StubCG:
147
+ def __init__(self, nodes, guard=None):
148
+ self.index = {"nodes": nodes}
149
+ if guard is not None:
150
+ self._readable = guard
151
+ _n = {A: {"s": "internal"}, B: {"s": "secret"}}
152
+ _check("known_ids按读隔离过滤",
153
+ linkref.known_ids(_StubCG(_n, lambda e: e["s"] == "internal"))
154
+ == {A})
155
+ _check("known_ids无读面时不过滤(非安全库MdCG)",
156
+ linkref.known_ids(_StubCG(_n)) == {A, B})
157
+ _check("known_ids读面抛异常则fail-closed",
158
+ linkref.known_ids(_StubCG(_n, lambda e: 1 / 0)) == set())
159
+ _check("known_ids无索引回退空集", linkref.known_ids(object()) == set())
160
+
161
+ # ---------- 2d 目标侧第二道读隔离:密钥面(live_targets 实读校验) ----------
162
+ # 背景(20260917 取证):private/secret 正文是密文,解封需本身份 DEK;
163
+ # 无 DEK 时 get 返回 None,但 _readable 只看索引密级字段 → 白名单会混入
164
+ # 「密级够、密钥不够」的死目标(真实库 secret 下 459 条),建边即产生悬空边。
165
+ class _StubReadCG:
166
+ def __init__(self, ok, boom=()):
167
+ self.calls = []
168
+ self._ok = set(ok)
169
+ self._boom = set(boom)
170
+
171
+ def get(self, nid):
172
+ self.calls.append(nid)
173
+ if nid in self._boom:
174
+ raise RuntimeError("locked")
175
+ return {"id": nid} if nid in self._ok else None
176
+
177
+ _s = _StubReadCG(ok={A}, boom={"mem_locked1"})
178
+ _check("live_targets保留实读可得目标、剔除密钥隔离目标",
179
+ linkref.live_targets(_s, [A, "mem_locked1", "mem_missing1"]) == [A],
180
+ repr(_s.calls))
181
+ _check("live_targets夹带抛异常目标fail-closed",
182
+ linkref.live_targets(_s, ["mem_locked1"]) == [])
183
+ _s2 = _StubReadCG(ok={A})
184
+ _cache = {}
185
+ linkref.live_targets(_s2, [A, A], cache=_cache)
186
+ _check("live_targets缓存复用(同id只实读一次)",
187
+ _s2.calls == [A] and _cache.get(A) is True, repr(_s2.calls))
188
+ _check("live_targets空输入", linkref.live_targets(_s2, []) == [])
189
+
190
+ # ---------- 3 端到端 ----------
191
+ out_t = pipe.execute(cg, {"content_kind": "text", "content": "目标 PASSED",
192
+ "layer": "knowledge", "node_id": TGT})
193
+ _check("目标节点落盘", out_t.get("committed") is True, repr(out_t))
194
+ out_s = pipe.execute(cg, {"content_kind": "text",
195
+ "content": "PASSED 关联 %s 的说明" % TGT,
196
+ "layer": "knowledge", "node_id": "mem_src0001"})
197
+ _check("源节点落盘", out_s.get("committed") is True, repr(out_s))
198
+ refs = _ref_edges(cg, "mem_src0001")
199
+ _check("端到端建reference边",
200
+ len(refs) == 1 and refs[0]["target"] == TGT, repr(refs))
201
+
202
+ pipe.execute(cg, {"content_kind": "text",
203
+ "content": "PASSED 关联 %s 的说明" % TGT,
204
+ "layer": "knowledge", "node_id": "mem_src0001"})
205
+ _check("重复写入幂等", len(_ref_edges(cg, "mem_src0001")) == 1,
206
+ repr(_ref_edges(cg, "mem_src0001")))
207
+
208
+ pipe.execute(cg, {"content_kind": "text",
209
+ "content": "PASSED 关联 mem_ghost9999",
210
+ "layer": "knowledge", "node_id": "mem_src0002"})
211
+ _check("未入库目标不建边", _ref_edges(cg, "mem_src0002") == [])
212
+
213
+ pipe.execute(cg, {"content_kind": "text",
214
+ "content": "PASSED 本节点 mem_self0001 见 %s" % TGT,
215
+ "layer": "knowledge", "node_id": "mem_self0001"})
216
+ _check("正文自引用被排除",
217
+ [x["target"] for x in _ref_edges(cg, "mem_self0001")] == [TGT],
218
+ repr(_ref_edges(cg, "mem_self0001")))
219
+
220
+ pipe.execute(cg, {"content_kind": "text",
221
+ "content": "PASSED 关联 %s" % TGT, "linkref": False,
222
+ "layer": "knowledge", "node_id": "mem_src0003"})
223
+ _check("linkref=False显式关闭", _ref_edges(cg, "mem_src0003") == [])
224
+
225
+ pipe.execute(cg, {"content_kind": "text", "content": "目标2 PASSED",
226
+ "layer": "knowledge", "node_id": "mem_tgt0002"})
227
+ pipe.execute(cg, {"content_kind": "text",
228
+ "content": "PASSED 现改关联 mem_tgt0002",
229
+ "layer": "knowledge", "node_id": "mem_src0001"})
230
+ _check("覆写后边跟随新正文",
231
+ [x["target"] for x in _ref_edges(cg, "mem_src0001")]
232
+ == ["mem_tgt0002"], repr(_ref_edges(cg, "mem_src0001")))
233
+
234
+ # ---------- 4 负路由 ----------
235
+ out_r = pipe.execute(cg, {"content_kind": "text",
236
+ "content": "FORBIDDEN_WORD 关联 %s" % TGT,
237
+ "layer": "knowledge", "node_id": "mem_rej0001"})
238
+ _check("REJECT路径不落盘不建边",
239
+ out_r.get("committed") is False and _fm(cg, "mem_rej0001") == {},
240
+ repr(out_r))
241
+
242
+ os.environ.pop("MDCG_POLICY_FILE", None)
243
+ out_d = pipe.execute(cg, {"content_kind": "text",
244
+ "content": "无规则可判 关联 %s" % TGT,
245
+ "layer": "knowledge", "node_id": "mem_def0001"})
246
+ _check("DEFER路径不落盘不建边",
247
+ out_d.get("moved_to") == "review_queue"
248
+ and _fm(cg, "mem_def0001") == {}, repr(out_d))
249
+ os.environ["MDCG_POLICY_FILE"] = policy
250
+
251
+ out_g = pipe.execute(cg, {"content_kind": "text",
252
+ "content": "PASSED 关联 %s" % TGT, "gated": True,
253
+ "layer": "contextual", "node_id": "mem_gat0001"})
254
+ _check("gated走替代执行路径(短路→after不执行)",
255
+ "gate" in out_g, repr(out_g))
256
+
257
+ # ---------- 5 韧性:建链失败不阻断写入 ----------
258
+ p2 = writepipe.install_default_gates(writepipe.WritePipeline())
259
+ cg2 = _mk_cg(tmp)
260
+ p2.execute(cg2, {"content_kind": "text", "content": "目标 PASSED",
261
+ "layer": "knowledge", "node_id": "mem_f0"})
262
+ orig = cg2.append_edge
263
+
264
+ def _boom(*a, **k):
265
+ raise RuntimeError("append_edge boom")
266
+
267
+ cg2.append_edge = _boom
268
+ try:
269
+ out = p2.execute(cg2, {"content_kind": "text",
270
+ "content": "PASSED 关联 mem_f0",
271
+ "layer": "knowledge", "node_id": "mem_f1"})
272
+ _check("建链失败不阻断写入", out.get("committed") is True, repr(out))
273
+ except Exception as ex: # noqa: BLE001
274
+ _check("建链失败不阻断写入", False, "异常%r" % ex)
275
+ finally:
276
+ cg2.append_edge = orig
277
+
278
+ # ---------- 5b 目标实读不出(密钥隔离)→ 不建边 ----------
279
+ p3 = writepipe.install_default_gates(writepipe.WritePipeline())
280
+ cg3 = _mk_cg(tmp)
281
+ p3.execute(cg3, {"content_kind": "text", "content": "目标3 PASSED",
282
+ "layer": "knowledge", "node_id": "mem_k0"})
283
+ g_orig = cg3.get
284
+
285
+ def _locked(nid, *a, **k):
286
+ return None if nid == "mem_k0" else g_orig(nid, *a, **k)
287
+
288
+ cg3.get = _locked
289
+ try:
290
+ out = p3.execute(cg3, {"content_kind": "text",
291
+ "content": "PASSED 关联 mem_k0",
292
+ "layer": "knowledge", "node_id": "mem_k1"})
293
+ _check("目标实读不出(密钥隔离)不建边——免生悬空边",
294
+ out.get("committed") is True and _ref_edges(cg3, "mem_k1") == [],
295
+ repr(_ref_edges(cg3, "mem_k1")))
296
+ finally:
297
+ cg3.get = g_orig
298
+
299
+ # ---------- 6 边域幂等(append_edge 本体) ----------
300
+ _check("append_edge自身幂等",
301
+ cg.append_edge("mem_tgt0002", linkref.make_edge(TGT)) is True
302
+ and cg.append_edge("mem_tgt0002", linkref.make_edge(TGT)) is False)
303
+
304
+
305
+ if __name__ == "__main__":
306
+ sys.exit(main())
@@ -25,7 +25,7 @@ import tempfile
25
25
  _HERE = os.path.dirname(os.path.abspath(__file__))
26
26
  sys.path.insert(0, os.path.dirname(_HERE))
27
27
 
28
- from .md_whitebox import DEFAULT_ROOT, build_db_from_md # noqa: E402
28
+ from .md_whitebox import DEFAULT_ROOT, build_db_from_md, corpus_gap # noqa: E402
29
29
 
30
30
  # 平铺导入引导(对齐 whitebox_kb/__init__:md_access 需在 sys.path 注入后导入)
31
31
  _WB = os.path.join(_HERE, "whitebox_kb")
@@ -134,6 +134,13 @@ def _param_groups(arg_tpls, samples):
134
134
 
135
135
  def main():
136
136
  ok = True
137
+ # 依赖自辩(2026-09-20 v14 缺陷 F):md 语料是 gitignored 本地数据面,
138
+ # 缺失/空壳时**本模块自己**打 SKIP 返回 0——不再依赖外部 runner 探测
139
+ # (旁路执行时 runner 不在场),也不再留下未捕获异常或空壳副作用。
140
+ _gap = corpus_gap(DEFAULT_ROOT)
141
+ if _gap:
142
+ print("SKIP test_md_access_parity:%s" % _gap)
143
+ return 0
137
144
  print("== [1] 重建派生库 + 建 MdConn ==")
138
145
  stats = build_db_from_md(force=True, verbose=True)
139
146
  md_conn = MdConn(DEFAULT_ROOT, layers=LAYERS)
@@ -142,8 +149,13 @@ def main():
142
149
  n_sq = sq.execute("SELECT COUNT(*) FROM nodes").fetchone()[0]
143
150
  n_md = len(md_conn._all_rows())
144
151
  print(f" 派生库 nodes={n_sq} md 直读行={n_md}")
145
- ok &= _ok(n_sq == n_md == stats["nodes"] == 4355,
146
- "行数等价且为 4355")
152
+ # 判据=三方等价(派生库 == md 直读 == 还原计数)。旧断言把 `== 4355`(导出前
153
+ # 快照行数)一并写死:绝对行数是本地数据面规模、非代码契约,锚死它会让「数据面
154
+ # 正常生长」被误判成等价性破坏(2026-09-19 重跑 migrate_wisdom_graph.export 后
155
+ # 4355 → 4459 即由此假红)。保留量级下界防退化为空库。
156
+ ok &= _ok(n_sq == n_md == stats["nodes"] and n_sq >= 4000,
157
+ f"行数三方等价(派生库 {n_sq} == md 直读 {n_md} == "
158
+ f"还原 {stats['nodes']})且 >= 4000")
147
159
 
148
160
  print("== [2] 8 处真实 SQL 逐位对拍 ==")
149
161
  names, prefixes, kps = _sample_params(md_conn)