@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,280 @@
1
+ # -*- coding: utf-8 -*-
2
+ """查询侧时效过滤(validity)验收 · GBrain 可借鉴点交接 §3②
3
+
4
+ 【裁决】使用者三处拍板,是本文件全部断言的判据来源:
5
+ ① 候选面**只排已过期**(valid_until 已过);**未生效(valid_from 未到)保留**。
6
+ 依据仓内既有纪律:`scrub` 模块「valid_from 绝不并入 _EXPIRY_KEYS」——
7
+ `not_yet`(尚未开始)与 `expired`(已经失效)语义相反;预约/计划类记忆
8
+ 在生效前仍应可召回,故不得用「时间轴上不活跃」一刀切。
9
+ ② 启用默认值 = **opt-in 默认关**(缺省不过滤):零 rust 镜像义务(保 py↔rust
10
+ 零漂移硬承诺)、零 bench 漂移、完全可逆。
11
+ ③ 过滤落在**候选层**(一处过滤全链路生效:search / search_rrf / recall),
12
+ 与 session / branch 过滤同构;MCP 面三入口透传。
13
+
14
+ 【覆盖】
15
+ ① `trust.is_expired` 布尔语义(四态→布尔;端点不可解析不猜测)+ scrub 纪律交叉守卫
16
+ ② 候选面:MdCG.search(裸核心)/ MdCGSecure.search(角色面)
17
+ ③ 默认关:缺省口径与启用口径逐条对照(opt-in 可逆性)
18
+ ④ search_rrf:融合面 + query 热缓存分键(validity 不入键会串口径)
19
+ ⑤ recall:RRF 分支 + 非 RRF 分支
20
+ ⑥ 图扩展:零词面交集节点经 edges 扩散可达时同样受 validity 约束
21
+ ⑦ MCP 面三入口透传(cg op=read / mdcg_search / mdcg_recall)
22
+
23
+ 【不适用】写入侧(add 落 valid_from/valid_until 是本项前提,非本项范围);
24
+ 库内真实数据面(当前 13514 节点零有效期,故本项为 opt-in 零漂移)。
25
+ """
26
+ import os
27
+ import shutil
28
+ import sys
29
+ import tempfile
30
+ import traceback
31
+
32
+ sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
33
+
34
+ # Windows cmd 默认 GBK:测试自带 UTF-8 兜底,不依赖调用方记得加 -X utf8。
35
+ if hasattr(sys.stdout, "reconfigure"):
36
+ sys.stdout.reconfigure(encoding="utf-8", errors="replace")
37
+
38
+ from md_cg import hotcache, mcp_server as MS, scrub, trust
39
+ from md_cg.mdcg import MdCG
40
+ from md_cg.mdcos import MdCGSecure
41
+ from md_cg.security import DEFAULT_SENSITIVITY, Principal
42
+
43
+ _ok = 0
44
+ _bad = []
45
+
46
+
47
+ def check(name, cond, detail=""):
48
+ global _ok
49
+ if cond:
50
+ _ok += 1
51
+ print("[PASS] " + name)
52
+ else:
53
+ _bad.append(name)
54
+ print("[FAIL] %s · %s" % (name, str(detail)[:220]))
55
+
56
+
57
+ Q = "量子咖啡机萃取压力曲线" # 罕见词:测试库内确定性命中
58
+ PAST, FUT = "2001-01-01", "2099-01-01"
59
+
60
+
61
+ def _princ():
62
+ return Principal(actor="t_validity", clearance=DEFAULT_SENSITIVITY,
63
+ can_write=True, can_admin=True, role="designer")
64
+
65
+
66
+ def _ids(res):
67
+ """命中集 id 清单。
68
+
69
+ 口径=**nid 是否在命中集**,而非「结果为空」——全扫兜底返回低分节点
70
+ 是检索器既有行为,不属过滤失败。
71
+ """
72
+ out = []
73
+ for r in res or []:
74
+ n = r[0] if isinstance(r, tuple) else r
75
+ if isinstance(n, dict) and isinstance(n.get("node"), dict):
76
+ n = n["node"] # _cg_call(op=read) 的 {"node": view} 包装
77
+ out.append((n or {}).get("id"))
78
+ return out
79
+
80
+
81
+ def _seed(cg):
82
+ """三态语料:expired / not_yet / unknown + 一条图扩展专用的 expired。"""
83
+ cg.add("mem_now", Q + " 现行规格 9 巴", layer="knowledge",
84
+ verification_basis="test", consistency=False)
85
+ cg.add("mem_old", Q + " 旧版规格已被取代", layer="knowledge",
86
+ verification_basis="test", consistency=False, valid_until=PAST)
87
+ cg.add("mem_future", Q + " 下一代规格预告", layer="knowledge",
88
+ verification_basis="test", consistency=False, valid_from=FUT)
89
+ cg.add("mem_hub", Q + " 主档索引", layer="knowledge",
90
+ verification_basis="test", consistency=False,
91
+ edges=[{"target": "mem_ghost", "relation_type": "causal"}])
92
+ # 词面与 Q 零交集:只能经图扩展(edges)抵达 —— 图扩展绕过候选层时的靶子
93
+ cg.add("mem_ghost", "旁系条目与提问没有任何词面重合", layer="knowledge",
94
+ verification_basis="test", consistency=False, valid_until=PAST)
95
+
96
+
97
+ # ---------------------------------------------------------------- ① 纯函数
98
+ def _t1():
99
+ check("①expired → True", trust.is_expired({"valid_until": PAST}) is True)
100
+ check("①not_yet 不算过期(语义相反纪律)",
101
+ trust.is_expired({"valid_from": FUT}) is False)
102
+ check("①active → False",
103
+ trust.is_expired({"valid_from": "2020-01-01",
104
+ "valid_until": "2030-01-01"}) is False)
105
+ check("①unknown(无时间轴)→ False", trust.is_expired({}) is False)
106
+ check("①None 入参 → False", trust.is_expired(None) is False)
107
+ check("①端点不可解析 → 不猜测(False,不误判为过期)",
108
+ trust.is_expired({"valid_until": "不是时间"}) is False)
109
+ check("①now 显式传入生效",
110
+ trust.is_expired({"valid_until": "2030-01-01"},
111
+ now=trust.parse_time("2035-01-01")) is True)
112
+ check("①判定序:未来起点优先(not_yet 而非 expired)",
113
+ trust.validity({"valid_from": FUT, "valid_until": PAST})[0] == "not_yet")
114
+ check("①scrub 纪律交叉守卫:valid_from 绝不并入 _EXPIRY_KEYS",
115
+ "valid_from" not in scrub._EXPIRY_KEYS
116
+ and "valid_until" in scrub._EXPIRY_KEYS
117
+ and "valid_from" in scrub._NOT_YET_KEYS,
118
+ str(scrub._EXPIRY_KEYS))
119
+ # ---- 2026-09-19 阶段一:规范键族 + 第三类语义(信念时间)交叉守卫 ----
120
+ check("①阶段一:effective_from 属未生效族、绝不入已结束族",
121
+ "effective_from" in scrub._NOT_YET_KEYS
122
+ and "effective_from" not in scrub._EXPIRY_KEYS,
123
+ str(scrub._EXPIRY_KEYS))
124
+ check("①阶段一:effective_until/expired_at 属已结束族、不入未生效族",
125
+ "effective_until" in scrub._EXPIRY_KEYS
126
+ and "expired_at" in scrub._EXPIRY_KEYS
127
+ and "effective_until" not in scrub._NOT_YET_KEYS
128
+ and "expired_at" not in scrub._NOT_YET_KEYS,
129
+ str(scrub._EXPIRY_KEYS))
130
+ check("①阶段一:believed_at 两族都不入(第三类语义·物理隔离)",
131
+ trust.BELIEVED_FIELD not in scrub._EXPIRY_KEYS
132
+ and trust.BELIEVED_FIELD not in scrub._NOT_YET_KEYS,
133
+ str(scrub._EXPIRY_KEYS))
134
+ check("①键族同步守卫:trust 别名元组 ⊆ scrub 对应键族(两侧禁各自漂移)",
135
+ set(trust.FROM_ALIASES) <= set(scrub._NOT_YET_KEYS)
136
+ and set(trust.UNTIL_ALIASES) <= set(scrub._EXPIRY_KEYS),
137
+ str(trust.FROM_ALIASES) + " / " + str(scrub._NOT_YET_KEYS))
138
+ # ---- 规范键优先、别名回落(双轨读取)----
139
+ check("①规范键优先:effective_from 压过 valid_from",
140
+ trust.validity({"effective_from": FUT,
141
+ "valid_from": "2020-01-01"})[0] == "not_yet")
142
+ check("①别名回落:仅历史键时照常判定(存量兼容)",
143
+ trust.validity({"valid_from": FUT})[0] == "not_yet")
144
+ check("①规范键不可解析 → 继续回落别名(不因写坏一键而漏判)",
145
+ trust.validity({"effective_until": "坏值",
146
+ "valid_until": PAST})[0] == "expired")
147
+ check("①believed_at 不参与时效判定(物理隔离·绝不误判)",
148
+ trust.validity({"believed_at": PAST})[0] == "unknown"
149
+ and trust.is_expired({"believed_at": PAST}) is False)
150
+ check("①believed_at 读取器:epoch 值 / 缺字段 None",
151
+ trust.believed_at({"believed_at": PAST}) is not None
152
+ and trust.believed_at({}) is None)
153
+ check("①time_window_msg 点名实际命中键(规范名/历史名各归其位)",
154
+ "effective_from" in trust.time_window_msg({"effective_from": FUT})
155
+ and "valid_from" in trust.time_window_msg({"valid_from": FUT}))
156
+
157
+
158
+ # ------------------------------------------------- ②③ 候选面 + 默认关
159
+ def _t2(root_core, root_sec):
160
+ core = MdCG(root_core)
161
+ _seed(core)
162
+
163
+ off = _ids(core.search(Q, k=20)[0])
164
+ check("③MdCG.search 默认关:expired 可见(opt-in 可逆)",
165
+ "mem_old" in off, off)
166
+ check("③MdCG.search 默认关:not_yet 可见", "mem_future" in off, off)
167
+ on = _ids(core.search(Q, k=20, validity=True)[0])
168
+ check("②MdCG.search 启用后 expired 剔除", "mem_old" not in on, on)
169
+ check("②MdCG.search 启用后 not_yet 保留", "mem_future" in on, on)
170
+ check("②MdCG.search 启用后 unknown 保留", "mem_now" in on, on)
171
+
172
+ sec = MdCGSecure(root_sec, principal=_princ())
173
+ _seed(sec)
174
+ sec_off = _ids(sec.search(Q, k=20)[0])
175
+ check("③Secure.search 默认关:expired 可见", "mem_old" in sec_off, sec_off)
176
+ sec_on = _ids(sec.search(Q, k=20, validity=True)[0])
177
+ check("②Secure.search 启用后 expired 剔除", "mem_old" not in sec_on, sec_on)
178
+ check("②Secure.search 启用后 not_yet 保留",
179
+ "mem_future" in sec_on, sec_on)
180
+
181
+
182
+ # --------------------------- ④ RRF + 缓存分键 ⑤ recall ⑥ 图扩展
183
+ def _t3(root):
184
+ cg = MdCGSecure(root, principal=_princ())
185
+ _seed(cg)
186
+ hotcache.attach(cg)
187
+
188
+ off, _ = cg.search_rrf(Q, k=20)
189
+ on, _ = cg.search_rrf(Q, k=20, validity=True)
190
+ check("④search_rrf 默认关:expired 可见", "mem_old" in _ids(off), _ids(off))
191
+ check("④search_rrf 启用后 expired 剔除", "mem_old" not in _ids(on), _ids(on))
192
+ check("④search_rrf 启用后 not_yet 保留", "mem_future" in _ids(on), _ids(on))
193
+
194
+ # 缓存分键:两口径必须各自成键,否则第二次调用命中第一次的缓存即串口径
195
+ hc = hotcache.get(cg)
196
+ on2, meta2 = cg.search_rrf(Q, k=20, validity=True)
197
+ off2, meta_off = cg.search_rrf(Q, k=20)
198
+ check("④缓存命中路径仍零 expired(meta.cached 可证命中)",
199
+ "mem_old" not in _ids(on2) and meta2.get("cached") is True,
200
+ "cached=%s ids=%s" % (meta2.get("cached"), _ids(on2)))
201
+ check("④两口径各自成键且互不顶替(默认口径仍见 expired 且命中自身缓存)",
202
+ "mem_old" in _ids(off2) and meta_off.get("cached") is True,
203
+ "off_cached=%s ids=%s" % (meta_off.get("cached"), _ids(off2)))
204
+ check("④缓存中两口径键并存(>=2 条)",
205
+ hc.stats()["query_cache_size"] >= 2, hc.stats())
206
+
207
+ # ⑥ 图扩展:零词面交集节点只能经 edges 抵达 —— 前置证据 + 过滤断言
208
+ check("⑥前置证据:不过滤时 ghost 经图扩展可达(零词面交集)",
209
+ "mem_ghost" in _ids(off), _ids(off))
210
+ check("⑥启用后 ghost 被剔除(图扩展不绕过时效过滤)",
211
+ "mem_ghost" not in _ids(on), _ids(on))
212
+
213
+ # ⑤ recall 两分支
214
+ p_off = [p.get("id") for p in cg.recall(Q, budget_tokens=6000, k=20)["pack"]]
215
+ check("⑤recall(RRF 分支)默认关:expired 可见", "mem_old" in p_off, p_off)
216
+ p_rrf = [p.get("id") for p in cg.recall(Q, budget_tokens=6000, k=20,
217
+ validity=True)["pack"]]
218
+ check("⑤recall(RRF 分支)启用后 expired 剔除", "mem_old" not in p_rrf, p_rrf)
219
+ check("⑤recall(RRF 分支)not_yet 保留", "mem_future" in p_rrf, p_rrf)
220
+ p_lin = [p.get("id") for p in cg.recall(Q, budget_tokens=6000, k=20,
221
+ use_rrf=False, validity=True)["pack"]]
222
+ check("⑤recall(非 RRF 分支)启用后 expired 剔除",
223
+ "mem_old" not in p_lin, p_lin)
224
+ check("⑤recall(非 RRF 分支)not_yet 保留", "mem_future" in p_lin, p_lin)
225
+
226
+
227
+ # ------------------------------------------------------------- ⑦ MCP 面
228
+ def _t4(root):
229
+ cg = MdCGSecure(root, principal=_princ())
230
+ _seed(cg)
231
+
232
+ s_on = MS.call_tool(cg, "mdcg_search",
233
+ {"query": Q, "k": 20, "validity": True})
234
+ ids_on = [r["node"]["id"] for r in s_on["results"]]
235
+ check("⑦mdcg_search 透传 validity(expired 剔除)",
236
+ "mem_old" not in ids_on, ids_on)
237
+ check("⑦mdcg_search 透传后 not_yet 保留", "mem_future" in ids_on, ids_on)
238
+ s_off = MS.call_tool(cg, "mdcg_search", {"query": Q, "k": 20})
239
+ ids_off = [r["node"]["id"] for r in s_off["results"]]
240
+ check("⑦默认关:mdcg_search 缺省 expired 可见", "mem_old" in ids_off, ids_off)
241
+
242
+ rc = MS.call_tool(cg, "mdcg_recall",
243
+ {"query": Q, "k": 20, "budget_tokens": 6000,
244
+ "validity": True})
245
+ pids = [p.get("id") for p in rc["pack"]]
246
+ check("⑦mdcg_recall 透传 validity(expired 剔除)",
247
+ "mem_old" not in pids, pids)
248
+ rc_off = MS.call_tool(cg, "mdcg_recall",
249
+ {"query": Q, "k": 20, "budget_tokens": 6000})
250
+ check("⑦默认关:mdcg_recall 缺省 expired 可见",
251
+ "mem_old" in [p.get("id") for p in rc_off["pack"]], rc_off.get("pack"))
252
+
253
+ out = MS._cg_call(cg, {"op": "read", "query": Q, "k": 20, "validity": True})
254
+ oids = _ids(out.get("results") or [])
255
+ check("⑦cg op=read 透传 validity(expired 剔除)", "mem_old" not in oids, oids)
256
+ out_off = MS._cg_call(cg, {"op": "read", "query": Q, "k": 20})
257
+ check("⑦默认关:cg op=read 缺省 expired 可见",
258
+ "mem_old" in _ids(out_off.get("results") or []),
259
+ _ids(out_off.get("results") or []))
260
+
261
+
262
+ def main():
263
+ tmp = tempfile.mkdtemp(prefix="mdcg_validity_")
264
+ try:
265
+ _t1()
266
+ _t2(os.path.join(tmp, "core"), os.path.join(tmp, "sec"))
267
+ _t3(os.path.join(tmp, "rrf"))
268
+ _t4(os.path.join(tmp, "mcp"))
269
+ except Exception:
270
+ traceback.print_exc()
271
+ _bad.append("未捕获异常")
272
+ finally:
273
+ shutil.rmtree(tmp, ignore_errors=True)
274
+ print("\nvalidity 验收:%d 通过%s" % (
275
+ _ok, (",%d 失败:%s" % (len(_bad), "; ".join(_bad))) if _bad else ""))
276
+ return 1 if _bad else 0
277
+
278
+
279
+ if __name__ == "__main__":
280
+ sys.exit(main())
@@ -26,6 +26,7 @@ for _p in (_ROOT, _HERE, os.path.join(_HERE, "whitebox_kb"),
26
26
  from wisdom_book import (ConditionDex, DEFAULT_DB, DEFAULT_MD_ROOT, # noqa: E402
27
27
  BASE_ENTRIES, BASE_RELATIONS)
28
28
  from md_access import MdConn, read_conn # noqa: E402
29
+ from md_whitebox import corpus_ready # noqa: E402
29
30
  from md_store import MdStore # noqa: E402
30
31
  from aeis_core import ConditionSpace, MemoryLayer, STNode # noqa: E402
31
32
 
@@ -44,8 +45,11 @@ def ok(cond, label):
44
45
  # ---------------------------------------------------------------------------
45
46
 
46
47
  def group_a(tmp):
47
- if not (os.path.exists(DEFAULT_DB) and os.path.isdir(DEFAULT_MD_ROOT)):
48
- print("组A SKIP:生产对拍数据不全(派生库或语料根缺失)")
48
+ # 语料就绪判据 = 「目录存在**且含 .md**」(2026-09-20 v14 缺陷 F):空壳
49
+ # 目录(兄弟测试经 MdCGOS(root) 的 makedirs 留下)不算就绪,否则组A 会
50
+ # 以「md 语料行数 0 > 4000」的失败示人,而真实原因是真源根本不在位。
51
+ if not (os.path.exists(DEFAULT_DB) and corpus_ready(DEFAULT_MD_ROOT)):
52
+ print("组A SKIP:生产对拍数据不全(派生库缺失或语料根缺失/空壳)")
49
53
  return
50
54
  print("组A · 生产数据对拍(全量)")
51
55
  # sqlite 侧用副本隔离:search_content 的 increment_access 会落库,
@@ -258,7 +262,7 @@ def group_d(tmp):
258
262
  ok(not dex2._md_mode, "fresh=True → sqlite 形态(兼容 api.py 兜底)")
259
263
  dex2.close()
260
264
 
261
- if os.path.isdir(DEFAULT_MD_ROOT):
265
+ if corpus_ready(DEFAULT_MD_ROOT):
262
266
  dex3 = ConditionDex(md_root=DEFAULT_MD_ROOT)
263
267
  try:
264
268
  conn = read_conn(dex3) # duck:MdStore.conn → MdConn
@@ -0,0 +1,214 @@
1
+ # -*- coding: utf-8 -*-
2
+ """写入路径拦截器链(writepipe)验收:Pi 钩子化移植(交接文档 §3⑥)。
3
+
4
+ 验收口径:
5
+ - 默认链行为与重构前 _cg_dispatch write 分支逐字节一致
6
+ (ACCEPT 直写 / REJECT 负记忆 / DEFER 入队 + 幂等去重联动 / gated 闸 /
7
+ 冲突 cvd 透传);
8
+ - 新增/移除一个拦截器不改核心文件(register/unregister 即插即拔);
9
+ - 角色/层权限校验结构上不可被拦截器绕过(反面清单:信任必须结构强制);
10
+ - REWRITE(拦截器改写 ctx["a"]["content"])/ 链序 / 短路 / after 观测者。
11
+ """
12
+
13
+ import json
14
+ import os
15
+ import shutil
16
+ import sys
17
+ import tempfile
18
+ import traceback
19
+
20
+ from . import writepipe
21
+ from .mdcos import MdCGSecure
22
+ from .security import AccessDenied, 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 main():
58
+ tmp = tempfile.mkdtemp(prefix="writepipe_")
59
+ old_policy = os.environ.pop("MDCG_POLICY_FILE", None)
60
+ try:
61
+ _run(tmp)
62
+ except Exception:
63
+ traceback.print_exc()
64
+ _bad.append("未捕获异常")
65
+ finally:
66
+ shutil.rmtree(tmp, ignore_errors=True)
67
+ if old_policy is not None:
68
+ os.environ["MDCG_POLICY_FILE"] = old_policy
69
+ else:
70
+ os.environ.pop("MDCG_POLICY_FILE", None)
71
+ print("\nwritepipe 验收:%d 通过%s" % (
72
+ _ok, (",%d 失败:%s" % (len(_bad), "; ".join(_bad))) if _bad else ""))
73
+ return 1 if _bad else 0
74
+
75
+
76
+ def _run(tmp):
77
+ policy = _policy(tmp)
78
+
79
+ # ---------- 1 默认链:与重构前 write 分支行为一致 ----------
80
+ pipe = writepipe.install_default_gates(writepipe.WritePipeline())
81
+ cg = _mk_cg(tmp)
82
+
83
+ out = pipe.execute(cg, {"content_kind": "text",
84
+ "content": "要素齐全 PASSED",
85
+ "layer": "knowledge"})
86
+ _check("默认链ACCEPT直写", out.get("committed") is True and out.get("ok") is True,
87
+ repr(out))
88
+ _check("冲突闸cvd透传", isinstance(out.get("consistency"), dict), repr(out))
89
+
90
+ out = pipe.execute(cg, {"content_kind": "text",
91
+ "content": "含 FORBIDDEN_WORD 的违禁内容",
92
+ "layer": "knowledge"})
93
+ _check("REJECT负记忆", out.get("moved_to") == "rejected"
94
+ and out.get("committed") is False, repr(out))
95
+
96
+ # DEFER(无规则文件 → 无法判定 → 入审核队列)+ 幂等去重联动
97
+ os.environ.pop("MDCG_POLICY_FILE", None)
98
+ a = {"content_kind": "text", "content": "无规则可判的内容",
99
+ "layer": "knowledge"}
100
+ out1 = pipe.execute(cg, dict(a))
101
+ _check("DEFER入队", out1.get("moved_to") == "review_queue"
102
+ and bool(out1.get("pid")), repr(out1))
103
+ out2 = pipe.execute(cg, dict(a))
104
+ _check("幂等去重联动", out2.get("dedup") is True
105
+ and out2.get("dup_of") == out1.get("pid"), repr(out2))
106
+ os.environ["MDCG_POLICY_FILE"] = policy
107
+
108
+ out = pipe.execute(cg, {"content_kind": "text",
109
+ "content": "PASSED 情境记忆", "gated": True,
110
+ "layer": "contextual"})
111
+ _check("gated闸代执行", "gate" in out, repr(out))
112
+
113
+ # ---------- 2 注册/移除拦截器不改核心文件 ----------
114
+ singleton = writepipe.default_pipeline()
115
+
116
+ def _block_skipme(ctx):
117
+ if "SKIPME" in (ctx["a"].get("content") or ""):
118
+ return {"ok": False, "committed": False,
119
+ "moved_to": "hook_blocked", "by": "test_block"}
120
+ return None
121
+
122
+ singleton.register_before("test_block", _block_skipme)
123
+ out = singleton.execute(cg, {"content_kind": "text",
124
+ "content": "PASSED SKIPME",
125
+ "layer": "knowledge"})
126
+ _check("注册即拦", out.get("moved_to") == "hook_blocked", repr(out))
127
+ singleton.unregister_before("test_block")
128
+ out = singleton.execute(cg, {"content_kind": "text",
129
+ "content": "PASSED SKIPME",
130
+ "layer": "knowledge"})
131
+ _check("移除即恢复(核心文件零改动)", out.get("committed") is True, repr(out))
132
+ # 默认 before 链含 linkref→deps→audit→consistency→gated(2026-09-19 deps 闸门新增)
133
+ _check("单例默认链", singleton.names()["before"]
134
+ == ["linkref", "deps", "audit", "consistency", "gated"],
135
+ repr(singleton.names()))
136
+ _check("单例默认after链", singleton.names()["after"]
137
+ == ["linkref", "trust"],
138
+ repr(singleton.names()))
139
+
140
+ # ---------- 3 链序 / 短路 / REWRITE ----------
141
+ p3 = writepipe.WritePipeline()
142
+ order = []
143
+ p3.register_before("g1", lambda ctx: order.append("g1") or None)
144
+ p3.register_before("g2", lambda ctx: order.append("g2") or None)
145
+ p3.register_before("g3", lambda ctx: order.append("g3")
146
+ or {"ok": False, "moved_to": "halt"})
147
+ cg3 = _mk_cg(tmp)
148
+ out = p3.execute(cg3, {"content_kind": "text", "content": "PASSED",
149
+ "layer": "knowledge"})
150
+ _check("链序按注册序", order == ["g1", "g2", "g3"], repr(order))
151
+ _check("终态短路不落盘", out.get("moved_to") == "halt"
152
+ and "id" not in out and "committed" not in out, repr(out))
153
+
154
+ p4 = writepipe.WritePipeline()
155
+ p4.register_before("rw", lambda ctx:
156
+ ctx["a"].__setitem__("content",
157
+ ctx["a"].get("content") + " REWRITTEN")
158
+ or None)
159
+ cg4 = _mk_cg(tmp)
160
+ out = p4.execute(cg4, {"content_kind": "text", "content": "PASSED",
161
+ "layer": "knowledge"})
162
+ _check("REWRITE改写生效", out.get("committed") is True
163
+ and bool(cg4.search("REWRITTEN")), repr(out))
164
+
165
+ # ---------- 4 权限结构上不可被拦截器绕过 ----------
166
+ cg_guest = _mk_cg(tmp, role="guest")
167
+ p5 = writepipe.WritePipeline()
168
+ p5.register_before("always_pass", lambda ctx: None)
169
+ try:
170
+ p5.execute(cg_guest, {"content_kind": "text", "content": "PASSED",
171
+ "consistency": False, "layer": "knowledge"})
172
+ _check("权限不可绕", False, "guest 写入未被拒")
173
+ except AccessDenied:
174
+ _check("权限不可绕", True)
175
+ except Exception as e: # noqa: BLE001
176
+ _check("权限不可绕", False,
177
+ "异常类型=%s:%s" % (type(e).__name__, e))
178
+
179
+ # ---------- 5 after 观测者 ----------
180
+ p6 = writepipe.WritePipeline()
181
+ seen = []
182
+ p6.register_after("spy", lambda ctx, out: seen.append(out.get("committed")))
183
+ cg6 = _mk_cg(tmp)
184
+ p6.execute(cg6, {"content_kind": "text", "content": "PASSED",
185
+ "layer": "knowledge"})
186
+ _check("after观测成功", seen == [True], repr(seen))
187
+
188
+ def _boom(ctx, out):
189
+ raise RuntimeError("after钩子故障")
190
+
191
+ p6.register_after("boom", _boom)
192
+ try:
193
+ p6.execute(cg6, {"content_kind": "text", "content": "PASSED 二次",
194
+ "layer": "knowledge"})
195
+ _check("after故障不吞", False, "异常被静默")
196
+ except RuntimeError:
197
+ _check("after故障不吞", True)
198
+
199
+ # ---------- 6 注册表卫生 ----------
200
+ p7 = writepipe.WritePipeline()
201
+ p7.register_before("x", lambda ctx: None)
202
+ p7.register_before("x", lambda ctx: None) # 同名幂等替换
203
+ _check("同名幂等注册", p7.names()["before"] == ["x"], repr(p7.names()))
204
+ try:
205
+ p7.register_before("y", 123)
206
+ _check("类型守卫", False, "非可调用未拒")
207
+ except TypeError:
208
+ _check("类型守卫", True)
209
+ _check("注销返回布尔", p7.unregister_before("x") is True
210
+ and p7.unregister_before("x") is False)
211
+
212
+
213
+ if __name__ == "__main__":
214
+ sys.exit(main())
package/md_cg/theory.py CHANGED
@@ -1,7 +1,7 @@
1
1
  # -*- coding: utf-8 -*-
2
2
  """md_cg · 版本层(蜂群互联 · 层0)
3
3
 
4
- 设计依据:`docs/蜂群互联_v0.1.md` §2.3 / §3.1 / §10。
4
+ 设计依据:`docs/swarm/蜂群互联_v0.1.md` §2.3 / §3.1 / §10。
5
5
 
6
6
  版本更新权 = 蜂群**唯一核心权限**(严格唯一,仅 designer)。本模块只做
7
7
  「声明 + 校验」两件事,不做跨节点证据交换(§9.1 属存在约束,留待 v0.3):
@@ -52,24 +52,29 @@ class TheoryError(Exception):
52
52
  # 存储
53
53
  # --------------------------------------------------------------------------
54
54
 
55
+ # 生效条件:path 为真值时返回 path,否则取 os.environ.get(THEORY_FILE_ENV),该值为空串/未设置时回落模块常量 DEFAULT_THEORY_FILE。
55
56
  def theory_file(path: str = None) -> str:
56
57
  return path or os.environ.get(THEORY_FILE_ENV) or DEFAULT_THEORY_FILE
57
58
 
58
59
 
60
+ # 生效条件:传入可被 json 序列化的 payload(dict)时返回 json.dumps(payload, sort_keys=True, ensure_ascii=False, separators=(",", ":"))。
59
61
  def _canon(payload: dict) -> str:
60
62
  """规范化 JSON:键排序 + 紧凑分隔符(哈希可复现的唯一前提)。"""
61
63
  return json.dumps(payload, sort_keys=True, ensure_ascii=False,
62
64
  separators=(",", ":"))
63
65
 
64
66
 
67
+ # 生效条件:payload 可被 _canon 序列化时返回 hashlib.sha256(_canon(payload).encode("utf-8")).hexdigest() 的十六进制摘要。
65
68
  def declaration_hash(payload: dict) -> str:
66
69
  return hashlib.sha256(_canon(payload).encode("utf-8")).hexdigest()
67
70
 
68
71
 
72
+ # 生效条件:对 SEALED_FIELDS 中每个 k 用 decl.get(k) 取值(缺键即 None),返回同集合长度的 dict。
69
73
  def _payload(decl: dict) -> dict:
70
74
  return {k: decl.get(k) for k in SEALED_FIELDS}
71
75
 
72
76
 
77
+ # 生效条件:以 dict(decl) 为副本 d,写入 d["declaration_hash"]=declaration_hash(_payload(d)),返回该副本(不改动入参 decl)。
73
78
  def seal(decl: dict) -> dict:
74
79
  """封缄:补上 declaration_hash。"""
75
80
  d = dict(decl)
@@ -77,6 +82,7 @@ def seal(decl: dict) -> dict:
77
82
  return d
78
83
 
79
84
 
85
+ # 生效条件:p=theory_file(path) 后,os.path.exists(p) 为假返回 None;open/json.load 抛 OSError 或 ValueError 也返回 None;否则 d 是 dict 时返回 d,不是 dict 返回 None。
80
86
  def load(path: str = None):
81
87
  p = theory_file(path)
82
88
  if not os.path.exists(p):
@@ -89,6 +95,7 @@ def load(path: str = None):
89
95
  return d if isinstance(d, dict) else None
90
96
 
91
97
 
98
+ # 生效条件:p=theory_file(path),先 os.makedirs(os.path.dirname(p) or "."),把 decl 以 ensure_ascii=False, indent=1 写入 p+".tmp" 再 os.replace 到 p,os.chmod(p, 0o600) 抛 OSError 时忽略,最终返回 p。
92
99
  def save(decl: dict, path: str = None) -> str:
93
100
  p = theory_file(path)
94
101
  os.makedirs(os.path.dirname(p) or ".", exist_ok=True)
@@ -107,6 +114,7 @@ def save(decl: dict, path: str = None) -> str:
107
114
  # 声明与校验
108
115
  # --------------------------------------------------------------------------
109
116
 
117
+ # 生效条件:v=(version or PROTOCOL_VERSION).strip(),v 为空串时抛 TheoryError;accepted 为假值(None/空)时 acc=[v],否则 list(accepted) 并剔除 strip 后为空的项,v 不在 acc 时 insert(0, v);declared_by 假值回落 "designer",declared_at=float(now if now is not None else time.time()),返回含 SCHEMA 的 dict。
110
118
  def make_declaration(version: str = None, accepted=None,
111
119
  declared_by: str = "designer", now: float = None) -> dict:
112
120
  """构造(未封缄的)版本声明。accepted 默认只含自身版本。"""
@@ -122,6 +130,7 @@ def make_declaration(version: str = None, accepted=None,
122
130
  "declared_at": float(now if now is not None else time.time())}
123
131
 
124
132
 
133
+ # 生效条件:d=decl if decl is not None else load(theory_file(path));d 为假时返回 theory_ok=True、auto=True、version=PROTOCOL_VERSION 的状态;d.get("schema")!=SCHEMA、hmac.compare_digest(str(d.get("declaration_hash") or ""), declaration_hash(_payload(d))) 不通过、v=d.get("theory_version") 为假、或 v 不在 acc=[str(x) for x in (d.get("accepted_versions") or [])] 时各自返回同一个 base 且 theory_ok=False,其余情况 base 更新为 theory_ok=True、reason="ok"。
125
134
  def check(path: str = None, decl: dict = None) -> dict:
126
135
  """校验版本声明 → 状态字典(永不抛异常,供启动路径安全调用)。
127
136
 
@@ -164,6 +173,7 @@ def check(path: str = None, decl: dict = None) -> dict:
164
173
  return base
165
174
 
166
175
 
176
+ # 生效条件:p=theory_file(path),仅当 load(p) 为 None 时尝试 declare(PROTOCOL_VERSION, actor="system:autoinit", path=p, append_audit=False) 并吞掉 TheoryError/OSError,最终返回 check(p)。
167
177
  def ensure(path: str = None) -> dict:
168
178
  """启动路径:无声明则落盘默认声明(声明常态化),再返回校验状态。
169
179
 
@@ -179,6 +189,7 @@ def ensure(path: str = None) -> dict:
179
189
  return check(p)
180
190
 
181
191
 
192
+ # 生效条件:version=None/空串时在 make_declaration 内回落 PROTOCOL_VERSION(仅回落结果 strip 后仍为空才抛 TheoryError),seal 后若 append_audit 为真值则把 (old or {}).get("audit") 或 [] 追加一条(仅 old 为真时追加)写入 decl["audit"],save(decl, path) 后返回 {'ok': True, 'declaration': decl, 'status': check(path)}。
182
193
  def declare(version: str = None, accepted=None, actor: str = "designer",
183
194
  path: str = None, append_audit: bool = True) -> dict:
184
195
  """写入版本声明(版本更新权的唯一物理落点)。"""
@@ -195,11 +206,13 @@ def declare(version: str = None, accepted=None, actor: str = "designer",
195
206
  return {"ok": True, "declaration": decl, "status": check(path)}
196
207
 
197
208
 
209
+ # 生效条件:以 path 调 load、check、theory_file,返回 {"declaration": load(path), "status": check(path), "theory_file": theory_file(path)}。
198
210
  def show(path: str = None) -> dict:
199
211
  return {"declaration": load(path), "status": check(path),
200
212
  "theory_file": theory_file(path)}
201
213
 
202
214
 
215
+ # 生效条件:返回含模块常量 SCHEMA、PROTOCOL_VERSION、list(SEALED_FIELDS)、list(ESCAPE_OPS) 及 theory_file() 与 THEORY_ENV/THEORY_FILE_ENV 的自描述 dict。
203
216
  def catalog() -> dict:
204
217
  """版本层自描述(供 whoami / service_info 暴露)。"""
205
218
  return {"schema": SCHEMA, "theory_file": theory_file(),
@@ -213,10 +226,12 @@ def catalog() -> dict:
213
226
  # CLI
214
227
  # --------------------------------------------------------------------------
215
228
 
229
+ # 生效条件:把 json.dumps(obj, ensure_ascii=False, indent=1)+"\n" 写入 sys.stdout,无返回值。
216
230
  def _print(obj):
217
231
  sys.stdout.write(json.dumps(obj, ensure_ascii=False, indent=1) + "\n")
218
232
 
219
233
 
234
+ # 生效条件:argv 交给 ap.parse_args(argv 为 None 时 argparse 读 sys.argv),子解析器 required=True 缺 cmd 时由 argparse 报错退出;a.cmd 为 declare/check/ensure/show/catalog 之一且未抛 TheoryError 时打印并返回 0,抛出 TheoryError 时写 stderr 返回 2。
220
235
  def main(argv=None):
221
236
  ap = argparse.ArgumentParser(
222
237
  prog="python -m md_cg.theory",
@@ -256,4 +271,4 @@ def main(argv=None):
256
271
 
257
272
 
258
273
  if __name__ == "__main__":
259
- sys.exit(main())
274
+ sys.exit(main())