@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,485 @@
1
+ # -*- coding: utf-8 -*-
2
+ """M4 落库治理自测(确定性部分):rule 驱动靶子 + 来源链 + 五纪律 + CLI。
3
+
4
+ 真源:docs/记忆评审系统_立项设计与施工交接_20260915.md §3 第 4 级 + §6 验收 + §7 负面清单。
5
+
6
+ 纪律(同 test_mr_m1/m2/m3):
7
+ · **自备数据源**——合成节点 + tempfile 合成库,零依赖真源库(外部 clone 全绿)。
8
+ · **零删除实锤**——全相位跑完节点集合只增不减(M4 只写 frontmatter 一个字段)。
9
+ · **不猜**——无来源记 `unfillable` + 原因分布;域外值**停止降级**;工作角色不被推断。
10
+ · **可回滚**——写入值即回滚锚点;当前值被人工改动 → `conflict` 不覆盖。
11
+
12
+ 覆盖:A 靶子定位(rule 驱动,引擎不写死) B 取值推导(只搬运已声明证据)
13
+ C 预演(零写入) D 执行(写前重查 / 留痕 / 幂等 / 零删除)
14
+ E 回滚(还原 / 删键 / 冲突) F CLI(fail-closed)
15
+ G 对照口径(复用 conformance,不复刻判据)
16
+ 运行:python -m md_cg.test_mr_m4
17
+ """
18
+ from __future__ import annotations
19
+
20
+ import contextlib
21
+ import hashlib
22
+ import io
23
+ import json
24
+ import os
25
+ import shutil
26
+ import sys
27
+ import tempfile
28
+
29
+ from . import conformance as CF
30
+ from . import crypto
31
+ from . import nodefile as NF
32
+ from .mdcos import MdCGOS
33
+ from .mreview import govern as GV
34
+
35
+ PASS = FAIL = SKIP = 0
36
+ FAILS = []
37
+
38
+
39
+ def ok(cond, label):
40
+ global PASS, FAIL
41
+ if cond:
42
+ PASS += 1
43
+ else:
44
+ FAIL += 1
45
+ FAILS.append(label)
46
+ print(" FAIL %s" % label)
47
+
48
+
49
+ def skip(label):
50
+ global SKIP
51
+ SKIP += 1
52
+ print(" SKIP %s" % label)
53
+
54
+
55
+ # ---------------------------- 合成库 ----------------------------
56
+
57
+ BODY = ("# 功能名:示例能力\n# 生效条件:载体/位置:本地仓;时间:全时窗(任意时刻成立);"
58
+ "方法:test;约束:无\n# 子功能:a/b\n# 执行:python -m md_cg.demo\n"
59
+ "# 验证方式:test\n# 不适用条件:无\n正文。")
60
+ LOCKED = crypto.ENC_PREFIX + "QUJDREVG" + crypto.ENC_SUFFIX
61
+
62
+
63
+ def mkroot(root, nodes):
64
+ """合成认知图根:`_index.json` + 节点盘文件(与 test_mr_m3 同范式)。"""
65
+ os.makedirs(root, exist_ok=True)
66
+ idx = {}
67
+ for nid, spec in nodes.items():
68
+ fm = dict(spec.get("fm") or {})
69
+ fm.setdefault("id", nid)
70
+ content = spec.get("content")
71
+ content = BODY if content is None else content
72
+ rel = spec.get("path") or ("%s/%s.md" % (spec.get("layer") or "knowledge", nid))
73
+ p = os.path.join(root, rel)
74
+ os.makedirs(os.path.dirname(p), exist_ok=True)
75
+ with open(p, "w", encoding="utf-8") as f:
76
+ f.write(NF.dumps(fm, content))
77
+ meta = dict(spec.get("meta") or {})
78
+ meta.update({"id": nid, "path": rel,
79
+ "layer": spec.get("layer") or "knowledge"})
80
+ if "role" in fm:
81
+ meta["role"] = fm["role"]
82
+ idx[nid] = meta
83
+ with open(os.path.join(root, CF.INDEX_FILE), "w", encoding="utf-8") as f:
84
+ json.dump({"nodes": idx}, f, ensure_ascii=False)
85
+ return root
86
+
87
+
88
+ def snapshot(root):
89
+ out = {}
90
+ for dp, _dns, fns in os.walk(root):
91
+ for fn in fns:
92
+ p = os.path.join(dp, fn)
93
+ with open(p, "rb") as f:
94
+ out[os.path.relpath(p, root)] = hashlib.md5(f.read()).hexdigest()
95
+ return out
96
+
97
+
98
+ def disk_role(root, nid, layer="knowledge"):
99
+ p = os.path.join(root, layer, "%s.md" % nid)
100
+ with open(p, encoding="utf-8") as f:
101
+ fm, _c = NF.loads(f.read())
102
+ return fm
103
+
104
+
105
+ def base_nodes():
106
+ """六类靶面各一条:成文标签 / writer / 无声明 / 已有值 / 域外 / 密文 + 域外层。"""
107
+ return {
108
+ "k1": {"fm": {"tags": ["role:knowledge", "主题"]}},
109
+ "k2": {"fm": {"writer": "dsh-memory"}},
110
+ "k3": {"fm": {"tags": ["无声明"]}},
111
+ "k4": {"fm": {"role": "user", "tags": ["role:knowledge"]}},
112
+ "k5": {"fm": {"tags": ["role:工具"]}},
113
+ "k6": {"content": LOCKED},
114
+ "c1": {"fm": {"tags": ["role:knowledge"]}, "layer": "contextual"},
115
+ }
116
+
117
+
118
+ def rm(path):
119
+ shutil.rmtree(path, ignore_errors=True)
120
+
121
+
122
+ # ---------------------------- A 组:靶子定位(rule 驱动)----------------------------
123
+
124
+ def phase_a(tmp):
125
+ print("[A] 靶子定位(规则库是靶子唯一真源,引擎不写死)")
126
+ rule = GV.role_rule()
127
+ ok(rule["rule_id"] == "R-ROLE-MISSING" and rule["field"] == "role",
128
+ "A1 从真规则库取到靶规则(field=%s)" % rule["field"])
129
+ ok(rule["layers"] == ["knowledge"], "A2 作用域取自 matcher.layer(%s)" % rule["layers"])
130
+ ok(rule["field"] == "role",
131
+ "A3 字段名取自 mechanical[%s].field(不写死)" % GV.CHECK_FIELD_ABSENT)
132
+
133
+ # 规则库可替换:换成临时规则库(field=其他字段)→ 引擎随之改靶,零代码改动
134
+ d = os.path.join(tmp, "rules")
135
+ os.makedirs(d, exist_ok=True)
136
+ with open(os.path.join(d, "x.json"), "w", encoding="utf-8") as f:
137
+ json.dump({"rules": [{"id": "R-ROLE-MISSING", "title": "改靶", "severity": "INFO",
138
+ "matcher": {"layer": ["contextual"]},
139
+ "mechanical": [{"check": "field_absent", "field": "owner"}]}]}, f)
140
+ r2 = GV.role_rule(rules_dir=d)
141
+ ok(r2["field"] == "owner" and r2["layers"] == ["contextual"],
142
+ "A4 改数据即改靶(field=%s layers=%s),引擎零改动" % (r2["field"], r2["layers"]))
143
+
144
+ for bad, label in (({"rules": []}, "A5 规则库无该规则 → ValueError(靶子失效必须红灯)"),
145
+ ({"rules": [{"id": "R-ROLE-MISSING", "severity": "INFO",
146
+ "mechanical": [{"check": "field_absent", "field": "role"}]}]},
147
+ "A6 规则缺 matcher.layer → ValueError"),
148
+ ({"rules": [{"id": "R-ROLE-MISSING", "severity": "INFO",
149
+ "matcher": {"layer": ["knowledge"]}, "mechanical": []}]},
150
+ "A7 规则缺 field_absent.field → ValueError")):
151
+ try:
152
+ GV.role_rule(rules=bad)
153
+ ok(False, label + "(未抛错)")
154
+ except ValueError:
155
+ ok(True, label)
156
+
157
+ root = mkroot(os.path.join(tmp, "a"), base_nodes())
158
+ try:
159
+ GV.role_plan(root, layer="contextual")
160
+ ok(False, "A8 跨层收窄应抛错(绝不跨层套用)")
161
+ except ValueError:
162
+ ok(True, "A8 跨层收窄抛错(绝不跨层套用)")
163
+ rep = GV.role_plan(root, layer="knowledge")
164
+ ok(rep["rule_layers"] == ["knowledge"] and rep["skipped_out_of_scope"] == 1,
165
+ "A9 层内收窄不抛错,域外条数如实上报(%d)" % rep["skipped_out_of_scope"])
166
+
167
+
168
+ # ---------------------------- B 组:取值推导 ----------------------------
169
+
170
+ def phase_b(tmp):
171
+ print("[B] 取值推导(只搬运已声明证据,绝不编造)")
172
+ root = mkroot(os.path.join(tmp, "b"), base_nodes())
173
+
174
+ # 默认档(tags + map),map 需显式补表(WRITER_ROLE_MAP 故意为空)
175
+ p = GV.role_plan(root)
176
+ ok(GV.SOURCE_LAYER not in p["sources"],
177
+ "B1 默认档不含 layer_default(%s)——不得用常量填满指标" % (p["sources"],))
178
+ ok(p["by_source"] == {"tags": 1}, "B2 默认档只认成文标签(%s)" % p["by_source"])
179
+ ok(p["unfillable_by_reason"].get(GV.REASON_NO_SOURCE) == 1,
180
+ "B3 无任何声明 → no_source_declared(不猜)")
181
+ ok(p["unfillable_by_reason"].get(GV.REASON_WRITER_NO_MAP + ":dsh-memory") == 1,
182
+ "B4 writer 有值但映射表空 → writer_no_mapping:<writer>(待终裁补表)")
183
+ ok(p["unfillable_by_reason"].get(GV.REASON_TAG_OOD + ":工具") == 1,
184
+ "B5 域外标签值如实上报(%s)" % p["unfillable_by_reason"])
185
+ ok(p["skipped_present"] == 1, "B6 已有 role 值者不重复回填")
186
+ ok(p["skipped_locked"] == 1, "B7 密文节点跳过(绝不解密回写)")
187
+
188
+ # ① tags 成文声明优先且直取
189
+ p2 = GV.role_plan(root, ids=["k1"], sources=("tags", "map", "layer_default"))
190
+ it = p2["items"][0]
191
+ ok(it["role"] == "knowledge" and it["source"] == "tags",
192
+ "B8 tags 声明直取(%s / %s)" % (it["role"], it["source"]))
193
+ ok(it["basis"] == GV.BASIS_TAG, "B9 依据落在 basis(可审计,%s)" % it["basis"])
194
+
195
+ # ② map 档:显式补表才生效
196
+ p3 = GV.role_plan(root, role_map={"dsh-memory": "knowledge"})
197
+ ok(p3["by_source"] == {"tags": 1, "map": 1},
198
+ "B10 映射表补声明后 map 档生效(%s)" % p3["by_source"])
199
+ ok(p3["unfillable_by_reason"].get(GV.REASON_WRITER_NO_MAP + ":dsh-memory") is None,
200
+ "B11 补表后原 writer_no_mapping 消失(原因分布随声明变化)")
201
+
202
+ # ② OOD:映射表写域外值 → 停止降级
203
+ p4 = GV.role_plan(root, role_map={"dsh-memory": "不存在的角色"})
204
+ ok(p4["unfillable_by_reason"].get(GV.REASON_MAP_OOD + ":不存在的角色") == 1,
205
+ "B13 映射表域外值 → map_role_out_of_domain(%s)" % p4["unfillable_by_reason"])
206
+
207
+ # ② 工作角色:映射表写 tool-output → 拒绝(不许把知识节点摘出默认召回)
208
+ p5 = GV.role_plan(root, role_map={"dsh-memory": "tool-output"})
209
+ ok(p5["unfillable_by_reason"].get(GV.REASON_MAP_WORK + ":tool-output") == 1,
210
+ "B14 映射表推成工作角色 → 拒绝(%s)" % p5["unfillable_by_reason"])
211
+ ok(p5["targeted"] == 1 and p5["by_source"] == {"tags": 1},
212
+ "B15 工作角色被拒后**不**回退到其它档(拒绝即终态)")
213
+
214
+ # ③ layer_default:显式开启才可用
215
+ p6 = GV.role_plan(root, sources=("tags", "map", "layer_default"))
216
+ ok(p6["by_source"].get("layer_default") == 2,
217
+ "B16 layer_default 档显式开启后生效(%s)" % p6["by_source"])
218
+ ok(p6["targeted"] > p["targeted"], "B17 开启后靶面扩大(%d → %d)"
219
+ % (p["targeted"], p6["targeted"]))
220
+
221
+ # tags 域外值不因开启更多档而降级
222
+ p7 = GV.role_plan(root, ids=["k5"], sources=("tags", "map", "layer_default"))
223
+ ok(p7["targeted"] == 0 and p7["unfillable_by_reason"].get(GV.REASON_TAG_OOD + ":工具") == 1,
224
+ "B18 标签域外值在最强档下仍不降级(显式声明 ≠ 无声明)")
225
+
226
+
227
+ # ---------------------------- C 组:预演(零写入)----------------------------
228
+
229
+ def phase_c(tmp):
230
+ print("[C] 预演(零写入)")
231
+ root = mkroot(os.path.join(tmp, "c"), base_nodes())
232
+ GV.role_plan(root) # 预热(首建实例可能补索引)
233
+ before = snapshot(root)
234
+ GV.role_plan(root, sources=GV.ALL_SOURCES)
235
+ GV.role_stats(root)
236
+ GV.history(root)
237
+ ok(snapshot(root) == before, "C1 预演 / 统计 / 留痕读全程零写入(指纹不变)")
238
+
239
+ p = GV.role_plan(root, sources=GV.ALL_SOURCES)
240
+ ok(p["dry_run"] is True and p["action"] == GV.ACTION_ROLE, "C2 预演自报只读")
241
+ ok(p["nodes_scanned"] == 6 and p["skipped_out_of_scope"] == 1,
242
+ "C3 扫描面=层内 6 / 域外 1(%d/%d)" % (p["nodes_scanned"], p["skipped_out_of_scope"]))
243
+ ok(p["targeted"] == 3, "C4 最强档靶面 3 条(%d)" % p["targeted"])
244
+
245
+ p2 = GV.role_plan(root, sources=GV.ALL_SOURCES, limit=2)
246
+ ok(len(p2["items"]) == 2 and p2["targeted"] == 3,
247
+ "C5 limit 只截清单不动靶面(items=%d targeted=%d)" % (len(p2["items"]), p2["targeted"]))
248
+
249
+ p3 = GV.role_plan(root, ids=["k3"])
250
+ ok(p3["nodes_scanned"] == 1 and p3["skipped_out_of_scope"] == 0,
251
+ "C6 ids 收窄同时收窄扫描面与域外计数")
252
+ p4 = GV.role_plan(root, prefix="k1")
253
+ ok(p4["nodes_scanned"] == 1, "C7 prefix 收窄(%d)" % p4["nodes_scanned"])
254
+ p5 = GV.role_plan(root, sources=GV.ALL_SOURCES, sample=2)
255
+ ok(len(p5.get("sample") or []) == 2, "C8 sample 抽样(%s)" % p5.get("sample"))
256
+ ok(p5["planned_ids"] == [i["id"] for i in p5["items"]],
257
+ "C9 planned_ids 与清单同源(%s)" % p5["planned_ids"])
258
+
259
+
260
+ # ---------------------------- D 组:执行 ----------------------------
261
+
262
+ def write_node(root, nid, fm, layer="knowledge", content=None):
263
+ p = os.path.join(root, layer, "%s.md" % nid)
264
+ with open(p, encoding="utf-8") as f:
265
+ _fm, c = NF.loads(f.read())
266
+ with open(p, "w", encoding="utf-8") as f:
267
+ f.write(NF.dumps(fm, c if content is None else content))
268
+
269
+
270
+ def idx_role(root, nid):
271
+ with open(os.path.join(root, CF.INDEX_FILE), encoding="utf-8") as f:
272
+ return (json.load(f)["nodes"].get(nid) or {}).get("role")
273
+
274
+
275
+ def phase_d(tmp):
276
+ print("[D] 执行(写前重查 / 留痕 / 幂等 / 零删除)")
277
+ root = mkroot(os.path.join(tmp, "d"), base_nodes())
278
+ files0 = set(snapshot(root))
279
+ c1_0 = snapshot(root).get("contextual/c1.md")
280
+
281
+ rep = GV.role_apply(root, role_map={"dsh-memory": "knowledge"},
282
+ batch="b1", actor="tester")
283
+ ok(rep["written"] == 2 and rep["by_source"] == {"tags": 1, "map": 1},
284
+ "D1 写入 2 条(%s)" % rep["by_source"])
285
+ ok(disk_role(root, "k1")["role"] == "knowledge", "D2 tags 档写盘生效")
286
+ ok(disk_role(root, "k2")["role"] == "knowledge", "D3 map 档写盘生效")
287
+ ok("role" not in disk_role(root, "k3"), "D4 无来源者不写(宁可缺失不猜)")
288
+ ok("role" not in disk_role(root, "k6"), "D5 密文节点不写")
289
+ ok(idx_role(root, "k1") == "knowledge",
290
+ "D6 role 进索引(覆盖率判据据此可算)")
291
+ ok(idx_role(root, "k3") is None, "D7 未回填者索引也无值(盘/索引一致)")
292
+
293
+ h = GV.history(root, action=GV.ACTION_ROLE)
294
+ rec = [r for r in h["records"] if r["node"] == "k1"][0]
295
+ ok(all(k in rec for k in ("batch", "actor", "write_id", "entry_id", "value",
296
+ "source", "basis", "fm_before", "layer", "rule_id")),
297
+ "D8 留痕字段完整(回滚凭据齐备)")
298
+ ok(rec["fm_before"] == {"role": None, "had_key": False},
299
+ "D9 留痕记下写前态(原本无键)")
300
+ ok(bool(rec["write_id"]) and len(rec["entry_id"]) == 12,
301
+ "D10 write_id / entry_id 齐备(回滚去重与对账凭据)")
302
+
303
+ rep2 = GV.role_apply(root, role_map={"dsh-memory": "knowledge"}, batch="b1")
304
+ ok(rep2["written"] == 0 and rep2["skipped_drift"] == 0,
305
+ "D11 幂等:重跑零写入(已有值在预演面即被 present 拦截)")
306
+
307
+ orig = GV.role_plan
308
+
309
+ def patched(x, **kw):
310
+ r = orig(x, **kw)
311
+ for it in r["items"]: # 预演→执行之间模拟他人改动
312
+ if it["id"] == "k3":
313
+ fm = disk_role(root, "k3")
314
+ fm["role"] = "assistant"
315
+ write_node(root, "k3", fm)
316
+ return r
317
+
318
+ GV.role_plan = patched
319
+ try:
320
+ rep3 = GV.role_apply(root, ids=["k3"], sources=GV.ALL_SOURCES, batch="b2")
321
+ finally:
322
+ GV.role_plan = orig
323
+ ok(rep3["written"] == 0 and rep3["skipped_drift"] == 1,
324
+ "D12 写前重查:预演后被人改动 → 不覆盖(written=%d drift=%d)"
325
+ % (rep3["written"], rep3["skipped_drift"]))
326
+ ok(disk_role(root, "k3")["role"] == "assistant", "D13 漂移节点保持他人值")
327
+
328
+ ok(set(snapshot(root)) >= files0 and snapshot(root).get("contextual/c1.md") == c1_0,
329
+ "D14 零删除:文件集合只增不减、域外节点零触碰")
330
+ with open(os.path.join(root, CF.INDEX_FILE), encoding="utf-8") as f:
331
+ ok(len(json.load(f)["nodes"]) == 7, "D15 治理后节点总数不变(7)")
332
+
333
+ r2 = mkroot(os.path.join(tmp, "d2"), {
334
+ "a1": {"fm": {"tags": ["role:user"]}},
335
+ "a2": {"fm": {"tags": ["role:knowledge"]}},
336
+ "a3": {"fm": {"tags": ["role:developer"]}}})
337
+ p = GV.role_plan(r2)
338
+ eid = [i for i in p["items"] if i["id"] == "a2"][0]["entry_id"]
339
+ rep4 = GV.role_apply(r2, entry_ids=[eid], batch="b3")
340
+ ok(rep4["written"] == 1 and disk_role(r2, "a2")["role"] == "knowledge",
341
+ "D16 entry_ids 工单式精确执行(只写 1 条)")
342
+ ok("role" not in disk_role(r2, "a1") and "role" not in disk_role(r2, "a3"),
343
+ "D17 工单外节点零触碰")
344
+ ok(rep4["plan_remaining"] == 2, "D18 余量如实上报(%d)" % rep4["plan_remaining"])
345
+ ok(rep4["entry_ids"] == [eid], "D19 留痕回执带 entry_id(可对账)")
346
+
347
+
348
+ # ---------------------------- E 组:回滚 ----------------------------
349
+
350
+ def phase_e(tmp):
351
+ print("[E] 回滚(还原 / 删键 / 冲突)")
352
+ root = mkroot(os.path.join(tmp, "e"), {
353
+ "a1": {"fm": {"tags": ["role:user"]}},
354
+ "a2": {"fm": {"tags": ["role:knowledge"], "role": ""}},
355
+ "a3": {"fm": {"tags": ["role:developer"]}}})
356
+ had = disk_role(root, "a2")
357
+ ok("role" in had and GV._blank(had.get("role")),
358
+ "E1 空串 role:键在值空 → 视为缺失(键存在性也要还原,%r)" % had.get("role"))
359
+ p0 = GV.role_plan(root)
360
+ ok(p0["targeted"] == 3 and p0["skipped_present"] == 0,
361
+ "E2 空串节点仍入靶面(targeted=%d)" % p0["targeted"])
362
+ base = GV.role_stats(root)["role_ratio_kn"]
363
+
364
+ rep = GV.role_apply(root, batch="e1", actor="t")
365
+ ok(rep["written"] == 3, "E3 三条写入(%d)" % rep["written"])
366
+ ratios_after = GV.role_stats(root)["role_ratio_kn"]
367
+ ok(ratios_after > base, "E4 覆盖率上升(%.4f → %.4f)" % (base, ratios_after))
368
+ ok(GV.role_stats(root)["by_value"] == {"user": 1, "knowledge": 1, "developer": 1},
369
+ "E5 逐值分布正确(%s)" % GV.role_stats(root)["by_value"])
370
+
371
+ fm = disk_role(root, "a1") # 人工覆盖 → 回滚必须让路
372
+ fm["role"] = "assistant"
373
+ write_node(root, "a1", fm)
374
+ rb = GV.role_rollback(root, batch="e1", actor="t")
375
+ ok(rb["reverted"] == 2 and rb["conflict"] == 1,
376
+ "E6 人工改动者计 conflict 不覆盖(reverted=%d conflict=%d)"
377
+ % (rb["reverted"], rb["conflict"]))
378
+ ok(disk_role(root, "a1")["role"] == "assistant", "E7 冲突节点保持人工值")
379
+ ok("role" not in disk_role(root, "a3"), "E8 原本无键 → 删键还原(不写空串)")
380
+ ok("role" in disk_role(root, "a2") and GV._blank(disk_role(root, "a2").get("role")),
381
+ "E9 原本空值 → 还原空值且键仍在(%r)" % disk_role(root, "a2").get("role"))
382
+ ok(idx_role(root, "a3") is None, "E10 索引随回滚同步(role 清空)")
383
+
384
+ rb2 = GV.role_rollback(root, batch="e1", actor="t")
385
+ ok(rb2["reverted"] == 0 and rb2["skipped_done"] == 2 and rb2["conflict"] == 1,
386
+ "E11 重复回滚凭 write_id 去重(skipped_done=%d)" % rb2["skipped_done"])
387
+ ok(GV.role_stats(root)["by_value"] == {"assistant": 1},
388
+ "E12 回滚后仅剩人工值(%s)" % GV.role_stats(root)["by_value"])
389
+
390
+
391
+ # ---------------------------- F 组:CLI(fail-closed)----------------------------
392
+
393
+ def run_cli(argv):
394
+ out, err = io.StringIO(), io.StringIO()
395
+ with contextlib.redirect_stdout(out), contextlib.redirect_stderr(err):
396
+ rc = GV.main(argv)
397
+ return rc, out.getvalue(), err.getvalue()
398
+
399
+
400
+ def phase_f(tmp):
401
+ print("[F] CLI(fail-closed)")
402
+ root = mkroot(os.path.join(tmp, "f"), base_nodes())
403
+ env_bak = os.environ.pop("MDCG_ROOT", None)
404
+ try:
405
+ ok(run_cli(["plan", "--root", root])[0] == 0, "F1 plan 退出码 0")
406
+ ok(run_cli(["plan"])[0] == 2, "F2 无 root → 2(fail-closed)")
407
+ ok(run_cli([])[0] == 2, "F3 无子命令 → 2(print_help)")
408
+ ok(run_cli(["apply", "--root", root])[0] == 2,
409
+ "F4 apply 无 --yes → 2(写盘须显式确认)")
410
+ ok(run_cli(["rollback", "--root", root])[0] == 2,
411
+ "F5 rollback 无范围 → 2(防全量误回滚)")
412
+ ok(run_cli(["plan", "--root", root, "--sources", "tags,bogus"])[0] == 2,
413
+ "F6 来源档域外 → 2(不静默降级)")
414
+ ok(run_cli(["plan", "--root", root, "--layer", "contextual"])[0] == 2,
415
+ "F7 跨层收窄 → 2(异常不得裸奔)")
416
+
417
+ rc, out, err = run_cli(["apply", "--root", root, "--yes", "--batch", "cli1",
418
+ "--sources", "tags,map,layer_default"])
419
+ ok(rc == 0 and GV.LAYER_DEFAULT_WARNING[:14] in err,
420
+ "F8 layer_default 开启时 stderr 透出终裁提示")
421
+ ok(disk_role(root, "k3")["role"] == "knowledge", "F9 CLI apply 生效")
422
+ ok(GV.history(root, batch="cli1")["total"] == 3,
423
+ "F10 CLI 留痕按批次可查(%d)" % GV.history(root, batch="cli1")["total"])
424
+
425
+ rc, out, _e = run_cli(["plan", "--root", root, "--ids", "k1", "--json"])
426
+ ok(rc == 0 and json.loads(out)["nodes_scanned"] == 1,
427
+ "F11 plan --json 可机读(M4 报告引用面)")
428
+ rc, out, _e = run_cli(["history", "--root", root, "--json"])
429
+ ok(rc == 0 and json.loads(out)["total"] >= 3, "F12 history --json 可机读")
430
+ rc, out, _e = run_cli(["rollback", "--root", root, "--batch", "cli1"])
431
+ ok(rc == 0 and "还原" in out, "F13 CLI rollback 人读摘要可读(%s)" % out.split("\n")[0])
432
+ ok("role" not in disk_role(root, "k3"), "F14 CLI 回滚生效(键被删除)")
433
+ finally:
434
+ if env_bak is not None:
435
+ os.environ["MDCG_ROOT"] = env_bak
436
+
437
+
438
+ # ---------------------------- G 组:对照口径 ----------------------------
439
+
440
+ def phase_g(tmp):
441
+ print("[G] 对照口径(复用 conformance,不复刻判据)")
442
+ root = mkroot(os.path.join(tmp, "g"), base_nodes())
443
+ st = GV.role_stats(root)
444
+ ok(st["threshold"] == float(CF.THRESHOLDS["role_coverage_min"]),
445
+ "G1 阈值直接取自 conformance(%s)" % st["threshold"])
446
+ mine = CF._coverage_metrics(CF.load_index(root)).get("role_ratio_kn")
447
+ ok(st["role_ratio_kn"] == mine, "G2 覆盖率与 conformance 逐位一致(%s)" % mine)
448
+ ok(st["target_met"] is False and st["gap_to_target"] > 0,
449
+ "G3 未达标时如实上报缺口(%.4f)" % st["gap_to_target"])
450
+
451
+ GV.role_apply(root, role_map={"dsh-memory": "knowledge"}, batch="g1")
452
+ st2 = GV.role_stats(root)
453
+ ok(st2["role_ratio_kn"] > st["role_ratio_kn"],
454
+ "G4 治理后覆盖率变化可量化(%.4f → %.4f)"
455
+ % (st["role_ratio_kn"], st2["role_ratio_kn"]))
456
+ ok(st2["by_layer"].get("knowledge") == 6,
457
+ "G5 逐层分布(knowledge=%s)" % st2["by_layer"].get("knowledge"))
458
+
459
+ try:
460
+ GV.run(root, "role_bogus")
461
+ ok(False, "G6 未知 action → ValueError")
462
+ except ValueError:
463
+ ok(True, "G6 未知 action → ValueError(入口防呆)")
464
+ ok(GV.run(root, "role", apply=False)["dry_run"] is True,
465
+ "G7 run(apply=False) 走预演(默认不写)")
466
+ ok(GV.run(root, "role_stats")["action"] == "role_stats", "G8 run 分发到对照")
467
+
468
+
469
+ # ---------------------------- 入口 ----------------------------
470
+
471
+ def main():
472
+ tmp = tempfile.mkdtemp(prefix="m4_govern_")
473
+ try:
474
+ for fn in (phase_a, phase_b, phase_c, phase_d, phase_e, phase_f, phase_g):
475
+ fn(tmp)
476
+ finally:
477
+ rm(tmp)
478
+ print("\nPASS %d FAIL %d SKIP %d" % (PASS, FAIL, SKIP))
479
+ for f in FAILS:
480
+ print(" - %s" % f)
481
+ return 1 if FAIL else 0
482
+
483
+
484
+ if __name__ == "__main__":
485
+ sys.exit(main())
package/md_cg/test_p1.py CHANGED
@@ -1,7 +1,7 @@
1
1
  # -*- coding: utf-8 -*-
2
2
  """md 认知图 P1 验收 · 白箱认知架构 9 维度补全
3
3
 
4
- 覆盖(对照 docs/认知图_MD目录方案_v0.1.md v0.3 待办 + 白箱 5 篇):
4
+ 覆盖(对照 docs/mdcg/认知图_MD目录方案_v0.1.md v0.3 待办 + 白箱 5 篇):
5
5
  1. MARKS 5 要素 + 验证基底 + 不适用条件
6
6
  2. 负记忆目录(rejected/unresolved)
7
7
  3. 资格判定四态(ACCEPT/REJECT/DEFER/BLINDSPOT)
@@ -2,7 +2,7 @@
2
2
  """md_cg · 第 11 篇:节点间自动冲突检测(三级决策:情绪 → 反思 → 递归反思)
3
3
 
4
4
  理论出处(本仓原文):
5
- · 情绪 = 信息差二阶变化 d²D/dt²(`docs/智能的公理化基石.md` §十一)
5
+ · 情绪 = 信息差二阶变化 d²D/dt²(`docs/theory/智能的公理化基石.md` §十一)
6
6
  —— L0,独立通道,**不参与信任计算**
7
7
  · 反题 = 预测与事实冲突(同文档 :529,条件论七操作)—— L1 条件级冲突检测
8
8
  · 递归受深度/节点数/循环/信息增益门槛约束(同文档 :273)—— L2 递归反思
@@ -2,12 +2,12 @@
2
2
  """md_cg · 第 12 篇:独立元认知(观察自身认知的二阶单元)
3
3
 
4
4
  理论出处(本仓原文):
5
- · 情绪 = 信息差二阶变化 d²D/dt²(`docs/智能的公理化基石.md` §十一,:412-510)
5
+ · 情绪 = 信息差二阶变化 d²D/dt²(`docs/theory/智能的公理化基石.md` §十一,:412-510)
6
6
  · 情感 = 信任二阶变化 d²T/dt²(同文档 :469-508)
7
7
  · 五大单元:元认知是反思/验证通道的**外部观察者**(同文档 :540-556)
8
8
  · 推论三「局部不可知」:盲区即知识(同文档 :277-288)
9
9
  · 信任 P_trust / P_gap(同文档 :393-408)
10
- · 导航税:显式状态记录(`docs/智能的认知过程.md`:84-86)
10
+ · 导航税:显式状态记录(`docs/theory/智能的认知过程.md`:84-86)
11
11
 
12
12
  覆盖:
13
13
  A 轨迹:D(t) / dD/dt / d²D/dt² / 情绪 / 四态分布
@@ -2,7 +2,7 @@
2
2
  """md_cg · 第 16 篇:自我状态层(薄自我 + 富索引)
3
3
 
4
4
  理论出处(本仓原文):
5
- · `docs/灵枢_自我层定义.md:6`:SELF 层 = 身份 + 价值观 + 图接口,
5
+ · `docs/mdcg/灵枢_自我层定义.md:6`:SELF 层 = 身份 + 价值观 + 图接口,
6
6
  不可遗忘、跨会话自动加载,且**是薄的**(不该变成事件仓库)。
7
7
  · 智能论 §十一:情绪 = 信息差二阶 d^2D/dt^2;§十:P_gap / P_trust;
8
8
  情感 = 信任二阶 d^2T/dt^2。
@@ -1,7 +1,7 @@
1
1
  # -*- coding: utf-8 -*-
2
2
  """代码索引 + 回读端到端测试(P26 · 认知图的代码面)。
3
3
 
4
- R1 改造的验收(对照 docs/认知图_索引与工程规范化_计划_v0.1.md):
4
+ R1 改造的验收(对照 docs/mdcg/认知图_索引与工程规范化_计划_v0.1.md):
5
5
 
6
6
  ① 渲染即 CCG:`codeindex.render` 必须产出 CCG 6 行。否则
7
7
  `judge_qualification` 第一步的 ccg_completeness 会直接判 BLINDSPOT ——
@@ -208,30 +208,56 @@ def main():
208
208
  ignite and ignite["parent"] == "Reactor", str(ignite and ignite["parent"]))
209
209
 
210
210
  # ===================================================== ④ 渲染即 CCG
211
- print("\n【4】render 产出 CCG(R1 核心修复)")
211
+ print("\n【4】render 产出 CCG(R1 核心修复 + Phase 0 契约裁决)")
212
212
  rendered = codeindex.render(py_item)
213
213
  cpl = nodefile.ccg_completeness(rendered)
214
- check("render 的 CCG 5 要素齐全(complete=True)",
215
- cpl["complete"] is True, str(cpl["required_present"]))
216
- check("render 6 行全在(all_present=True)", cpl["all_present"] is True)
217
- check("render 不复制实现正文",
218
- "return mass * speed" not in rendered)
219
-
220
- # 生效条件**必须**与 frontmatter 同源:四槽合成,单槽不是生效条件。
221
- # 改造前正文写「大域=X;检索…时」(第三种方言),frontmatter 只写单槽
222
- # observation_position → condition_space_text(require_full=True) 恒为 ""。
214
+ check("render 不复制实现正文", "return mass * speed" not in rendered)
215
+ # Phase 0 契约裁决(docs/mdcg/代码评审与条件化注释_契约_v0.1.md):
216
+ # 生效条件只承载**功能前置条件**,不由索引元条件合成。ALPHA 的
217
+ # compute_energy 没有源码 CCG 注释 → 诚实缺该要素,而非被元条件冒充。
218
+ missing = [m for m in nodefile.CCG_REQUIRED
219
+ if m not in cpl["required_present"]]
220
+ check("合成区不冒充生效条件(源码未声明 → 无该行)",
221
+ "# 生效条件:" not in rendered, rendered.split("\n")[0][:60])
222
+ check("缺生效条件被诚实判定(complete=False 且恰缺该要素)",
223
+ cpl["complete"] is False and missing == ["生效条件"], str(missing))
224
+ check("合成区其余 5 要素齐备(不因缺条件整体失效)",
225
+ all(m in cpl["required_present"] for m in nodefile.CCG_MARKS
226
+ if m != "生效条件"), str(cpl["required_present"]))
227
+
228
+ # 条件空间**必须**与 frontmatter 同源:四槽齐备,单槽不是条件空间。
223
229
  cs = codeindex.condition_space(py_item)
224
230
  check("condition_space 四槽齐备",
225
231
  set(cs) == set(nodefile.CONDITION_SLOTS_REQUIRED), str(sorted(cs)))
226
232
  synth = nodefile.condition_space_text(cs)
227
- check("四槽合成出非空生效条件(单槽冒充已废止)", bool(synth), synth)
228
- cond_line = next((ln for ln in rendered.split("\n")
229
- if ln.startswith("# 生效条件:")), "")
230
- check("正文生效条件 = 四槽合成结果(正文与 frontmatter 同源)",
231
- cond_line == f"# 生效条件:{synth}", cond_line)
233
+ check("四槽合成出非空条件声明(单槽冒充已废止)", bool(synth), synth)
234
+ # 索引元条件**独立承载**且与 CCG 六要素零重名(同源同函数)
235
+ meta_head = f"# {nodefile.INDEX_META_MARK}:"
236
+ meta_line = next((ln for ln in rendered.split("\n")
237
+ if ln.startswith(meta_head)), "")
238
+ check("索引元条件独立成行且非 CCG 字段名",
239
+ bool(meta_line) and not nodefile.is_ccg_mark(nodefile.INDEX_META_MARK),
240
+ meta_line[:70])
241
+ check("索引元条件 = 四槽同源(与 frontmatter 同一纯函数)",
242
+ meta_line == meta_head + nodefile.condition_space_text(
243
+ cs, require_full=False), meta_line[:90])
232
244
  check("时间槽用全时窗哨兵(不把写入时刻伪造成条件)",
233
245
  nodefile.is_full_time_window(cs.get("time_window")),
234
246
  str(cs.get("time_window")))
247
+ # 人工优先:源码声明生效条件时必须置首且胜出(行序压制的反面自证)
248
+ annotated = dict(py_item)
249
+ annotated["comments"] = ["# 生效条件:mass > 0 且 speed >= 0"]
250
+ r2_lines = codeindex.render(annotated).split("\n")
251
+ first_cond = next((ln for ln in r2_lines
252
+ if ln.startswith("# 生效条件:")), "")
253
+ check("源码声明的生效条件置首且逐字保留(未被压制)",
254
+ first_cond == "# 生效条件:mass > 0 且 speed >= 0", first_cond)
255
+ # 合成行按条目实际 kind 构造,避免把 kind 字面量写死在断言里(防漂移)
256
+ synth_head = f"# 功能名:{py_item['name']}({py_item['kind']})"
257
+ check("源码区先于合成区(人工优先的确定性序)",
258
+ synth_head in r2_lines
259
+ and r2_lines.index(first_cond) < r2_lines.index(synth_head),
260
+ str(r2_lines[:3]))
235
261
  weak = codeindex.condition_space(js_item)
236
262
  check("弱提取在方法槽诚实降级(不冒充编译器)",
237
263
  codeindex.LANG_WEAK in weak["observation_tool"]
@@ -239,22 +265,25 @@ def main():
239
265
  weak["observation_tool"])
240
266
 
241
267
  # ===================================================== ⑤ 检索 + 资格
242
- print("\n【5】检索资格判定(旧行为:代码节点恒定 BLINDSPOT)")
268
+ print("\n【5】检索资格判定(Phase 0:合成区不冒充生效条件 → 诚实 BLINDSPOT)")
243
269
  cg.flush()
244
270
  py_nid = codeindex.node_id(py_item)
245
271
  read = call_tool(cg, "cg", {"op": "read", "query": "compute_energy", "k": 10})
246
272
  hits = [r for r in read.get("results", []) if r["node"]["id"] == py_nid]
247
273
  check("代码节点可被检索到(存得进→查得到)", bool(hits),
248
274
  f"hits={len(hits)}")
249
- check("CCG 完整 + 基底已声明 → DEFER(不再恒定 BLINDSPOT,情境未确认条件不冒充接受)",
250
- bool(hits) and hits[0]["state"] == "DEFER",
275
+ # Phase 0 契约裁决的**代价面**(预期变更,非回归):源码未声明功能
276
+ # 生效条件前,代码条目缺证据 → BLINDSPOT。改造前它靠「索引元条件占用
277
+ # 生效条件字段」取得 DEFER——而那正是「补了注释与没补不可区分」的病根。
278
+ check("源码未声明生效条件 → BLINDSPOT(不再用索引元条件冒充 DEFER)",
279
+ bool(hits) and hits[0]["state"] == "BLINDSPOT",
251
280
  str(hits[0]["state"] if hits else None))
252
281
 
253
282
  js_nid = codeindex.node_id(js_item)
254
283
  read_js = call_tool(cg, "cg", {"op": "read", "query": "areaOfCircle", "k": 10})
255
284
  js_hits = [r for r in read_js.get("results", []) if r["node"]["id"] == js_nid]
256
- check("弱提取节点同样可判(state=DEFER,非 BLINDSPOT)",
257
- bool(js_hits) and js_hits[0]["state"] == "DEFER",
285
+ check("弱提取节点同样诚实缺证据(state=BLINDSPOT)",
286
+ bool(js_hits) and js_hits[0]["state"] == "BLINDSPOT",
258
287
  str(js_hits[0]["state"] if js_hits else None))
259
288
 
260
289
  # ===================================================== ⑥ code_ref