@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
package/md_cg/docindex.py CHANGED
@@ -58,6 +58,7 @@ DEFAULT_SENSITIVITY = "internal"
58
58
  PRIVATE_HINTS = ("private", "secret", "internal", "未公开", "私有", "内部")
59
59
 
60
60
 
61
+ # 生效条件:override 为真值即返回 (override, "调用方显式指定");否则 path(假值按 "")按 "\" 与 "/" 分段,任一段小写含 PRIVATE_HINTS 中任一提示即返回 ("private", 路径段命中理由),全部不命中返回 (DEFAULT_SENSITIVITY, 默认密级理由);
61
62
  def sensitivity_for(path, override=None):
62
63
  """返回 (密级, 依据)。override 优先;否则按路径段保守降级。
63
64
 
@@ -81,6 +82,7 @@ _FENCE = re.compile(r"^\s*(```+|~~~+)")
81
82
  _ATX = re.compile(r"^(#{1,6})\s+(.+?)\s*#*\s*$")
82
83
 
83
84
 
85
+ # 生效条件:lines 非空且 lines[0].strip() == "---" 时,从下标 1 起找到首个 strip() == "---" 的行并返回其后一行下标 i+1;lines 为空、首行不是 "---" 或找不到闭合 "---" 时返回 0;
84
86
  def _body_start(lines):
85
87
  """跳过开头 YAML frontmatter,返回正文起始行下标(0 基)。"""
86
88
  if lines and lines[0].strip() == "---":
@@ -90,6 +92,7 @@ def _body_start(lines):
90
92
  return 0
91
93
 
92
94
 
95
+ # 生效条件:从 start(默认 0)遍历 lines,未处于围栏时遇 _FENCE 匹配行打开同标记围栏、围栏内遇同标记行关闭并继续;围栏外 _ATX 匹配行追加 {level: 一级 # 个数, title: 去空白后的标题, lineno: i+1};返回 out 列表;start 不小于 len(lines) 时返回空列表;
93
96
  def _headings(lines, start=0):
94
97
  """产出 ATX 标题 `{level,title,lineno}`;**围栏代码块内的 `#` 不算标题**。"""
95
98
  out, fence = [], None
@@ -112,6 +115,7 @@ def _headings(lines, start=0):
112
115
  return out
113
116
 
114
117
 
118
+ # 生效条件:title 真值时取其 strip 后小写,删去 `*[]() 与除 \w\s- 外字符,再把空白/下划线连成 "-" 并 strip("-");title 为假值(含 None、空串)时按空串处理并返回 "";
115
119
  def _anchor(title):
116
120
  a = (title or "").strip().lower()
117
121
  a = re.sub(r"`|\*|\[|\]|\(|\)", "", a)
@@ -119,6 +123,7 @@ def _anchor(title):
119
123
  return re.sub(r"[\s_]+", "-", a).strip("-")
120
124
 
121
125
 
126
+ # 生效条件:region_lines 逐行 strip 后跳过空行与 "---",去掉行首 #+ 和 >|*- 标记,以空格连接成 text;返回 text[:limit](limit 默认 MAX_SUMMARY;limit=0 返回 "",limit=None 返回全文,limit='' 时切片抛 TypeError,负 limit 按负索引切片);
122
127
  def _summary(region_lines, limit=MAX_SUMMARY):
123
128
  """把一段正文压成一行摘要(去 markdown 噪声,不逐字保留)。"""
124
129
  parts = []
@@ -133,6 +138,7 @@ def _summary(region_lines, limit=MAX_SUMMARY):
133
138
  return text[:limit]
134
139
 
135
140
 
141
+ # 生效条件:以 heads[i]["level"]-1 为需匹配层级向前回溯,返回按层级递减补齐的祖先标题列表(不含 heads[i] 自身)。
136
142
  def _path_titles(heads, i):
137
143
  """第 i 个标题的祖先链(不含自身),按层级补齐。"""
138
144
  out, need = [], heads[i]["level"] - 1
@@ -145,11 +151,13 @@ def _path_titles(heads, i):
145
151
  return out
146
152
 
147
153
 
154
+ # 生效条件:直接以 lines、lineno、end 调用 codeindex.region_hash 并返回其结果;
148
155
  def _region_hash(lines, lineno, end):
149
156
  # 唯一实现复用 codeindex.region_hash:两侧各写一份,漂移检测会悄悄失效。
150
157
  return codeindex.region_hash(lines, lineno, end)
151
158
 
152
159
 
160
+ # 生效条件:ext = suffix 真值时原样使用的 suffix,否则取 os.path.splitext(path)[1].lower();ext 不在 SUFFIX 时抛 ValueError;在 SUFFIX 时把 source 按 "\n" 拆分,经 _body_start 与 _headings 得到标题,仅 level<=MAX_LEVEL 且非 small 的标题生成条目,小/过深子节摘要并入父摘要,返回 items;
153
161
  def extract(source, path="", suffix=None):
154
162
  """抽取一份 md 的章节条目;按后缀分派。返回条目列表(可能为空)。
155
163
 
@@ -213,6 +221,7 @@ def extract(source, path="", suffix=None):
213
221
  # --------------------------------------------------------------------------
214
222
  # 渲染 / id
215
223
  # --------------------------------------------------------------------------
224
+ # 生效条件:item.get("path") 缺键或为假值时 path 取 "",top 取 path.split("/")[0] or "."(故空 path 时 top=".");path 为真值时 top 取其 "/" 前首段,首段为空则 top=".";返回含 observation_position(大域=top)、time_window([nodefile.FULL_TIME_WINDOW_MIN, nodefile.FULL_TIME_WINDOW_MAX])、observation_tool、existence_constraint(以 path 拼入)的四槽字典;
216
225
  def condition_space(item):
217
226
  """章节条目 → 条件空间四槽(纯函数,**唯一来源**)。
218
227
 
@@ -237,6 +246,7 @@ def condition_space(item):
237
246
  }
238
247
 
239
248
 
249
+ # 生效条件:item 含 heading、path、lineno、end、anchor 键时渲染 7 行 CCG 文本(第2行取 condition_space(item) 文本),item.get("parent") 或 "" 假值回落 "(顶层章节)",item.get("summary_parts") 假值回落 "(该节无直接正文,见子节)",item.get("children") 假值回落空列表且不追加子节行,children 非空时追加 "# 子节:" + 前 12 个;返回以 "\n" 连接的行串;
240
250
  def render(item):
241
251
  """章节条目 → CCG 6 行正文(可被 search 命中,不含全文)。
242
252
 
@@ -265,6 +275,7 @@ def render(item):
265
275
  return "\n".join(lines)
266
276
 
267
277
 
278
+ # 生效条件:以 item["path"] + "#" + "/".join(item["heading_path"]) 为 key,item.get("dup", 1)(缺键取 1)大于 1 时追加 "#" + item["dup"],返回 "doc_" + sha1(key utf-8) hexdigest 前 12 位;
268
279
  def node_id(item):
269
280
  """稳定 id:path#heading_path 的短哈希(重复索引幂等;同名用 dup 区分)。"""
270
281
  key = item["path"] + "#" + "/".join(item["heading_path"])
@@ -273,6 +284,116 @@ def node_id(item):
273
284
  return "doc_" + hashlib.sha1(key.encode("utf-8")).hexdigest()[:12]
274
285
 
275
286
 
287
+ # ==========================================================================
288
+ # fence 真源绑定(§3.5):条件卡 ↔ Markdown 真源的「往返列」
289
+ #
290
+ # 往返列 = 真源(root + path)+ 定位(anchor / heading_path)+ 行位(lineno/end)
291
+ # + 校验(hash)。**行位是易腐化量,键不含行位**:在章节上方插一段话会让整篇行号
292
+ # 位移,但键 `path#heading_path` 不变——故「同一章节」的匹配一律走键,行位只用于
293
+ # 回读原文与漂移判定。写侧唯一入口是 `refindex._doc_ref`(与索引同源,避免第二份
294
+ # 列口径);本节只补读侧:取列 / 校验 / 造键 / 反查行位。
295
+ # · 正向:卡片 → 真源(`doc_ref.lineno/end` + `refindex.read_ref` 回读原文区间)
296
+ # · 反向:真源行 → 卡片(`locate`;对账器 `--at path:line` 即此接口)
297
+ # ==========================================================================
298
+
299
+ #: 必需列:真源(root/path)+ 定位(anchor)+ 行位(lineno/end)+ 校验(hash)
300
+ BINDING_FIELDS = ("root", "path", "anchor", "lineno", "end", "hash")
301
+ #: 附加列:有则参与更精确匹配与人读,缺省不算绑定失败
302
+ BINDING_OPTIONAL = ("heading_path", "heading", "level", "lang", "precise")
303
+
304
+
305
+ # 生效条件:node 为 cg.get 产物(含 frontmatter 子字典)或 frontmatter dict 本身,且其 doc_ref 为 dict 时,返回按 BINDING_FIELDS + BINDING_OPTIONAL 投影的列值 dict(缺列以 None 占位);非 dict / 无 doc_ref 返回 None。
306
+ def binding_of(node):
307
+ """取一条条件卡的往返列;**非索引节点返回 None**(不猜、不补默认值)。"""
308
+ fm = node
309
+ if isinstance(node, dict) and isinstance(node.get("frontmatter"), dict):
310
+ fm = node["frontmatter"]
311
+ if not isinstance(fm, dict):
312
+ return None
313
+ ref = fm.get("doc_ref")
314
+ if not isinstance(ref, dict) or not ref:
315
+ return None
316
+ return {k: ref.get(k) for k in BINDING_FIELDS + BINDING_OPTIONAL}
317
+
318
+
319
+ # 生效条件:b 为 dict 且 heading_path 为非空 list/tuple 时返回 path#join(heading_path, '/'),否则回落 path#anchor;b 非 dict 返回空串。
320
+ def binding_key(b):
321
+ """稳定键 `path#heading_path`:**不含行位**(真源重排只动行位、不动键)。"""
322
+ if not isinstance(b, dict):
323
+ return ""
324
+ path = str(b.get("path") or "")
325
+ hp = b.get("heading_path")
326
+ if isinstance(hp, (list, tuple)) and hp:
327
+ return path + "#" + "/".join(str(x) for x in hp)
328
+ return path + "#" + str(b.get("anchor") or "")
329
+
330
+
331
+ # 生效条件:b 为 dict 时返回 'path#anchor'(与 render 正文里的「本条目属于」逐字同源);非 dict 返回空串。
332
+ def binding_slug(b):
333
+ """人读定位串 `path#anchor`——与 `render` 正文里的「本条目属于」同源。"""
334
+ b = b if isinstance(b, dict) else {}
335
+ return f"{b.get('path') or ''}#{b.get('anchor') or ''}"
336
+
337
+
338
+ # 生效条件:b 为 dict 时逐列校验(缺列 / 类型错 / 行位越界),返回 {"ok": bool, "issues": [str, ...]};**不抛异常**——对账要逐条报告,不能因一条坏数据中断全库;行位仅在列齐且类型对时才判,避免级联噪声。
339
+ def validate_binding(b):
340
+ """校验往返列形状。**不抛异常**:对账逐条报告,不能一条坏数据中断全库。"""
341
+ if not isinstance(b, dict):
342
+ return {"ok": False, "issues": ["绑定不是字典(该节点无 doc_ref)"]}
343
+ issues = []
344
+ for k in BINDING_FIELDS:
345
+ if b.get(k) in (None, ""):
346
+ issues.append(f"缺列 {k}")
347
+ for k in ("root", "path", "anchor", "hash"):
348
+ v = b.get(k)
349
+ if v not in (None, "") and not isinstance(v, str):
350
+ issues.append(f"{k} 应为字符串,实为 {type(v).__name__}")
351
+ if not issues:
352
+ try:
353
+ lo, hi = int(b["lineno"]), int(b["end"])
354
+ except (TypeError, ValueError):
355
+ issues.append("行位不是整数")
356
+ else:
357
+ if lo < 1:
358
+ issues.append(f"lineno 越界({lo} < 1)")
359
+ if hi < lo:
360
+ issues.append(f"end 早于 lineno({lo}-{hi})")
361
+ return {"ok": not issues, "issues": issues}
362
+
363
+
364
+ # 生效条件:old/new 任意为 dict 或假值;按 (path, anchor, heading_path, lineno, end, hash) 固定顺序返回取值不同的列名列表(空列表=同一章节同一行位),任一侧假值按空 dict 处理。
365
+ def binding_drift(old, new):
366
+ """旧往返列 vs 新往返列 → 变化列名(顺序固定,供报告与测试断言)。"""
367
+ old, new = old if isinstance(old, dict) else {}, new if isinstance(new, dict) else {}
368
+ return [k for k in ("path", "anchor", "heading_path", "lineno", "end", "hash")
369
+ if old.get(k) != new.get(k)]
370
+
371
+
372
+ # 生效条件:items 为 extract 产出的条目序列、lineno 可转 int;返回覆盖该行的条目中 **lineno 最大者**(嵌套即最内层,h3 优于其父 h2);不可转值或无可覆盖条目返回 None。
373
+ def locate(items, lineno):
374
+ """反向定位:真源第 `lineno` 行 → 覆盖它的条目(嵌套取**最内层**)。
375
+
376
+ 区间闭合:标题行算本节;多层嵌套时取 `lineno` 最大者即最内层。
377
+ """
378
+ try:
379
+ ln = int(lineno)
380
+ except (TypeError, ValueError):
381
+ return None
382
+ hit = None
383
+ for it in items or []:
384
+ lo, hi = it.get("lineno"), it.get("end")
385
+ if lo is None or hi is None:
386
+ continue
387
+ try:
388
+ lo, hi = int(lo), int(hi)
389
+ except (TypeError, ValueError):
390
+ continue
391
+ if lo <= ln <= hi and (hit is None or lo > int(hit["lineno"])):
392
+ hit = it
393
+ return hit
394
+
395
+
396
+ # 生效条件:以 root 为根 os.walk,patterns 假值回落 SUFFIX;files 达到 max_files 或 items 达到 max_items 时提前返回并置 stats["truncated"]/truncated_reason;fresh 非 None 且 fresh(rel, fp) 为真时跳过该文件读盘并计 skipped_unchanged;on_file 非 None 且 open/extract 成功后以 (rel, fp, got) 回调;名字在 SKIP_DIRS 的目录仅剪枝不记录,skip_dirs 经 codeindex.skip_matcher 命中的目录剪枝并记入 stats["skipped_dirs"];返回 (items, errors, stats)。
276
397
  def index_dir(root, patterns=None, max_files=500, max_items=2000,
277
398
  fresh=None, on_file=None, skip_dirs=None):
278
399
  """按大域(目录)遍历 md,产出 `(items, errors, stats)`。零 LLM。
@@ -350,4 +471,4 @@ def index_dir(root, patterns=None, max_files=500, max_items=2000,
350
471
  return items, errors, stats
351
472
  stats["files"] = files
352
473
  stats["skipped_suffixes"] = sorted(s for s in seen_suffix if s and s not in pats)[:12]
353
- return items, errors, stats
474
+ return items, errors, stats
@@ -58,14 +58,16 @@ PATHS = ("lexical", "bucket", "entity", "graph") # 引擎默认四路 RRF
58
58
  PATHS_CAL = PATHS + ("semantic",) # 口径 B:显式启用条件结构路(负路由所在)
59
59
 
60
60
 
61
+ # 生效条件:仅评测口径调用(主库不调用);必须双改 md_cg.mdcg 与 md_cg.mdcos 两份 from-import 的同名值(漏改其一即静默失效),置为 10**9;无返回值、不落盘;
61
62
  def unlock_global_cap():
62
63
  """评测口径:解除 GLOBAL_CAP 截断(bench_membench patch_lexical_full 同法)。
63
64
 
64
- 默认 GLOBAL_CAP=500 会把 LIKE 预筛命中按「插入序」截到前 500 条再打分;
65
- 大型英文语料(turn 高度互相似)预筛命中数千条 → 证据 turn 被插入序随机
66
- 截掉,检索质量混入插入序 lottery(sanity:证据原文自检索仅 10% hit@1)。
67
- 评测主口径解除之,只测排序质量。mdcg 与 mdcos 各持一份 from-import 值,
68
- 必须双改(test_p43_pooling 先例)。
65
+ 【2026-09-16 后语义更新】截断依据已由「插入序」改为「相关度」(`mdcg
66
+ .cut_by_relevance` 与 `mdcos._lexical` 均先全量打分再排序截断,cap 值仍
67
+ 500)——插入序 lottery 已消除。评测仍解除 cap 的理由只剩一条:避免
68
+ 「按相关度截掉尾部」在长语料上压低召回天花板(相近似 turn 数千条时,
69
+ 排 501 名之后仍可能是证据)——评测要测**排序质量**而非截断策略。
70
+ mdcg 与 mdcos 各持一份 from-import 值,必须双改(test_p43_pooling 先例)。
69
71
  """
70
72
  import md_cg.mdcg as m
71
73
  import md_cg.mdcos as mo
@@ -73,6 +75,7 @@ def unlock_global_cap():
73
75
  mo.GLOBAL_CAP = 10 ** 9
74
76
 
75
77
 
78
+ # 生效条件:仅评测口径调用(短条目语料下 jaccard 会退化);把 md_cg.mdcg.SCORE_MODE 置为 "jaccard"(mdcos 读同一份、无需双改);无返回值、不落盘;
76
79
  def use_jaccard():
77
80
  """评测口径:词法打分切 jaccard(对称归一化,长度自惩罚)。
78
81
 
@@ -93,6 +96,7 @@ def use_jaccard():
93
96
  m.SCORE_MODE = "jaccard"
94
97
 
95
98
 
99
+ # 生效条件:对 path 调用 open(path, encoding="utf-8") 后逐行读取,仅对 strip 后非空的 line yield json.loads(line);
96
100
  def iter_jsonl(path):
97
101
  with open(path, encoding="utf-8") as f:
98
102
  for line in f:
@@ -101,6 +105,7 @@ def iter_jsonl(path):
101
105
  yield json.loads(line)
102
106
 
103
107
 
108
+ # 生效条件:ds 等于 "lm" 时 path=LM_Q,否则 path=LC_Q;先由 iter_jsonl(path) 取全部 rows,再仅当 qtypes 为真值时保留 r["qtype"] 在 qtypes 中的行(qtypes 为 None/空串/空容器等假值则不过滤);
104
109
  def load_questions(ds, qtypes=None):
105
110
  """ds: 'lm' | 'lc'。qtypes 过滤题型(None=全部)。"""
106
111
  path = LM_Q if ds == "lm" else LC_Q
@@ -110,6 +115,7 @@ def load_questions(ds, qtypes=None):
110
115
  return rows
111
116
 
112
117
 
118
+ # 生效条件:对 rows/n(seed 默认 7),若 not n 或 n >= len(rows) 则原样返回 rows,否则返回 random.Random(seed).sample(rows, n);
113
119
  def sample_questions(rows, n, seed=7):
114
120
  if not n or n >= len(rows):
115
121
  return rows
@@ -118,12 +124,14 @@ def sample_questions(rows, n, seed=7):
118
124
 
119
125
  # ------------------------------------------------------------------ 建库
120
126
 
127
+ # 生效条件:对 t,若 t.get("speaker") 为真值则返回 f"{t['speaker']}: {t['text']} [{t['date']}]",否则返回 f"{t['text']} [{t['date']}]";
121
128
  def lm_turn_text(t):
122
129
  """LongMemEval turn → 入库文本(日期内联,词法路可召回时间词)。"""
123
130
  return f"{t['speaker']}: {t['text']} [{t['date']}]" if t.get("speaker") \
124
131
  else f"{t['text']} [{t['date']}]"
125
132
 
126
133
 
134
+ # 生效条件:对 r,若 r.get("title") 为真值则返回 f"{r['text']} [{r['title']}]",否则返回 r["text"];
127
135
  def lc_turn_text(r):
128
136
  """LoCoMo turn → 入库文本(title 含 Data time 时间戳,内联保序)。"""
129
137
  return f"{r['text']} [{r['title']}]" if r.get("title") else r["text"]
@@ -146,6 +154,7 @@ _STOP_HEADS = frozenset(
146
154
  _NEG_MARKS = ("n't", " not ", " never ", "nobody", "nothing", "no one", "none of")
147
155
 
148
156
 
157
+ # 生效条件:对 text(limit 默认 4),按正则收集非 _STOP_HEADS 开头的大写词与 4 位年/时刻/日期数字并去重后,返回前 limit 项;
149
158
  def _extract_entities(text, limit=4):
150
159
  """确定性实体近似:连续大写词(人名/地名)+ 日期/时间数字。只读 turn 正文。
151
160
  纯数字编号("1.")不是实体——只保留年份/时刻/日期形态,否则列表编号会
@@ -161,6 +170,7 @@ def _extract_entities(text, limit=4):
161
170
  return ents[:limit]
162
171
 
163
172
 
173
+ # 生效条件:对 title,若 title 为假值则返回空串,否则按 YYYY-MM-DD、英文星期日期、d/d/d 三个正则顺序取首个匹配串,无匹配返回空串;
164
174
  def _date_tag(title):
165
175
  """从 title 抽规范化日期串做 tag。整段 title 入 tags 会污染实体路
166
176
  (_path_entity 的 `query in t` 方向:长 tag 是误报源)。"""
@@ -173,6 +183,7 @@ def _date_tag(title):
173
183
  return m.group(0) if m else ""
174
184
 
175
185
 
186
+ # 生效条件:r 为语料 turn 行(缺 text/role 等键时按空串处理);date_key/title_key 为 None 时对应日期取空串;返回 (CCG 五要素正文, tags, condition_space) 三元组,仅当原文出现强否定标记才生成不适用条件、否则显式写「(无)」;
176
187
  def calibrate_turn(r, date_key=None, title_key=None):
177
188
  """turn 行 → (CCG 五要素正文, tags, condition_space)。
178
189
 
@@ -225,10 +236,12 @@ def calibrate_turn(r, date_key=None, title_key=None):
225
236
  return body, tags, cond_space
226
237
 
227
238
 
239
+ # 生效条件:任意 t 与可选 ctx(默认 None)传入即原样转调 calibrate_turn(t, date_key="date", ctx=ctx),返回其结果;
228
240
  def calibrate_lm_turn(t, ctx=None):
229
241
  return calibrate_turn(t, date_key="date", ctx=ctx)
230
242
 
231
243
 
244
+ # 生效条件:任意 r 与可选 ctx(默认 None)传入即原样转调 calibrate_turn(r, title_key="title", ctx=ctx),返回其结果;
232
245
  def calibrate_lc_turn(r, ctx=None):
233
246
  return calibrate_turn(r, title_key="title", ctx=ctx)
234
247
 
@@ -238,6 +251,7 @@ def calibrate_lc_turn(r, ctx=None):
238
251
  # 指代消解,把语料加工成可沿链行走的形态。以下三步全部是**确定性规则近似**
239
252
  # (纯标准库、盲于查询集、随报告公开词表),不引入任何模型依赖。
240
253
 
254
+ # 生效条件:对 corpus_path(min_df 默认 3),逐行取 text=str(r.get("text") or "")、spk=str(r.get("speaker") or "").strip(),以 {spk}(spk 真值)或空集合并入正文大写词首词(排除 _STOP_HEADS)更新 df 计数,最后返回计数 >= min_df 的词集合;
241
255
  def build_canon(corpus_path, min_df=3):
242
256
  """离线实体规范化:全语料扫一遍,聚合大写词/人名的文档频次(df)。
243
257
  df≥min_df 的是 canonical 实体(会话成员 + 高频专名)——单 turn 局部抽取
@@ -267,11 +281,13 @@ _INTENT_HEADS = ("plan", "going to", "thinking of", "thinking about", "want to",
267
281
  "prefer", "favorite", "love", "hate", "enjoy")
268
282
 
269
283
 
284
+ # 生效条件:对 text 小写并前后加空格得到 low,返回 _INTENT_HEADS 中满足 f" {h}" 出现在 low 的项;
270
285
  def _intent_of(text):
271
286
  low = f" {text.lower()} "
272
287
  return [h for h in _INTENT_HEADS if f" {h}" in low]
273
288
 
274
289
 
290
+ # 生效条件:r 为语料 turn 行;ctx 为写入时全库视图(canon/intents/last_canon),ctx 为 None 时按空视图处理并退化为 v2 行为;返回 (CCG 五要素正文, tags, condition_space) 三元组;
275
291
  def calibrate_turn(r, date_key=None, title_key=None, ctx=None):
276
292
  """turn 行 → (CCG 五要素正文, tags, condition_space)。
277
293
 
@@ -335,6 +351,7 @@ def calibrate_turn(r, date_key=None, title_key=None, ctx=None):
335
351
  return body, tags, cond_space
336
352
 
337
353
 
354
+ # 生效条件:rebuild=True 且 os.path.isdir(root) 时先 rmtree(root);cg_cls 为假值(如 None)时回落 MdCGOS(root, autoflush=500);语料行数 n_rows ≤ cg.index["nodes"] 现有节点数(n_rows=0 的空语料也满足)时直接复用返回 cg;否则逐行写入——calib_of 为真走标定口径 B(先 build_canon,calib_of(r, {"canon": canon, "last_canon": last_canon}) 取 body/tags/condition_space),calib_of 为假(含 None)走 legacy 口径 A(用 text_of(r)),id 已在 cg.index["nodes"] 的行跳过,verbose 为真时每 20000 行打印进度,循环后 cg.flush() 再返回 cg;
338
355
  def build_eval_cg(cg_cls, root, corpus_path, text_of, src_tag, verbose=True,
339
356
  calib_of=None, rebuild=False):
340
357
  """幂等建库:节点数已达语料行数则直接复用。返回 cg(不 flush 句柄)。
@@ -388,11 +405,13 @@ def build_eval_cg(cg_cls, root, corpus_path, text_of, src_tag, verbose=True,
388
405
  return cg
389
406
 
390
407
 
408
+ # 生效条件:对传入 cg,用闭包 cache(键为 entry["path"])包装其原 cg._read 并赋回 cg._read,返回该 cache;仅当 p = entry["path"] 不在 cache 时调用 orig(entry) 并缓存其(含假值)结果,p 已在 cache 中时直接返回缓存值;
391
409
  def install_read_cache(cg):
392
410
  """评测只读:节点文件读进内存,避免逐次检索重复磁盘 I/O。"""
393
411
  cache = {}
394
412
  orig = cg._read
395
413
 
414
+ # 生效条件:entry["path"] 未在闭包 cache 中时调用 orig(entry) 存入并返回,已在 cache 中则直接返回缓存值(cache 与原 _read 由外层 install_read_cache 提供);
396
415
  def _cached(entry):
397
416
  p = entry["path"]
398
417
  if p not in cache:
@@ -405,6 +424,7 @@ def install_read_cache(cg):
405
424
 
406
425
  # ------------------------------------------------------------------ 检索与指标
407
426
 
427
+ # 生效条件:对 cg/query(k 默认 5、paths 默认 PATHS、judge 默认 False),若 fusion 为真值则加入 kw["fusion"],若 path_weights 为真值则加入 kw["path_weights"],若 context is not None(含空 dict/空串)则加入 kw["context"],再调用 cg.search_rrf(...);
408
428
  def run_query(cg, query, k=5, paths=PATHS, judge=False, fusion=None,
409
429
  path_weights=None, context=None):
410
430
  """单查询,返回 (结果四元组列表, meta)。
@@ -425,6 +445,7 @@ def run_query(cg, query, k=5, paths=PATHS, judge=False, fusion=None,
425
445
  return cg.search_rrf(query, k=k, paths=paths, judge=judge, record=False, **kw)
426
446
 
427
447
 
448
+ # 生效条件:当 evidence 为真值且 res 中某 r 的 r[0]["id"] 属于 evidence 时返回首个 1-based 排名,否则(evidence 为假值或未命中)返回 0;
428
449
  def first_evidence_rank(res, evidence):
429
450
  """首个证据 turn 的排名(1-based;未命中 0)。"""
430
451
  if not evidence:
@@ -435,6 +456,7 @@ def first_evidence_rank(res, evidence):
435
456
  return 0
436
457
 
437
458
 
459
+ # 生效条件:对 questions 中每个 it,若 context_of 为真值则 ctx=context_of(it) 否则 ctx=None;以 k/paths/judge/fusion/path_weights 调用 run_query,按 evidence_turns 算 first_evidence_rank,收集 qid/qtype/rank/top1_score/n_res 行,返回 rows;
438
460
  def evaluate_group(cg, questions, k=5, paths=PATHS, judge=False, verbose=True,
439
461
  fusion=None, path_weights=None, context_of=None):
440
462
  """一组题 → per-question 明细行。负例组传 judge=False(拒答看分数线)。
@@ -461,6 +483,7 @@ def evaluate_group(cg, questions, k=5, paths=PATHS, judge=False, verbose=True,
461
483
  return rows
462
484
 
463
485
 
486
+ # 生效条件:当 rows 为真值时按 k(默认 5)计算 hit@1、hit@k、MRR、score_p10/p50 及 by_qtype 指标,否则返回 {"n": 0};
464
487
  def summarize(rows, k=5):
465
488
  """hit@1 / hit@K / MRR + 分题型。score_p10 供拒答线校准。"""
466
489
  if not rows:
@@ -491,6 +514,7 @@ def summarize(rows, k=5):
491
514
  return out
492
515
 
493
516
 
517
+ # 生效条件:当 rows 非空时按 r["top1_score"] < line 统计拒答数并返回拒绝率,否则返回 {"n": 0};
494
518
  def refusal_metrics(rows, line):
495
519
  """负例组:Top-1 分 < line 记为拒答。返回拒答率与明细统计。"""
496
520
  n = len(rows)
@@ -501,6 +525,7 @@ def refusal_metrics(rows, line):
501
525
  "refused": ref, "line": line}
502
526
 
503
527
 
528
+ # 生效条件:当 rows 非空时返回 r["top1_score"] < line 的行占比,否则返回 0.0;
504
529
  def false_refusal_rate(rows, line):
505
530
  """正例误杀(被错误拒答的正例比例):正例 Top-1 分 < line。"""
506
531
  n = len(rows)
@@ -509,6 +534,7 @@ def false_refusal_rate(rows, line):
509
534
  return sum(1 for r in rows if r["top1_score"] < line) / n
510
535
 
511
536
 
537
+ # 生效条件:当 pos_rows 中存在 rank==1 的行时,返回这些行 top1_score 升序后 max(0, len//10) 索引处的值,否则返回 0.0;
512
538
  def calibrate_line(pos_rows):
513
539
  """拒答线 = 正例 hit@1 题 Top-1 分的 10 分位。"""
514
540
  scores = sorted(r["top1_score"] for r in pos_rows if r["rank"] == 1)
@@ -517,6 +543,7 @@ def calibrate_line(pos_rows):
517
543
  return scores[max(0, len(scores) // 10)]
518
544
 
519
545
 
546
+ # 生效条件:当 name/payload 给出时,确保 RESULTS 目录(os.makedirs(RESULTS, exist_ok=True)),将 payload 以 ensure_ascii=False、indent=2 写入 RESULTS/name 并返回 path;
520
547
  def save_result(name, payload):
521
548
  """评测结果 JSON 落盘(报告数据源)。"""
522
549
  os.makedirs(RESULTS, exist_ok=True)
@@ -527,6 +554,7 @@ def save_result(name, payload):
527
554
  return path
528
555
 
529
556
 
557
+ # 生效条件:对 groups(k 默认 5),若某组 s 的 s.get("n") 为假(缺 n 或 n=0)则打印 0 行并跳过,否则按 s["hit@1"]、s[f"hit@{k}"]、s["mrr"] 打印并遍历 s.get("by_qtype", {}) 输出子行;
530
558
  def print_table(title, groups, k=5):
531
559
  """groups: {组名: summarize 输出}。"""
532
560
  print(f"\n== {title}(证据命中口径,k={k})==")
@@ -543,5 +571,6 @@ def print_table(title, groups, k=5):
543
571
  f"{st[f'hit@{k}']:>9.1%}{st['mrr']:>8.3f}")
544
572
 
545
573
 
574
+ # 生效条件:对任意 x,返回 f"{x * 100:.1f}%" 的百分比字符串;
546
575
  def pct(x):
547
- return f"{x * 100:.1f}%"
576
+ return f"{x * 100:.1f}%"
package/md_cg/evidence.py CHANGED
@@ -62,22 +62,27 @@ class EvidenceError(Exception):
62
62
  # 路径与身份
63
63
  # --------------------------------------------------------------------------
64
64
 
65
+ # 生效条件:当传入 path 为真值时返回 path;否则若环境变量 SWARM_DIR_ENV 为真值则返回其值;否则返回模块级常量 DEFAULT_SWARM_DIR。
65
66
  def swarm_dir(path: str = None) -> str:
66
67
  return path or os.environ.get(SWARM_DIR_ENV) or DEFAULT_SWARM_DIR
67
68
 
68
69
 
70
+ # 生效条件:当传入 swarm 参数(假值回落到 swarm_dir 的默认逻辑)时,返回 os.path.join(swarm_dir(swarm), "peers")。
69
71
  def peers_dir(swarm: str = None) -> str:
70
72
  return os.path.join(swarm_dir(swarm), "peers")
71
73
 
72
74
 
75
+ # 生效条件:当传入 swarm 参数(假值回落到 swarm_dir 的默认逻辑)时,返回 os.path.join(swarm_dir(swarm), "inbox")。
73
76
  def inbox_dir(swarm: str = None) -> str:
74
77
  return os.path.join(swarm_dir(swarm), "inbox")
75
78
 
76
79
 
80
+ # 生效条件:当传入 root 为真值时返回 root;否则返回环境变量 ROOT_ENV 的值(可能为 None)。
77
81
  def _root_of(root: str = None) -> str:
78
82
  return root or os.environ.get(ROOT_ENV)
79
83
 
80
84
 
85
+ # 生效条件:当 explicit 或环境变量 NODE_ID_ENV strip 后非空时返回该值;否则若 _root_of(root) 返回真值,返回基于规范绝对路径的 'node-<12hex>';否则抛出 EvidenceError。
81
86
  def node_id(root: str = None, explicit: str = None) -> str:
82
87
  """本节点身份。
83
88
 
@@ -95,6 +100,7 @@ def node_id(root: str = None, explicit: str = None) -> str:
95
100
  return "node-" + hashlib.sha256(key.encode("utf-8")).hexdigest()[:12]
96
101
 
97
102
 
103
+ # 生效条件:包内 md_cg.theory 可导入且 _th.check() 成功时返回 {version, accepted, theory_ok};任何异常(版本层缺失或校验失败)一律吞掉返回 {},不阻断证据层主流程;
98
104
  def _theory_state() -> dict:
99
105
  try:
100
106
  from . import theory as _th
@@ -106,11 +112,13 @@ def _theory_state() -> dict:
106
112
  return {}
107
113
 
108
114
 
115
+ # 生效条件:当传入 obj 时,返回其 json.dumps(obj, ensure_ascii=False, sort_keys=True, separators=(',',':')).encode("utf-8") 字节。
109
116
  def _canon(obj) -> bytes:
110
117
  return json.dumps(obj, ensure_ascii=False, sort_keys=True,
111
118
  separators=(",", ":")).encode("utf-8")
112
119
 
113
120
 
121
+ # 生效条件:当传入 s 时,返回 str(s) 中每个字符若为字母数字或 '_' '-' 则保留,否则替换为 '_' 的字符串。
114
122
  def _slug(s) -> str:
115
123
  return "".join(ch if (ch.isalnum() or ch in "_-") else "_" for ch in str(s))
116
124
 
@@ -119,6 +127,7 @@ def _slug(s) -> str:
119
127
  # 节点名片(发现)
120
128
  # --------------------------------------------------------------------------
121
129
 
130
+ # 生效条件:当传入 root、subsystem(默认常量 SUBSYSTEM)、signers_file 时,基于 _signer.policy_for 和 _signer.get_signer 返回名片字典,其中 root 为 os.path.abspath(_root_of(root)) 若 _root_of(root) 真值否则 None。
122
131
  def card(root: str = None, *, subsystem: str = SUBSYSTEM,
123
132
  signers_file: str = None) -> dict:
124
133
  """本节点名片:身份 + 版本 + 签名器公开信息(**不含密钥**)。"""
@@ -139,6 +148,7 @@ def card(root: str = None, *, subsystem: str = SUBSYSTEM,
139
148
  }
140
149
 
141
150
 
151
+ # 生效条件:当传入 root、swarm、subsystem(默认常量 SUBSYSTEM)、signers_file 时,调用 card 得到 c,将 c 以 JSON 写入 peers_dir(swarm)/<c["node_id"]>.json,返回 {"ok": True, "file": p, "card": c}。
142
152
  def publish_card(root: str = None, *, swarm: str = None,
143
153
  subsystem: str = SUBSYSTEM, signers_file: str = None) -> dict:
144
154
  """把本节点名片写入共享目录 `peers/<node_id>.json`(0600)。"""
@@ -148,6 +158,7 @@ def publish_card(root: str = None, *, swarm: str = None,
148
158
  return {"ok": True, "file": p, "card": c}
149
159
 
150
160
 
161
+ # 生效条件:当 peers_dir(swarm) 是目录时,遍历其中 .json 文件,加载为 dict 且 kind 等于常量 CARD_KIND 的名片,若 exclude_self 为真则排除 node_id 等于 node_id(root) 的名片(node_id(root) 抛 EvidenceError 时 self_id 为 None 不排除),返回按文件名排序的列表及计数;目录不存在则返回空列表。
151
162
  def peers(swarm: str = None, *, exclude_self: bool = True,
152
163
  root: str = None) -> dict:
153
164
  """发现共享目录里的对端名片(按 node_id 排序)。"""
@@ -183,6 +194,7 @@ _PACK_FIELDS = ("schema", "kind", "pack_id", "from_node", "from_root",
183
194
  "theory", "created_at", "subjects", "items")
184
195
 
185
196
 
197
+ # 生效条件:当传入 pack 字典时,返回仅含模块级常量 _PACK_FIELDS 中字段(缺失字段取 pack.get(k) 得到 None)的规范化 JSON 字节。
186
198
  def _pack_payload(pack: dict) -> bytes:
187
199
  """签名载荷:固定字段 + 排序序列化,同参数必同字节。"""
188
200
  body = {}
@@ -191,6 +203,7 @@ def _pack_payload(pack: dict) -> bytes:
191
203
  return _canon(body)
192
204
 
193
205
 
206
+ # 生效条件:当传入 cg、subjects、since 时,遍历 cg.index["nodes"] 中按 node_id 排序的节点,仅收集标签含常量 TAG_OBS、不含 TAG_ANCHOR/TAG_TRAIT、不含 TAG_XNODE、能提取 subject: 标签且该 subject 在 subjects(subjects 为真时)或不限制(subjects 假值时)、且若 since 为真且 ts 为真则要求 float(ts) >= float(since) 的节点,返回含 node_id、subject、text(截断 MAX_TEXT)、kind、role、layer、过滤后的 tags、importance、verification_basis、ts 的列表。
194
207
  def _collect(cg, subjects=None, since=None) -> list:
195
208
  """收集本根**本地原始**观测证据(排除档案节点与已导入的跨节点证据)。"""
196
209
  want = set(subjects or [])
@@ -237,6 +250,7 @@ def _collect(cg, subjects=None, since=None) -> list:
237
250
  return items
238
251
 
239
252
 
253
+ # 生效条件:当传入 cg、subjects、since、swarm、subsystem(默认 SUBSYSTEM)、signers_file、require_signature(默认 True)时,收集证据并截断至常量 MAX_ITEMS,用 _signer.sign_for 以常量 SIGN_ACTION 签名,若 require_signature 为真且签名未成功则抛出 EvidenceError,否则返回含 pack、count、truncated、signed 的字典。
240
254
  def export_pack(cg, *, subjects=None, since=None, swarm: str = None,
241
255
  subsystem: str = SUBSYSTEM, signers_file: str = None,
242
256
  require_signature: bool = True) -> dict:
@@ -275,6 +289,7 @@ def export_pack(cg, *, subjects=None, since=None, swarm: str = None,
275
289
  "truncated": truncated, "signed": pack["sig"]["signed"]}
276
290
 
277
291
 
292
+ # 生效条件:当传入 pack、path、swarm 时,若 path 为假值则用 os.path.join(inbox_dir(swarm), f"{_slug(pack.get('from_node'))}__{pack.get('pack_id')}.json") 生成路径,将 pack 以 JSON 写入该路径并返回 {"ok": True, "file": path}。
278
293
  def write_pack(pack: dict, path: str = None, swarm: str = None) -> dict:
279
294
  """把证据包落盘到收件箱(或指定路径)。"""
280
295
  if not path:
@@ -288,6 +303,7 @@ def write_pack(pack: dict, path: str = None, swarm: str = None) -> dict:
288
303
  # 证据包:导入
289
304
  # --------------------------------------------------------------------------
290
305
 
306
+ # 生效条件:当 src 是 dict 时直接返回 src;否则以 src 为路径打开 JSON 文件并返回 json.load(f)。
291
307
  def _read_pack(src) -> dict:
292
308
  if isinstance(src, dict):
293
309
  return src
@@ -295,6 +311,7 @@ def _read_pack(src) -> dict:
295
311
  return json.load(f)
296
312
 
297
313
 
314
+ # 生效条件:当 pack 为 dict 且 kind 等于常量 KIND、int(pack.get("schema") or 0) 不大于常量 SCHEMA、from_node strip 后非空、items 为 list 时返回空字符串;否则按序返回对应错误字符串(非 dict、kind 不符、schema 过新、缺 from_node、缺 items)。
298
315
  def _validate(pack: dict) -> str:
299
316
  if not isinstance(pack, dict):
300
317
  return "证据包不是 JSON 对象"
@@ -309,10 +326,12 @@ def _validate(pack: dict) -> str:
309
326
  return ""
310
327
 
311
328
 
329
+ # 生效条件:当传入 from_node 和 orig 时,返回 f"xnode_{_slug(from_node)}_{_slug(orig)}"。
312
330
  def _xnode_id(from_node: str, orig: str) -> str:
313
331
  return f"xnode_{_slug(from_node)}_{_slug(orig)}"
314
332
 
315
333
 
334
+ # 生效条件:当传入 pack、reason、swarm、**kw 时,构建含 at、reason、from_node、pack_id 及 kw 中非 None 项的拒绝记录,尝试追加到 inbox_dir(swarm)/_rejected.jsonl(OSError 忽略),返回 {"ok": False, "rejected": True, "reason": reason, "from_node": ...} 并合并 kw 中非 None 项。
316
335
  def _note_reject(pack: dict, reason: str, swarm: str = None, **kw) -> dict:
317
336
  rec = {"at": time.time(), "reason": reason,
318
337
  "from_node": (pack or {}).get("from_node"),
@@ -330,6 +349,7 @@ def _note_reject(pack: dict, reason: str, swarm: str = None, **kw) -> dict:
330
349
  return out
331
350
 
332
351
 
352
+ # 生效条件:当传入 cg、src、subsystem(默认 SUBSYSTEM)、swarm、signers_file、archive(默认 True)时,依次读取 src 为 pack、_validate 校验、来源 node_id 不等于本节点、_signer.verify_for 验签成功、且有签名或 signer 等于 "null" 方可通过;通过则对 pack["items"] 前常量 MAX_ITEMS 项中 subject/text 非空者以 _identity.observe 写入(带常量 TAG_XNODE 和 SOURCE_PREFIX 标签,importance 默认 0.5,verification_basis 默认 "data"),统计 written/skipped,若 archive 为真则写包到收件箱,返回含 ok、from_node、pack_id、imported、skipped、truncated、node_ids、archived 的字典;任一拒绝分支调用 _note_reject 返回拒绝结果。
333
353
  def import_pack(cg, src, *, subsystem: str = SUBSYSTEM, swarm: str = None,
334
354
  signers_file: str = None, archive: bool = True) -> dict:
335
355
  """导入对端证据包:验签 → 落本根(带 `source:<node>` 标签)。
@@ -404,6 +424,7 @@ def import_pack(cg, src, *, subsystem: str = SUBSYSTEM, swarm: str = None,
404
424
  # 查询
405
425
  # --------------------------------------------------------------------------
406
426
 
427
+ # 生效条件:当传入 cg、subject、source、limit(默认 100)时,收集标签含常量 TAG_XNODE 的节点,若 subject 为真则仅保留 subject 标签等于 subject 的节点,若 source 为真则仅保留 source 标签等于 source 的节点,返回最后 int(limit or 100) 条记录(limit 为假值如 0 时取 100)的 count 与列表。
407
428
  def evidence(cg, *, subject: str = None, source: str = None,
408
429
  limit: int = 100) -> dict:
409
430
  """列本根已导入的跨节点证据(按来源/主体过滤)。"""
@@ -432,6 +453,7 @@ def evidence(cg, *, subject: str = None, source: str = None,
432
453
  return {"count": len(out), "evidence": out}
433
454
 
434
455
 
456
+ # 生效条件:无参数调用时返回包含模块级常量 SUBSYSTEM、SIGN_ACTION、SWARM_DIR_ENV、NODE_ID_ENV、ROOT_ENV、MAX_ITEMS、MAX_TEXT 及 swarm_dir() 等字段的自描述字典。
435
457
  def catalog() -> dict:
436
458
  return {
437
459
  "layer": "跨节点证据存储(蜂群互联 v0.3)",
@@ -453,6 +475,7 @@ def catalog() -> dict:
453
475
  # 工具
454
476
  # --------------------------------------------------------------------------
455
477
 
478
+ # 生效条件:当传入 path 和 obj 时,在 path 所在目录(若 dirname 为空则 ".")创建目录,将 obj 以 JSON 写入 path+".tmp"(ensure_ascii=False, indent=1, sort_keys=True),尝试 chmod 0600,替换到 path,返回 path。
456
479
  def _write_json(path: str, obj) -> str:
457
480
  os.makedirs(os.path.dirname(path) or ".", exist_ok=True)
458
481
  tmp = path + ".tmp"
@@ -466,6 +489,7 @@ def _write_json(path: str, obj) -> str:
466
489
  return path
467
490
 
468
491
 
492
+ # 生效条件:当传入 root 且 _root_of(root) 返回真值时,以 designer 角色 Principal(tenant 取 os.environ.get("MDCG_TENANT", "default") 的实际值,actor 取 os.environ.get("MDCG_ACTOR", "swarm-cli") 的实际值)打开 MdCGSecure;否则抛出 EvidenceError。
469
493
  def _open_cg(root: str = None):
470
494
  """CLI 用:以 designer 身份打开根(令牌路径仍推荐走 MCP)。"""
471
495
  from .mdcos import MdCGSecure
@@ -483,10 +507,12 @@ def _open_cg(root: str = None):
483
507
  return MdCGSecure(r, principal=p)
484
508
 
485
509
 
510
+ # 生效条件:当传入 obj 时,打印 json.dumps(obj, ensure_ascii=False, indent=1, default=str)。
486
511
  def _print(obj):
487
512
  print(json.dumps(obj, ensure_ascii=False, indent=1, default=str))
488
513
 
489
514
 
515
+ # 生效条件:argv 为 None 时取 sys.argv[1:];经 argparse 解析后必填子命令(catalog/card/export/import 等)之一,参数缺失或非法由 argparse 直接退出;返回进程退出码;
490
516
  def main(argv=None):
491
517
  ap = argparse.ArgumentParser(
492
518
  prog="python -m md_cg.evidence",
@@ -552,4 +578,4 @@ def main(argv=None):
552
578
 
553
579
 
554
580
  if __name__ == "__main__":
555
- sys.exit(main())
581
+ sys.exit(main())