@furongjun1999/dsh-memory 0.4.11 → 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (580) hide show
  1. package/README.md +552 -465
  2. package/codebuddy/CODEBUDDY.md +11 -3
  3. package/codebuddy/README.md +92 -90
  4. package/codebuddy/mcp.json +27 -27
  5. package/docs/GBrain/345/217/257/345/200/237/351/211/264/347/202/271_/347/201/265/346/236/242/350/220/275/347/202/271/344/272/244/346/216/245_20260919.md +169 -169
  6. package/docs/Pi/345/217/257/345/255/246/344/271/240/344/274/230/347/202/271_/347/201/265/346/236/242/345/244/247/350/204/221/346/224/271/350/277/233/344/272/244/346/216/245_20260915.md +144 -144
  7. package/docs/README.md +143 -111
  8. package/docs/discipline/harnesses.yaml +244 -226
  9. package/docs/discipline/templates/full.md.tmpl +61 -61
  10. package/docs/discipline/templates/rules.mdc.tmpl +68 -0
  11. package/docs/discipline/templates/skill.md.tmpl +23 -23
  12. package/docs/eval/AGI/344/270/203/347/273/264/350/257/204/345/210/206/346/212/245/345/221/212_md_cg_v1.0.md +299 -299
  13. package/docs/eval/AGI/344/270/203/347/273/264/350/257/204/345/210/206/346/212/245/345/221/212_md_cg_v2.0.md +239 -239
  14. package/docs/eval/bench_lingshu_self/bench_self.py +140 -0
  15. package/docs/eval/bench_lingshu_self/self_bench_result.json +404 -0
  16. package/docs/eval/bench_lingshu_self//347/201/265/346/236/242/350/207/252/345/272/223/347/253/257/345/210/260/347/253/257/346/243/200/347/264/242/345/256/236/346/265/213_v1.0.md +38 -0
  17. package/docs/eval//344/270/215/345/217/257/351/235/240/346/200/247/350/220/275/345/234/260_P0_v1.0.md +185 -0
  18. package/docs/eval//345/256/236/351/252/214/346/226/271/346/241/210_/345/255/246/344/271/240/351/227/255/347/216/257AB/344/270/216/346/250/252/350/257/204_v1.0.md +174 -174
  19. package/docs/eval//346/225/205/351/232/234/346/263/250/345/205/245/345/256/236/346/265/213_v1.0.md +422 -0
  20. package/docs/eval//346/250/252/350/257/204_/345/205/255/345/256/266100/351/242/230/344/270/255/350/213/261/345/217/214/346/237/245_v1.0.md +223 -223
  21. package/docs/eval//347/253/257/345/210/260/347/253/257LoCoMoQA/345/220/214/345/217/243/345/276/204/345/257/271/347/205/247_v1.0.md +100 -0
  22. package/docs/eval//347/253/257/345/210/260/347/253/257/345/271/262/346/211/260/346/261/240/350/257/204/346/265/213_/347/241/256/345/256/232/346/200/247/350/243/201/345/206/263vsLLM_judge_v1.1.md +197 -0
  23. package/docs/eval//347/274/272/351/231/267/346/214/226/346/216/230_/350/207/252/344/270/273/350/277/255/344/273/243_v1.md +156 -0
  24. package/docs/eval//347/274/272/351/231/267/346/214/226/346/216/230_/350/207/252/344/270/273/350/277/255/344/273/243_v10.md +210 -0
  25. package/docs/eval//347/274/272/351/231/267/346/214/226/346/216/230_/350/207/252/344/270/273/350/277/255/344/273/243_v11.md +227 -0
  26. package/docs/eval//347/274/272/351/231/267/346/214/226/346/216/230_/350/207/252/344/270/273/350/277/255/344/273/243_v12.md +203 -0
  27. package/docs/eval//347/274/272/351/231/267/346/214/226/346/216/230_/350/207/252/344/270/273/350/277/255/344/273/243_v13.md +233 -0
  28. package/docs/eval//347/274/272/351/231/267/346/214/226/346/216/230_/350/207/252/344/270/273/350/277/255/344/273/243_v14.md +191 -0
  29. package/docs/eval//347/274/272/351/231/267/346/214/226/346/216/230_/350/207/252/344/270/273/350/277/255/344/273/243_v15.md +213 -0
  30. package/docs/eval//347/274/272/351/231/267/346/214/226/346/216/230_/350/207/252/344/270/273/350/277/255/344/273/243_v16.md +214 -0
  31. package/docs/eval//347/274/272/351/231/267/346/214/226/346/216/230_/350/207/252/344/270/273/350/277/255/344/273/243_v17.md +199 -0
  32. package/docs/eval//347/274/272/351/231/267/346/214/226/346/216/230_/350/207/252/344/270/273/350/277/255/344/273/243_v2.md +156 -0
  33. package/docs/eval//347/274/272/351/231/267/346/214/226/346/216/230_/350/207/252/344/270/273/350/277/255/344/273/243_v3.md +152 -0
  34. package/docs/eval//347/274/272/351/231/267/346/214/226/346/216/230_/350/207/252/344/270/273/350/277/255/344/273/243_v4.md +128 -0
  35. package/docs/eval//347/274/272/351/231/267/346/214/226/346/216/230_/350/207/252/344/270/273/350/277/255/344/273/243_v5.md +114 -0
  36. package/docs/eval//347/274/272/351/231/267/346/214/226/346/216/230_/350/207/252/344/270/273/350/277/255/344/273/243_v6.md +192 -0
  37. package/docs/eval//347/274/272/351/231/267/346/214/226/346/216/230_/350/207/252/344/270/273/350/277/255/344/273/243_v7.md +187 -0
  38. package/docs/eval//347/274/272/351/231/267/346/214/226/346/216/230_/350/207/252/344/270/273/350/277/255/344/273/243_v8.md +207 -0
  39. package/docs/eval//347/274/272/351/231/267/346/214/226/346/216/230_/350/207/252/344/270/273/350/277/255/344/273/243_v9.md +203 -0
  40. package/docs/{mdcg → hive}//344/273/244/347/211/214/344/270/216/350/247/222/350/211/262/346/235/203/350/201/214/345/210/206/347/246/273_v0.1.md +165 -160
  41. package/docs/hive//344/273/244/347/211/214/350/257/255/344/271/211/344/277/256/346/255/243_/344/273/262/350/243/201/344/275/215_v0.1.md +76 -0
  42. package/docs/{mdcg → hive}//345/255/220/344/273/243/347/220/206/351/205/215/347/275/256/346/240/207/345/207/206_v0.5.md +236 -236
  43. package/docs/hive//345/256/211/345/205/250/345/256/241/350/256/241/345/256/236/351/224/232_v0.1.md +202 -0
  44. package/docs/hive//346/243/200/347/264/242/346/224/266/346/225/233/345/256/236/346/265/213/344/270/216S1b/350/256/276/350/256/241_v0.1.md +43 -43
  45. package/docs/hive//346/243/200/347/264/242/347/256/227/346/263/225/345/217/243/345/276/204/345/257/271/347/205/247_v0.1.md +85 -0
  46. package/docs/hive//346/243/200/347/264/242/350/267/257/345/276/204/344/270/216/350/256/244/347/237/245/347/273/223/346/236/204/345/245/221/347/272/246_v0.1.md +102 -102
  47. package/docs/hive//350/234/202/345/267/242M6_ingest/345/256/236/346/226/275/350/256/241/345/210/222_v0.1.md +47 -0
  48. package/docs/hive//350/234/202/345/267/242/345/217/214/345/256/236/344/276/213/344/272/222/351/252/214_/350/256/276/350/256/241/345/256/232/347/250/277.md +519 -503
  49. package/docs/hive//350/234/202/345/267/242/345/267/245/344/275/234/350/256/260/345/277/206_/351/241/271/347/233/256/350/256/241/345/210/222.md +85 -85
  50. package/docs/hive//350/234/202/345/267/242/350/256/276/350/256/241_/347/220/206/350/256/272/345/257/271/351/275/220_v0.1.md +191 -0
  51. package/docs/hive//350/234/202/345/267/242/350/277/255/344/273/243_/345/256/217/350/247/202/344/270/216/347/276/244/344/275/223/350/260/203/345/272/246_v0.1.md +90 -0
  52. package/docs/images/lingshu-moonlight-covenant-poster-preview.jpg +0 -0
  53. package/docs/images/lingshu-moonlight-covenant-poster.png +0 -0
  54. package/docs/mdcg/D_meta_/345/267/245/347/250/213/345/214/226/346/226/271/346/241/210_v0.2.md +216 -216
  55. package/docs/mdcg/README/350/257/246/347/273/206/347/211/210_v0.4.10.md +646 -646
  56. package/docs/mdcg/lingshu_tutorial.html +14449 -14449
  57. package/docs/mdcg/release_v0.4.11.md +49 -0
  58. package/docs/mdcg/release_v0.4.5.md +55 -55
  59. package/docs/mdcg/tool_table_v0.3.0.md +117 -117
  60. package/docs/mdcg//345/205/250/345/272/223/344/273/243/347/240/201/350/257/204/345/256/241/344/270/216/346/235/241/344/273/266/345/214/226/346/263/250/351/207/212_/350/256/241/345/210/222_v0.1.md +600 -600
  61. package/docs/mdcg//345/212/237/350/203/275/350/260/203/347/224/250/346/230/240/345/260/204/350/241/250_v0.1.md +40 -40
  62. package/docs/mdcg//345/215/225/345/205/203/350/207/252/346/210/221/351/224/232/347/202/271_/347/263/273/347/273/237/346/217/220/347/244/272/350/257/215/346/240/207/345/207/206_v0.3.md +275 -275
  63. package/docs/mdcg//345/217/221/345/270/203/351/227/250/347/246/201/351/223/276_v0.1.md +54 -0
  64. package/docs/mdcg//346/272/220/347/240/201/347/272/247/346/236/266/346/236/204/345/256/241/350/256/241_GPT/346/211/271/350/257/204/345/257/271/347/205/247_v1.0.md +130 -130
  65. package/docs/mdcg//347/201/265/346/236/24282/345/267/245/345/205/267_/345/212/237/350/203/275/346/225/264/347/220/206/344/270/216/350/277/201/347/247/273/346/230/240/345/260/204_v0.1.md +278 -278
  66. package/docs/mdcg//347/201/265/346/236/242/350/256/260/345/277/206/345/212/250/350/257/215/345/215/217/350/256/256_v1.0-draft.md +172 -172
  67. package/docs/mdcg//347/274/272/345/217/243/345/215/225_P0/346/224/266/345/217/243_v0.1.md +270 -270
  68. package/docs/mdcg//350/256/244/347/237/245/345/233/276_G4-G8/347/274/272/345/217/243/350/243/201/345/256/232/345/215/225_v0.1.md +491 -491
  69. package/docs/mdcg//350/256/244/347/237/245/345/233/276_/346/235/241/344/273/266/347/251/272/351/227/264/345/220/210/346/210/220/344/270/216/347/224/237/346/225/210/346/235/241/344/273/266/345/217/243/345/276/204_v0.1.md +340 -340
  70. package/docs/mdcg//350/256/244/347/237/245/345/233/276_/347/264/242/345/274/225/344/270/216/345/267/245/347/250/213/350/247/204/350/214/203/345/214/226_/350/256/241/345/210/222_v0.1.md +588 -588
  71. package/docs/plans/GridWorld/346/234/200/345/260/217/351/227/255/347/216/257/350/247/204/346/240/274_v0.1.md +176 -176
  72. package/docs/plans//345/221/275/345/220/215/346/262/273/347/220/206_/351/241/271/347/233/256/350/256/241/345/210/222.md +81 -81
  73. package/docs/swarm//350/234/202/347/276/244/344/272/222/350/201/224_v0.1.md +704 -704
  74. package/docs/swarm//350/234/202/347/276/244/345/220/214/351/224/231/346/243/200/346/265/213/345/256/236/351/252/214/345/215/217/350/256/256_v0.1.md +242 -242
  75. package/docs/theory//344/270/215/345/217/257/351/235/240/345/256/232/347/220/206/344/270/216/345/244/261/346/225/210/344/274/230/345/205/210/346/241/206/346/236/266_v0.3.md +365 -0
  76. package/docs/theory//345/215/225/347/272/277/347/250/213/344/270/216/346/263/250/346/204/217/345/212/233/351/233/206/344/270/255_/346/227/240/344/272/211/350/256/256/347/220/206/350/256/272/346/226/207/346/241/243_v1.0.md +129 -129
  77. package/docs/theory//345/271/266/345/217/221/345/277/205/347/204/266/346/200/247/347/220/206/350/256/272_v0.2.md +134 -134
  78. package/docs/theory//346/231/272/350/203/275/350/256/2723.4.md +5260 -5260
  79. package/docs/theory//346/246/202/345/277/265/345/210/206/345/261/202/345/257/271/351/275/220/350/241/250_v0.1.md +145 -145
  80. package/docs/theory//347/220/206/350/256/272_/346/234/272/345/210/266_/344/273/243/347/240/201_/345/256/236/351/252/214_/347/274/272/345/217/243/347/237/251/351/230/265_v0.1.md +144 -144
  81. package/docs/theory//347/220/206/350/256/272/344/273/223/346/213/206/345/210/206/344/270/216/347/231/275/347/256/261/347/237/245/350/257/206/345/272/223/345/206/205/350/277/201_v0.1.md +198 -198
  82. package/docs/theory//350/256/244/347/237/245/344/273/243/347/220/206/344/270/216/346/224/266/346/225/233/347/273/223/346/236/204_/346/235/241/344/273/266/350/256/272/351/207/215/346/236/204_v0.1.md +221 -221
  83. package/docs/world_model//344/270/226/347/225/214/346/250/241/345/236/213_/347/245/236/347/273/217/347/275/221/347/273/234/345/272/225/345/261/202/346/236/266/346/236/204_v1.0.md +237 -237
  84. package/docs//344/270/215/345/217/257/351/235/240/346/200/247/347/220/206/350/256/272_v0.1.md +343 -0
  85. package/docs//345/217/221/345/270/203/344/273/266/345/233/236/346/272/257/350/257/264/346/230/216_v0.1.md +82 -82
  86. package/docs//345/267/245/344/275/234/347/272/252/345/276/213_/350/256/244/347/237/245/345/233/276/346/235/241/347/233/256_v1.1.json +28 -2
  87. package/dsh/README.md +82 -82
  88. package/dsh/cordis.yml.example +139 -139
  89. package/dsh/update-lingshu.bat +11 -11
  90. package/lib/bridge.d.ts +9 -0
  91. package/lib/bridge.js +35 -0
  92. package/lib/hooks.js +36 -2
  93. package/lib/index.js +7 -1
  94. package/lib/lib/roleplay_web.js +116 -29
  95. package/lib/lib/token_store.d.ts +7 -1
  96. package/lib/lib/token_store.js +12 -3
  97. package/md_cg/__init__.py +7 -7
  98. package/md_cg/audit.py +379 -368
  99. package/md_cg/autonomy.py +287 -287
  100. package/md_cg/backfill.py +1328 -1327
  101. package/md_cg/backfill_bigdomain.py +34 -34
  102. package/md_cg/backfill_bucket_zh.py +35 -0
  103. package/md_cg/bench6_arms.py +410 -410
  104. package/md_cg/bench6_common.py +230 -230
  105. package/md_cg/bench6_competitors.py +212 -212
  106. package/md_cg/bench_axis_domain.py +257 -257
  107. package/md_cg/bench_blind_comp.py +308 -308
  108. package/md_cg/bench_e2e_judge.py +532 -0
  109. package/md_cg/bench_e2e_locomo_qa.py +368 -0
  110. package/md_cg/bench_e2e_qa.py +256 -0
  111. package/md_cg/bench_en_atoms_public.py +230 -230
  112. package/md_cg/bench_governance.py +348 -348
  113. package/md_cg/bench_lme_zh.py +410 -410
  114. package/md_cg/bench_locomo.py +121 -121
  115. package/md_cg/bench_locomo_zh.py +450 -450
  116. package/md_cg/bench_locomo_zh_public.py +147 -147
  117. package/md_cg/bench_longmem.py +112 -112
  118. package/md_cg/bench_membench.py +632 -632
  119. package/md_cg/bench_p0.py +149 -149
  120. package/md_cg/bench_progressive.py +287 -287
  121. package/md_cg/bench_role_views.py +238 -238
  122. package/md_cg/bench_task_ab.py +243 -243
  123. package/md_cg/bench_task_ab_llm.py +408 -408
  124. package/md_cg/bench_unified_en.py +204 -204
  125. package/md_cg/bench_zh_mad.py +601 -601
  126. package/md_cg/blindspot_tickets.py +123 -123
  127. package/md_cg/branches.py +301 -285
  128. package/md_cg/build_postings.py +73 -73
  129. package/md_cg/ccgc.py +1006 -948
  130. package/md_cg/census.py +132 -132
  131. package/md_cg/chain.py +315 -300
  132. package/md_cg/codeindex.py +531 -531
  133. package/md_cg/coldverify.py +292 -292
  134. package/md_cg/comment_gate.py +337 -337
  135. package/md_cg/cond_compose.py +190 -190
  136. package/md_cg/cond_facts.py +154 -154
  137. package/md_cg/cond_template.json +106 -106
  138. package/md_cg/condition_anchor.py +142 -142
  139. package/md_cg/conformance.py +726 -726
  140. package/md_cg/consistency.py +717 -717
  141. package/md_cg/consolidate.py +1537 -1439
  142. package/md_cg/corpus.py +110 -110
  143. package/md_cg/crosscheck.py +1098 -1097
  144. package/md_cg/crypto.py +3 -1
  145. package/md_cg/d_meta.py +310 -310
  146. package/md_cg/datapath.py +78 -18
  147. package/md_cg/docindex.py +473 -473
  148. package/md_cg/eval_common.py +575 -575
  149. package/md_cg/evidence.py +4 -2
  150. package/md_cg/evolution.py +477 -477
  151. package/md_cg/export.py +222 -220
  152. package/md_cg/forgetting.py +581 -581
  153. package/md_cg/fsutil.py +377 -329
  154. package/md_cg/hotcache.py +48 -7
  155. package/md_cg/hyperedge.py +251 -251
  156. package/md_cg/identity.py +390 -390
  157. package/md_cg/insight.py +500 -500
  158. package/md_cg/interop.py +338 -0
  159. package/md_cg/judgment_manifest.py +177 -0
  160. package/md_cg/lexicon/build_cedict_en_zh.py +329 -329
  161. package/md_cg/lexicon/build_standard_en.py +171 -171
  162. package/md_cg/lexicon/expand_en_zh.py +211 -211
  163. package/md_cg/lifecycle.py +272 -272
  164. package/md_cg/linkref.py +280 -280
  165. package/md_cg/links.py +140 -107
  166. package/md_cg/mcp_server.py +403 -55
  167. package/md_cg/md_whitebox.py +345 -345
  168. package/md_cg/mdcg.py +783 -233
  169. package/md_cg/mdcos.py +500 -70
  170. package/md_cg/metacognition.py +591 -591
  171. package/md_cg/migrate.py +119 -119
  172. package/md_cg/migrate_aeis.py +221 -221
  173. package/md_cg/migrate_roleplay.py +293 -293
  174. package/md_cg/migrate_wisdom_graph.py +360 -360
  175. package/md_cg/mreview/__init__.py +25 -25
  176. package/md_cg/mreview/__main__.py +110 -110
  177. package/md_cg/mreview/bundle.py +178 -178
  178. package/md_cg/mreview/candidates.py +262 -262
  179. package/md_cg/mreview/govern.py +694 -693
  180. package/md_cg/mreview/locate.py +939 -939
  181. package/md_cg/mreview/pipeline.py +728 -728
  182. package/md_cg/mreview/rules/duplication.json +21 -21
  183. package/md_cg/mreview/rules/field_coverage.json +54 -54
  184. package/md_cg/mreview/rules/source_license.json +21 -21
  185. package/md_cg/mreview/rules/template_flow.json +21 -21
  186. package/md_cg/mreview/ruleset.py +252 -252
  187. package/md_cg/nodefile.py +575 -575
  188. package/md_cg/pooling.py +484 -472
  189. package/md_cg/postings.py +300 -298
  190. package/md_cg/predict.py +1100 -1100
  191. package/md_cg/progressive.py +123 -123
  192. package/md_cg/protect.py +272 -272
  193. package/md_cg/protocol/md_cg_gate.proto +33 -33
  194. package/md_cg/protocol.py +372 -372
  195. package/md_cg/provenance.py +582 -582
  196. package/md_cg/reach.py +453 -453
  197. package/md_cg/readcache.py +143 -0
  198. package/md_cg/reconcile.py +228 -0
  199. package/md_cg/refindex.py +833 -833
  200. package/md_cg/refine.py +604 -604
  201. package/md_cg/review_cli.py +170 -0
  202. package/md_cg/roleviews.py +89 -89
  203. package/md_cg/routing.py +393 -365
  204. package/md_cg/run_tests.py +211 -0
  205. package/md_cg/scrub.py +13 -3
  206. package/md_cg/security.py +128 -18
  207. package/md_cg/self_state.py +1029 -1029
  208. package/md_cg/selfreport.py +152 -151
  209. package/md_cg/semantic/__init__.py +10 -10
  210. package/md_cg/semantic/canonical.py +122 -122
  211. package/md_cg/semantic/en_normalizer.py +364 -364
  212. package/md_cg/semantic/en_zh_map.json +28694 -0
  213. package/md_cg/semantic/export_en_zh_map.py +64 -0
  214. package/md_cg/semantic/unify.py +45 -0
  215. package/md_cg/semantic/zh_en_atoms.py +139 -139
  216. package/md_cg/signer.py +7 -4
  217. package/md_cg/sources.py +816 -582
  218. package/md_cg/statushdr.py +179 -179
  219. package/md_cg/stg.py +54 -37
  220. package/md_cg/subgraph.py +729 -729
  221. package/md_cg/sustain.py +35 -5
  222. package/md_cg/tasks.py +470 -470
  223. package/md_cg/test_access_hints.py +147 -0
  224. package/md_cg/test_action_derive.py +203 -203
  225. package/md_cg/test_audit_rotate.py +270 -270
  226. package/md_cg/test_autonomy.py +143 -143
  227. package/md_cg/test_bench_governance.py +102 -102
  228. package/md_cg/test_blindspot_tickets.py +166 -166
  229. package/md_cg/test_branch_discard_tombstone.py +136 -0
  230. package/md_cg/test_branches.py +13 -3
  231. package/md_cg/test_ccg_perturb.py +184 -184
  232. package/md_cg/test_ccgc.py +433 -433
  233. package/md_cg/test_census_prune.py +81 -81
  234. package/md_cg/test_chain_read_isolate.py +168 -0
  235. package/md_cg/test_cond_compose_anchors.py +76 -76
  236. package/md_cg/test_cond_match.py +165 -165
  237. package/md_cg/test_condition_anchor.py +81 -81
  238. package/md_cg/test_d_meta.py +412 -412
  239. package/md_cg/test_datapath_device_name.py +203 -0
  240. package/md_cg/test_datapath_root.py +199 -199
  241. package/md_cg/test_emit_negtail_cache.py +156 -0
  242. package/md_cg/test_en_pipeline.py +22 -2
  243. package/md_cg/test_gain_gate.py +212 -212
  244. package/md_cg/test_govern_directread.py +421 -0
  245. package/md_cg/test_health_scale.py +173 -173
  246. package/md_cg/test_hive_ingest.py +285 -0
  247. package/md_cg/test_hot_cold.py +215 -215
  248. package/md_cg/test_hyperedge.py +245 -245
  249. package/md_cg/test_i26_empty_first_write.py +116 -0
  250. package/md_cg/test_i27_e041_identity.py +128 -0
  251. package/md_cg/test_i28_hotcache_prodpath.py +122 -0
  252. package/md_cg/test_i32_hotcache_env_key.py +218 -0
  253. package/md_cg/test_identity_attribution.py +96 -15
  254. package/md_cg/test_index_durability.py +17 -3
  255. package/md_cg/test_interop.py +95 -0
  256. package/md_cg/test_interop_judgment.py +228 -0
  257. package/md_cg/test_issue39_utf8_stdio.py +273 -0
  258. package/md_cg/test_lifecycle.py +309 -309
  259. package/md_cg/test_linkref.py +306 -306
  260. package/md_cg/test_links_concurrent_write.py +188 -0
  261. package/md_cg/test_lock.py +43 -43
  262. package/md_cg/test_md_access_parity.py +255 -255
  263. package/md_cg/test_md_writepath.py +345 -345
  264. package/md_cg/test_mdstore_search_parity.py +160 -0
  265. package/md_cg/test_merge_upsert.py +168 -0
  266. package/md_cg/test_mr_m2.py +587 -587
  267. package/md_cg/test_mr_m3.py +710 -710
  268. package/md_cg/test_mr_m4.py +485 -485
  269. package/md_cg/test_n123_derive_expiry_chain.py +205 -0
  270. package/md_cg/test_n130_verify_falsified_protect.py +185 -0
  271. package/md_cg/test_n131_merge_gate.py +205 -0
  272. package/md_cg/test_p0.py +250 -250
  273. package/md_cg/test_p1.py +316 -316
  274. package/md_cg/test_p10_identity.py +173 -173
  275. package/md_cg/test_p11_consistency.py +233 -233
  276. package/md_cg/test_p12_metacognition.py +212 -212
  277. package/md_cg/test_p13_encryption.py +241 -241
  278. package/md_cg/test_p14_sustain.py +249 -249
  279. package/md_cg/test_p15_scrub.py +280 -280
  280. package/md_cg/test_p16_self_state.py +301 -301
  281. package/md_cg/test_p17_predict.py +354 -354
  282. package/md_cg/test_p18_whitebox.py +171 -171
  283. package/md_cg/test_p19_migrate_roleplay.py +149 -149
  284. package/md_cg/test_p1x_ref_root.py +160 -0
  285. package/md_cg/test_p20_evolution.py +315 -315
  286. package/md_cg/test_p21_tokens.py +293 -270
  287. package/md_cg/test_p22_theory.py +175 -175
  288. package/md_cg/test_p23_links.py +311 -311
  289. package/md_cg/test_p24_evidence.py +227 -227
  290. package/md_cg/test_p25_weights.py +156 -156
  291. package/md_cg/test_p26_refindex.py +416 -416
  292. package/md_cg/test_p27_docindex.py +16 -7
  293. package/md_cg/test_p28_refcheck.py +305 -305
  294. package/md_cg/test_p29_session_ingest_export.py +354 -333
  295. package/md_cg/test_p2_mcp.py +3 -0
  296. package/md_cg/test_p3.py +11 -2
  297. package/md_cg/test_p30_maintain.py +330 -330
  298. package/md_cg/test_p31_insight.py +534 -534
  299. package/md_cg/test_p32_backfill.py +7 -1
  300. package/md_cg/test_p33_ccg_wiring.py +293 -293
  301. package/md_cg/test_p34_crosscheck.py +331 -331
  302. package/md_cg/test_p35_conditioned_claim.py +252 -252
  303. package/md_cg/test_p36_kp_align.py +230 -230
  304. package/md_cg/test_p37_condition_space.py +248 -248
  305. package/md_cg/test_p38_concurrent_flush.py +102 -0
  306. package/md_cg/test_p38_contextualize.py +273 -273
  307. package/md_cg/test_p39_verify_flow.py +153 -0
  308. package/md_cg/test_p39_vision_evidence.py +369 -369
  309. package/md_cg/test_p40_refine_worklist.py +241 -241
  310. package/md_cg/test_p41_evolve_patrol.py +224 -224
  311. package/md_cg/test_p42_provenance.py +269 -269
  312. package/md_cg/test_p43_pooling.py +412 -398
  313. package/md_cg/test_p44_md_whitebox.py +231 -231
  314. package/md_cg/test_p45_session_identity.py +219 -219
  315. package/md_cg/test_p46_unit_scope.py +272 -272
  316. package/md_cg/test_p47_session_view.py +316 -0
  317. package/md_cg/test_p4_fuzzy.py +223 -223
  318. package/md_cg/test_p5_semantic.py +226 -226
  319. package/md_cg/test_p6_consolidate.py +440 -387
  320. package/md_cg/test_p7_goals_recent.py +202 -202
  321. package/md_cg/test_p8_subgraph_chain.py +200 -200
  322. package/md_cg/test_p9_forget_protect.py +231 -231
  323. package/md_cg/test_predict_beta.py +135 -135
  324. package/md_cg/test_preflight_failclosed.py +100 -100
  325. package/md_cg/test_progressive.py +146 -146
  326. package/md_cg/test_propose_tail_index.py +157 -0
  327. package/md_cg/test_protocol.py +243 -243
  328. package/md_cg/test_reach.py +378 -378
  329. package/md_cg/test_reach_keys.py +201 -201
  330. package/md_cg/test_read_clip.py +141 -141
  331. package/md_cg/test_read_scope_b27.py +277 -0
  332. package/md_cg/test_readcache_default_on.py +168 -0
  333. package/md_cg/test_readcache_precise_inval.py +270 -0
  334. package/md_cg/test_readcache_prodpath.py +203 -0
  335. package/md_cg/test_reconcile_v0.py +294 -0
  336. package/md_cg/test_retr_gates_prodpath.py +140 -0
  337. package/md_cg/test_retr_s1.py +344 -340
  338. package/md_cg/test_retr_s1b.py +276 -209
  339. package/md_cg/test_retr_s3.py +194 -194
  340. package/md_cg/test_retr_s4.py +163 -163
  341. package/md_cg/test_retr_s5.py +200 -200
  342. package/md_cg/test_retr_s6.py +157 -157
  343. package/md_cg/test_retr_s7.py +392 -384
  344. package/md_cg/test_retr_s8_time.py +369 -316
  345. package/md_cg/test_retr_s9_edges.py +286 -286
  346. package/md_cg/test_retr_s9_entity_ctx.py +9 -3
  347. package/md_cg/test_retr_score_once.py +208 -0
  348. package/md_cg/test_review_conformance.py +367 -367
  349. package/md_cg/test_review_onepass.py +170 -0
  350. package/md_cg/test_role_views.py +354 -354
  351. package/md_cg/test_rrf_graph_seed_cache.py +154 -0
  352. package/md_cg/test_security_audit.py +155 -0
  353. package/md_cg/test_security_audit_b26.py +161 -0
  354. package/md_cg/test_security_audit_v21.py +250 -0
  355. package/md_cg/test_sem_noise.py +242 -242
  356. package/md_cg/test_semantic_canonical.py +16 -2
  357. package/md_cg/test_session_isolation.py +168 -0
  358. package/md_cg/test_snapshot_autoclose.py +187 -0
  359. package/md_cg/test_subproc_encoding.py +192 -192
  360. package/md_cg/test_sustain_mutual.py +153 -153
  361. package/md_cg/test_tail_watermark_race.py +208 -0
  362. package/md_cg/test_tasks.py +409 -409
  363. package/md_cg/test_tenant_env_override_warn.py +139 -0
  364. package/md_cg/test_tenant_registry_corrupt_warn.py +151 -0
  365. package/md_cg/test_tool_face.py +189 -189
  366. package/md_cg/test_transfer.py +180 -180
  367. package/md_cg/test_trust.py +361 -361
  368. package/md_cg/test_twophase.py +286 -286
  369. package/md_cg/test_v14_fixes.py +38 -20
  370. package/md_cg/test_validity_filter.py +280 -280
  371. package/md_cg/test_verify_answer.py +138 -138
  372. package/md_cg/test_verify_dirty_reconcile.py +157 -0
  373. package/md_cg/test_wisdom_md_store.py +292 -292
  374. package/md_cg/test_writelimit.py +197 -197
  375. package/md_cg/test_writepipe.py +214 -214
  376. package/md_cg/theory.py +6 -3
  377. package/md_cg/tokens.py +85 -14
  378. package/md_cg/tool_face.py +260 -260
  379. package/md_cg/trust.py +986 -950
  380. package/md_cg/twophase.py +231 -231
  381. package/md_cg/units.py +668 -667
  382. package/md_cg/vision_evidence.py +667 -666
  383. package/md_cg/weights.py +624 -624
  384. package/md_cg/whitebox.py +527 -527
  385. package/md_cg/whitebox_kb/__init__.py +37 -37
  386. package/md_cg/whitebox_kb/aeis_core/__init__.py +42 -42
  387. package/md_cg/whitebox_kb/aeis_core/semantic.py +280 -280
  388. package/md_cg/whitebox_kb/aeis_core/textutil.py +13 -13
  389. package/md_cg/whitebox_kb/engine.py +310 -310
  390. package/md_cg/whitebox_kb/seed_knowledge//346/231/272/350/203/275/350/256/2723.4.md +5260 -5260
  391. package/md_cg/whitebox_kb/wisdom/browser_units.py +2631 -2631
  392. package/md_cg/whitebox_kb/wisdom/causal_discover.py +432 -432
  393. package/md_cg/whitebox_kb/wisdom/chat_engine.py +1506 -1506
  394. package/md_cg/whitebox_kb/wisdom/compiler_code_units.py +3033 -3033
  395. package/md_cg/whitebox_kb/wisdom/condition_algebra.py +112 -112
  396. package/md_cg/whitebox_kb/wisdom/condition_frame.py +315 -315
  397. package/md_cg/whitebox_kb/wisdom/condition_kb.py +108 -108
  398. package/md_cg/whitebox_kb/wisdom/conflict_map.json +4445 -4445
  399. package/md_cg/whitebox_kb/wisdom/core/lexer.py +512 -512
  400. package/md_cg/whitebox_kb/wisdom/core/name_checker.py +1023 -1023
  401. package/md_cg/whitebox_kb/wisdom/cspmn.py +258 -258
  402. package/md_cg/whitebox_kb/wisdom/csre.py +264 -264
  403. package/md_cg/whitebox_kb/wisdom/danmaku_audit.py +252 -252
  404. package/md_cg/whitebox_kb/wisdom/distilled_condition_units.json +6417 -6417
  405. package/md_cg/whitebox_kb/wisdom/docs/WB-EVAL-20260902b.json +1950 -1950
  406. package/md_cg/whitebox_kb/wisdom/docs/WB-EVAL-20260902c.json +1296 -1296
  407. package/md_cg/whitebox_kb/wisdom/docs/whitebox_capability_graph_demo.json +59 -59
  408. package/md_cg/whitebox_kb/wisdom/graph_db_units.py +3089 -3089
  409. package/md_cg/whitebox_kb/wisdom/knowledge_points.py +318 -318
  410. package/md_cg/whitebox_kb/wisdom/md_access.py +470 -470
  411. package/md_cg/whitebox_kb/wisdom/md_store.py +276 -251
  412. package/md_cg/whitebox_kb/wisdom/migrate_wisdom.py +476 -476
  413. package/md_cg/whitebox_kb/wisdom/multilang_ir.py +128 -124
  414. package/md_cg/whitebox_kb/wisdom/navigate.py +248 -248
  415. package/md_cg/whitebox_kb/wisdom/neural_retrieve.py +224 -224
  416. package/md_cg/whitebox_kb/wisdom/os_units.py +2735 -2735
  417. package/md_cg/whitebox_kb/wisdom/pattern_separation.py +376 -376
  418. package/md_cg/whitebox_kb/wisdom/prereq_map.json +364 -364
  419. package/md_cg/whitebox_kb/wisdom/python_code_units.py +2766 -2766
  420. package/md_cg/whitebox_kb/wisdom/role_solidified.json +7 -7
  421. package/md_cg/whitebox_kb/wisdom/route_memory.py +244 -244
  422. package/md_cg/whitebox_kb/wisdom/scene_reconstruction.py +148 -148
  423. package/md_cg/whitebox_kb/wisdom/snr_report.json +40 -40
  424. package/md_cg/whitebox_kb/wisdom/test_code_compose_domains.py +7541 -7541
  425. package/md_cg/whitebox_kb/wisdom/test_compiler_self_bootstrap.py +354 -354
  426. package/md_cg/whitebox_kb/wisdom/test_ecosystem_assembly.py +158 -158
  427. package/md_cg/whitebox_kb/wisdom/test_ecosystem_demos.py +193 -193
  428. package/md_cg/whitebox_kb/wisdom/test_graph_db.py +292 -292
  429. package/md_cg/whitebox_kb/wisdom/test_python_self_bootstrap.py +160 -160
  430. package/md_cg/whitebox_kb/wisdom/trigger_words_index.json +4366 -4366
  431. package/md_cg/whitebox_kb/wisdom/verifier.py +1297 -1297
  432. package/md_cg/writelimit.py +356 -356
  433. package/md_cg/writepipe.py +20 -8
  434. package/package.json +101 -96
  435. package/skills/plugin.json +54 -54
  436. package/skills/skills/designer-perspective/SKILL.md +158 -158
  437. package/skills/skills/designer-perspective/references/01-observation-position.md +66 -66
  438. package/skills/skills/designer-perspective/references/02-structure-recognition.md +62 -62
  439. package/skills/skills/designer-perspective/references/03-direction-judgment.md +55 -55
  440. package/skills/skills/designer-perspective/references/04-qualification-verdict.md +72 -72
  441. package/skills/skills/designer-perspective/references/05-condition-attribution.md +74 -74
  442. package/skills/skills/designer-perspective/scripts/designer.py +545 -545
  443. package/skills/skills/designer-perspective/tests/cases.jsonl +17 -17
  444. package/skills/skills/designer-perspective/tests/selftest.py +61 -61
  445. package/skills/skills/lingshu-browser/SKILL.md +60 -60
  446. package/skills/skills/lingshu-compiler/SKILL.md +56 -56
  447. package/skills/skills/lingshu-compiler/units/analyze-type-infer/SKILL.md +45 -45
  448. package/skills/skills/lingshu-compiler/units/check-name-real/SKILL.md +45 -45
  449. package/skills/skills/lingshu-compiler/units/compile-assign/SKILL.md +45 -45
  450. package/skills/skills/lingshu-compiler/units/compile-expr-tree/SKILL.md +45 -45
  451. package/skills/skills/lingshu-compiler/units/compile-full-pipeline/SKILL.md +45 -45
  452. package/skills/skills/lingshu-compiler/units/compile-func-def/SKILL.md +45 -45
  453. package/skills/skills/lingshu-compiler/units/compile-if-then/SKILL.md +45 -45
  454. package/skills/skills/lingshu-compiler/units/compile-logic-expr/SKILL.md +45 -45
  455. package/skills/skills/lingshu-compiler/units/compile-recursive/SKILL.md +45 -45
  456. package/skills/skills/lingshu-compiler/units/compile-scope/SKILL.md +45 -45
  457. package/skills/skills/lingshu-compiler/units/compile-type-check/SKILL.md +45 -45
  458. package/skills/skills/lingshu-compiler/units/compile-while/SKILL.md +45 -45
  459. package/skills/skills/lingshu-compiler/units/compiler-0010c4bf/SKILL.md +45 -45
  460. package/skills/skills/lingshu-compiler/units/compiler-0355bffb/SKILL.md +45 -45
  461. package/skills/skills/lingshu-compiler/units/compiler-0361708a/SKILL.md +45 -45
  462. package/skills/skills/lingshu-compiler/units/compiler-054a0414/SKILL.md +45 -45
  463. package/skills/skills/lingshu-compiler/units/compiler-05a1691a/SKILL.md +45 -45
  464. package/skills/skills/lingshu-compiler/units/compiler-05eeed1e/SKILL.md +45 -45
  465. package/skills/skills/lingshu-compiler/units/compiler-0622a1f6/SKILL.md +45 -45
  466. package/skills/skills/lingshu-compiler/units/compiler-08b54217/SKILL.md +45 -45
  467. package/skills/skills/lingshu-compiler/units/compiler-0a62b70c/SKILL.md +45 -45
  468. package/skills/skills/lingshu-compiler/units/compiler-0ab24d00/SKILL.md +45 -45
  469. package/skills/skills/lingshu-compiler/units/compiler-0e093688/SKILL.md +45 -45
  470. package/skills/skills/lingshu-compiler/units/compiler-0e82b966/SKILL.md +45 -45
  471. package/skills/skills/lingshu-compiler/units/compiler-0ee9b9b9/SKILL.md +45 -45
  472. package/skills/skills/lingshu-compiler/units/compiler-0f3787e6/SKILL.md +45 -45
  473. package/skills/skills/lingshu-compiler/units/compiler-1028685f/SKILL.md +45 -45
  474. package/skills/skills/lingshu-compiler/units/compiler-11897630/SKILL.md +45 -45
  475. package/skills/skills/lingshu-compiler/units/compiler-16661b9b/SKILL.md +45 -45
  476. package/skills/skills/lingshu-compiler/units/compiler-1f722303/SKILL.md +45 -45
  477. package/skills/skills/lingshu-compiler/units/compiler-25be1262/SKILL.md +45 -45
  478. package/skills/skills/lingshu-compiler/units/compiler-2a76ba07/SKILL.md +45 -45
  479. package/skills/skills/lingshu-compiler/units/compiler-2dbea54a/SKILL.md +45 -45
  480. package/skills/skills/lingshu-compiler/units/compiler-2df52f16/SKILL.md +45 -45
  481. package/skills/skills/lingshu-compiler/units/compiler-2ec2c9d2/SKILL.md +45 -45
  482. package/skills/skills/lingshu-compiler/units/compiler-2f8c8f39/SKILL.md +45 -45
  483. package/skills/skills/lingshu-compiler/units/compiler-38378ac7/SKILL.md +45 -45
  484. package/skills/skills/lingshu-compiler/units/compiler-39457d2e/SKILL.md +45 -45
  485. package/skills/skills/lingshu-compiler/units/compiler-39fb5926/SKILL.md +45 -45
  486. package/skills/skills/lingshu-compiler/units/compiler-47f4fbfa/SKILL.md +45 -45
  487. package/skills/skills/lingshu-compiler/units/compiler-4a8cd1f1/SKILL.md +45 -45
  488. package/skills/skills/lingshu-compiler/units/compiler-4b230b6b/SKILL.md +45 -45
  489. package/skills/skills/lingshu-compiler/units/compiler-4cbbda95/SKILL.md +45 -45
  490. package/skills/skills/lingshu-compiler/units/compiler-4d5a68ff/SKILL.md +45 -45
  491. package/skills/skills/lingshu-compiler/units/compiler-56dc9bdd/SKILL.md +45 -45
  492. package/skills/skills/lingshu-compiler/units/compiler-57e76ebe/SKILL.md +45 -45
  493. package/skills/skills/lingshu-compiler/units/compiler-61ff016b/SKILL.md +45 -45
  494. package/skills/skills/lingshu-compiler/units/compiler-63eae588/SKILL.md +45 -45
  495. package/skills/skills/lingshu-compiler/units/compiler-64c4224b/SKILL.md +45 -45
  496. package/skills/skills/lingshu-compiler/units/compiler-66377955/SKILL.md +45 -45
  497. package/skills/skills/lingshu-compiler/units/compiler-674ab4f6/SKILL.md +45 -45
  498. package/skills/skills/lingshu-compiler/units/compiler-6af76fd6/SKILL.md +45 -45
  499. package/skills/skills/lingshu-compiler/units/compiler-6d979db2/SKILL.md +45 -45
  500. package/skills/skills/lingshu-compiler/units/compiler-6de326c0/SKILL.md +45 -45
  501. package/skills/skills/lingshu-compiler/units/compiler-6f1c0eea/SKILL.md +45 -45
  502. package/skills/skills/lingshu-compiler/units/compiler-724d6c9b/SKILL.md +45 -45
  503. package/skills/skills/lingshu-compiler/units/compiler-7690177f/SKILL.md +45 -45
  504. package/skills/skills/lingshu-compiler/units/compiler-7b229a7d/SKILL.md +45 -45
  505. package/skills/skills/lingshu-compiler/units/compiler-7f471b3a/SKILL.md +45 -45
  506. package/skills/skills/lingshu-compiler/units/compiler-815cad08/SKILL.md +45 -45
  507. package/skills/skills/lingshu-compiler/units/compiler-83c61634/SKILL.md +45 -45
  508. package/skills/skills/lingshu-compiler/units/compiler-8c798c81/SKILL.md +45 -45
  509. package/skills/skills/lingshu-compiler/units/compiler-8dd747ac/SKILL.md +45 -45
  510. package/skills/skills/lingshu-compiler/units/compiler-90324d0a/SKILL.md +45 -45
  511. package/skills/skills/lingshu-compiler/units/compiler-94b8d72d/SKILL.md +45 -45
  512. package/skills/skills/lingshu-compiler/units/compiler-94f12231/SKILL.md +45 -45
  513. package/skills/skills/lingshu-compiler/units/compiler-95937c16/SKILL.md +45 -45
  514. package/skills/skills/lingshu-compiler/units/compiler-98a5625b/SKILL.md +45 -45
  515. package/skills/skills/lingshu-compiler/units/compiler-98b4c42f/SKILL.md +45 -45
  516. package/skills/skills/lingshu-compiler/units/compiler-98e3894a/SKILL.md +45 -45
  517. package/skills/skills/lingshu-compiler/units/compiler-9bdfe4b8/SKILL.md +45 -45
  518. package/skills/skills/lingshu-compiler/units/compiler-9d9b4e83/SKILL.md +45 -45
  519. package/skills/skills/lingshu-compiler/units/compiler-9f7be5ad/SKILL.md +45 -45
  520. package/skills/skills/lingshu-compiler/units/compiler-a6daf076/SKILL.md +45 -45
  521. package/skills/skills/lingshu-compiler/units/compiler-a7995a0c/SKILL.md +45 -45
  522. package/skills/skills/lingshu-compiler/units/compiler-a8399248/SKILL.md +45 -45
  523. package/skills/skills/lingshu-compiler/units/compiler-aabbd099/SKILL.md +45 -45
  524. package/skills/skills/lingshu-compiler/units/compiler-afe169d8/SKILL.md +45 -45
  525. package/skills/skills/lingshu-compiler/units/compiler-b0678bda/SKILL.md +45 -45
  526. package/skills/skills/lingshu-compiler/units/compiler-b09bd196/SKILL.md +45 -45
  527. package/skills/skills/lingshu-compiler/units/compiler-b1396e23/SKILL.md +45 -45
  528. package/skills/skills/lingshu-compiler/units/compiler-b9b31ce0/SKILL.md +45 -45
  529. package/skills/skills/lingshu-compiler/units/compiler-c10264a7/SKILL.md +45 -45
  530. package/skills/skills/lingshu-compiler/units/compiler-cb1e8e4b/SKILL.md +45 -45
  531. package/skills/skills/lingshu-compiler/units/compiler-ccafd438/SKILL.md +45 -45
  532. package/skills/skills/lingshu-compiler/units/compiler-cda9c262/SKILL.md +45 -45
  533. package/skills/skills/lingshu-compiler/units/compiler-ce648068/SKILL.md +45 -45
  534. package/skills/skills/lingshu-compiler/units/compiler-cf5776a4/SKILL.md +45 -45
  535. package/skills/skills/lingshu-compiler/units/compiler-d974e5d3/SKILL.md +45 -45
  536. package/skills/skills/lingshu-compiler/units/compiler-e3979fd3/SKILL.md +45 -45
  537. package/skills/skills/lingshu-compiler/units/compiler-eb1cf2b5/SKILL.md +45 -45
  538. package/skills/skills/lingshu-compiler/units/compiler-ecb30d5b/SKILL.md +45 -45
  539. package/skills/skills/lingshu-compiler/units/compiler-f8c8b24b/SKILL.md +45 -45
  540. package/skills/skills/lingshu-compiler/units/compiler-f99fedbe/SKILL.md +45 -45
  541. package/skills/skills/lingshu-compiler/units/compiler-fa8ff5f7/SKILL.md +45 -45
  542. package/skills/skills/lingshu-compiler/units/compiler-fe1b058d/SKILL.md +45 -45
  543. package/skills/skills/lingshu-compiler/units/lex-chinese-program/SKILL.md +45 -45
  544. package/skills/skills/lingshu-compiler/units/lex-dao-de-jing/SKILL.md +45 -45
  545. package/skills/skills/lingshu-compiler/units/lex-nine-chapters/SKILL.md +45 -45
  546. package/skills/skills/lingshu-compiler/units/vm-arithmetic/SKILL.md +45 -45
  547. package/skills/skills/lingshu-compiler/units/vm-array-ops/SKILL.md +45 -45
  548. package/skills/skills/lingshu-compiler/units/vm-closure-call/SKILL.md +45 -45
  549. package/skills/skills/lingshu-compiler/units/vm-closure-create/SKILL.md +45 -45
  550. package/skills/skills/lingshu-compiler/units/vm-compare/SKILL.md +45 -45
  551. package/skills/skills/lingshu-compiler/units/vm-cond-jump/SKILL.md +45 -45
  552. package/skills/skills/lingshu-compiler/units/vm-cond-space/SKILL.md +45 -45
  553. package/skills/skills/lingshu-compiler/units/vm-exception/SKILL.md +45 -45
  554. package/skills/skills/lingshu-compiler/units/vm-func-call/SKILL.md +45 -45
  555. package/skills/skills/lingshu-compiler/units/vm-loop-run/SKILL.md +45 -45
  556. package/skills/skills/lingshu-compiler/units/vm-profiling/SKILL.md +45 -45
  557. package/skills/skills/lingshu-compiler/units/vm-refcount/SKILL.md +45 -45
  558. package/skills/skills/lingshu-compiler/units/vm-run-loop/SKILL.md +45 -45
  559. package/skills/skills/lingshu-compiler/units/vm-short-circuit/SKILL.md +45 -45
  560. package/skills/skills/lingshu-compiler/units/vm-stack-guard/SKILL.md +45 -45
  561. package/skills/skills/lingshu-compiler/units/vm-stack-ops/SKILL.md +45 -45
  562. package/skills/skills/lingshu-compiler/units/vm-trust-accum/SKILL.md +45 -45
  563. package/skills/skills/lingshu-graph/SKILL.md +63 -63
  564. package/skills/skills/lingshu-net/SKILL.md +48 -48
  565. package/skills/skills/lingshu-os/SKILL.md +64 -64
  566. package/skills/skills/lingshu-pylang/SKILL.md +71 -71
  567. package/src/bridge.ts +33 -0
  568. package/src/hooks.ts +38 -2
  569. package/src/index.ts +526 -518
  570. package/src/lib/datapath.ts +326 -326
  571. package/src/lib/mdcg_client.ts +413 -413
  572. package/src/lib/mutual.ts +428 -428
  573. package/src/lib/prompt_safety.ts +62 -62
  574. package/src/lib/python_path.ts +71 -71
  575. package/src/lib/roleplay_web.ts +116 -29
  576. package/src/lib/token_store.ts +13 -3
  577. package/src/tools.ts +212 -212
  578. package/zcode/AGENTS.md +11 -3
  579. package/zcode/README.md +41 -41
  580. /package/docs/{mdcg → hive}//344/270/273/344/273/243/347/220/206/345/255/220/344/273/243/347/220/206/350/256/260/345/277/206/346/236/266/346/236/204/350/256/276/350/256/241.md" +0 -0
package/md_cg/mdcos.py CHANGED
@@ -30,13 +30,13 @@ from .mdcg import (MdCG, expand_query_terms, bigrams, normalize_en, STATE_ACCEPT
30
30
  TIER_BUCKET_SCAN, TIER_GLOBAL_LIKE, TIER_GLOBAL_SCAN,
31
31
  GLOBAL_CAP, expand_query_terms_weighted,
32
32
  expand_query_terms_llm, en_zh_bigrams, semantic_on,
33
- cut_by_relevance)
33
+ cut_by_relevance, apply_retrieval_gates)
34
34
  from . import (nodefile, routing, chain, subgraph, forgetting, protect,
35
35
  identity, consistency, metacognition, crypto, sustain,
36
36
  self_state, predict, evolution, weights, pooling,
37
37
  writelimit, reach, trust, roleviews)
38
38
  from .fsutil import (FileLock, atomic_write, append_jsonl, read_jsonl,
39
- count_jsonl)
39
+ read_jsonl_tail, count_jsonl, publish)
40
40
  from .security import (Principal, TenantRegistry, AccessDenied,
41
41
  SENSITIVITY_ORDER, DEFAULT_SENSITIVITY, _rank)
42
42
 
@@ -326,6 +326,46 @@ class MdCGOS(MdCG):
326
326
  os.makedirs(self.trash_dir, exist_ok=True)
327
327
  self._audit_writes = 0 # 进程内写入计数(轮转探测节流,稳态零 stat)
328
328
  self._audit_index = None # 归档索引缓存(懒加载)
329
+ # 审核队列增量对账缓存(issue #32):inbox/decisions 均为 append-only
330
+ # (写点全在 strict 锁内经 append_jsonl,无轮转/截断),propose 的
331
+ # 幂等对账从「每条全量重读 O(M+D)」改为「stat 尾部增量」——跨进程
332
+ # append 由 stat 自动并入,无需显式失效。
333
+ self._inbox_idx = {} # phash → [(pid, node_id)] 文件序
334
+ self._inbox_idx_size = -1 # 已并入的 inbox 字节偏移(扫描水位)
335
+ self._inbox_idx_mtime = -1
336
+ self._pid_st = {} # pid → 最新裁决记录
337
+ self._pid_st_size = -1 # 已并入的 decisions 字节偏移
338
+ self._pid_st_mtime = -1
339
+ # review 治理面全记录增量缓存(issue #35):review_stats/review_list/
340
+ # review_decide/_cascade_dedup/verify_review_record 单次调用内曾各自
341
+ # 全量重读同一 jsonl(inbox 2 遍 + decisions 1-4 遍)——改为 stat 尾部
342
+ # 增量的全记录缓存(与 _pid_status 同款对账),调用方内存线性扫。
343
+ # 注意:缓存列表为内部状态,调用方只读不得变更。
344
+ self._inbox_recs = [] # inbox 全记录(文件序)
345
+ self._inbox_recs_size = -1
346
+ self._inbox_recs_mtime = -1
347
+ self._dec_recs = [] # decisions 全记录(文件序)
348
+ self._dec_recs_size = -1
349
+ self._dec_recs_mtime = -1
350
+ # 热缓存挂载(issue #28,2026-09-23):读侧(search_rrf)与失效侧
351
+ # (writepipe)早已接好,唯独无人调 attach——生产恒 None,重复 query
352
+ # 无法命中。默认关(env 未设 → get(self) 恒 None,零变更纪律);
353
+ # MDCG_HOTCACHE=1 显式启用,容量经 MDCG_HOTCACHE_MAX_NODES/MAX_QUERIES
354
+ # 可配(缺省 256 对 567 节点级库会持续 LRU 淘汰,大库请调大)。
355
+ if os.environ.get("MDCG_HOTCACHE") == "1":
356
+ from . import hotcache as _hc
357
+ _hc.attach(self)
358
+ # 检索读缓存挂载(issue #31 P2,批次 21):MdStore 理论落地——节点
359
+ # 文件解析产物常驻(567 池实测 572 次 open/查询 → 缓存后 ~0),写失
360
+ # 效走 _dirty 代际哨兵(所有写路径必然标脏,无漏挂面)。
361
+ # 默认**开**(issue #31 后续修复):生产检索入口(本类 search →
362
+ # _read_many)默认态每查询全池 open+2×realpath+frontmatter 解析,
363
+ # 3300 池实测 ~602ms/查询、万级外推 ~2s+(O(n) 线性);开关早已
364
+ # 存在但默认关 → 生产恒不装配。MDCG_READ_CACHE=0 显式关闭(opt-out
365
+ # 退出阀:跨进程改写须本进程立即可见 / 内存受限的部署回到旧行为)。
366
+ from . import readcache as _rc
367
+ if _rc.enabled():
368
+ _rc.install(self)
329
369
 
330
370
  # ================= 6. payload-free 审计 =================
331
371
 
@@ -442,7 +482,7 @@ class MdCGOS(MdCG):
442
482
  name = "_audit.%06d.jsonl" % self._next_shard_seq()
443
483
  dst = os.path.join(self.audit_archive, name)
444
484
  try:
445
- os.replace(self.audit_log, dst)
485
+ publish(self.audit_log, dst) # Windows 短重试:AV 短锁不误判抢输
446
486
  except OSError:
447
487
  return None # 抢输(文件已被移走)→ 让位,不报错
448
488
  scale = self._audit_count_shard(dst, size) # 分档:有界扫描 / 只读量级
@@ -565,10 +605,22 @@ class MdCGOS(MdCG):
565
605
  """在父类 add 之上:写入 role(frontmatter + 索引),默认 role=None(知识)。"""
566
606
  if role is not None:
567
607
  kw["role"] = role
608
+ # 会话归属必须落 frontmatter:本文件 session 过滤的文档即写「frontmatter.session,
609
+ # 写入时自动落盘」,但父类 add 的 fm 固定字典里没有该键、_stage 只把它放进索引 →
610
+ # 实际只有索引带归属,任何 rebuild_index() 都会抹掉(实测 73 节点里 56 个无
611
+ # frontmatter.session,而 _audit.jsonl 的 add 事件却 152/152 都记了 session:
612
+ # 写入口径与落盘口径脱钩)。与 role 同款:显式给就尊重,否则取 self.session
613
+ # (MdCGSecure 由 principal.session 注入,mcp_server 按请求切换 cg.session)。
614
+ _sess = kw.get("session") or getattr(self, "session", None)
615
+ if _sess and not kw.get("session"):
616
+ kw["session"] = _sess
568
617
  nid = super().add(node_id, content, layer=layer, **kw)
569
618
  e = self.index["nodes"].get(nid)
570
619
  if e is not None and role is not None:
571
620
  e["role"] = role
621
+ if e is not None and _sess:
622
+ e["session"] = _sess
623
+ if e is not None and (role is not None or _sess):
572
624
  self._dirty[nid] = e
573
625
  self._audit("add", nid, layer=layer, role=role,
574
626
  payload_hash=_sig(content))
@@ -608,7 +660,8 @@ class MdCGOS(MdCG):
608
660
  for e in self.index["nodes"].values():
609
661
  if e.get("layer") in ("rejected", "unresolved", "goals"):
610
662
  continue # 负记忆走覆盖标记;目标只做定向,都不进正排
611
- if session and e.get("session") != session:
663
+ # '"*"' = 显式跨会话(读遍所有会话);缺省 None 同义(见 stg.timeline)
664
+ if session and session != "*" and e.get("session") != session:
612
665
  continue
613
666
  if e.get("branch_id") not in (None, branch):
614
667
  continue
@@ -705,6 +758,10 @@ class MdCGOS(MdCG):
705
758
  详见 _candidates。
706
759
  """
707
760
  q = (query or "").strip()
761
+ # 批次 15 统一口径(unify.py):任意语言 query → 标准原子序列;
762
+ # 与 MdCG.search/search_rrf 三入口同口径(MDCG_UNIFY_QUERY=0 可关)
763
+ from .semantic.unify import unify_query
764
+ q = unify_query(q)
708
765
  if not q:
709
766
  return [], {"tier": None, "reason": "empty_query", "scanned": 0}
710
767
  pool_cfg = pooling.resolve(pooling.from_env(pools))
@@ -738,13 +795,34 @@ class MdCGOS(MdCG):
738
795
  if context is not None:
739
796
  ctx = context if isinstance(context, dict) else {}
740
797
  route_bucket = routing.bucket_dir(routing.route_key(ctx, ctx.get("tags")))
798
+ # 与 MdCG.search 同一纪律(X2):无域信号时不施加桶约束,否则 route_key
799
+ # 回落的 orphan 会被当成真桶,下面 T0/T1 阶段读到该桶并因 valid>=min_results
800
+ # 直接 return,**全量阶梯永不执行** —— 标了 domain: 的节点整片不可见。
801
+ # 本覆写才是生产路径(MdCGSecure → 本类):缺此条则 MCP 端依旧丢召回。
802
+ if route_bucket == routing.ORPHAN:
803
+ route_bucket = None
741
804
  big_domain = routing.big_domain_classify(terms)
742
805
  big_scores = routing.big_domain_score_breakdown(terms)
806
+ # S1/S1b/S2 收敛 + S4 审计——与 MdCG.search 共享同一实现(issue #25:
807
+ # 本覆写此前完全不含门控,生产路径(MdCGSecure → 本类)从未生效——
808
+ # 开关显式打开也不生效、gates 审计不出现、scanned 恒全表)。
809
+ # 收敛在 _read_many 之前 → scanned 真实下降;gates 非空才落键
810
+ # (默认关时 meta 键集合与改动前逐字节一致,零变更纪律)。
811
+ entries, gates = apply_retrieval_gates(
812
+ entries, terms, big_domain, context, min_results)
813
+ if gates:
814
+ stat["gates"] = gates
743
815
  neg_coverage = self._neg_coverage(terms) if include_neg else []
744
816
 
745
- # 生效条件:对 docs 调 self._score 后,若其中分数 >0 的条数达到(search 作用域内的)min_results 就返回 self._emit(scored, k, tier, stat, route_bucket, record, len(docs), ...),否则返回 None。
746
- def try_stage(docs, tier):
747
- scored = self._score(docs, q, qb, pool_cfg)
817
+ # 生效条件:scored 为 None 时对 docs 调 self._score(docs, q, qb, pool_cfg),非 None 时直接以之为打分结果;分数 >0 的条数达到(search 作用域内的)min_results 就返回 self._emit(scored, k, tier, stat, route_bucket, record, len(docs), ...),否则返回 None。
818
+ def try_stage(docs, tier, scored=None):
819
+ # P2-4 回移(v7 N51 留档修复,缺陷迭代第 14 轮):支持传入已打分
820
+ # 结果——此前 T2/T3 均为「打分截断后又对 picked 重打」(600 池实测
821
+ # _score 4 调 [0,0,600,500]=1100 文档,500/1100≈45% 冗余)。与基类
822
+ # try_stage(mdcg.py P2-4,批次 31)同模板;_score 在单次 search 内
823
+ # 是纯函数(docs/q/qb/pool_cfg 均不变),透传不改变任何分值与输出。
824
+ if scored is None:
825
+ scored = self._score(docs, q, qb, pool_cfg)
748
826
  valid = sum(1 for _, s in scored if s > 0)
749
827
  if valid >= min_results:
750
828
  return self._emit(scored, k, tier, stat, route_bucket, record,
@@ -752,6 +830,22 @@ class MdCGOS(MdCG):
752
830
  big_domain, big_scores, pool_cfg)
753
831
  return None
754
832
 
833
+ def _prescored(docs, scored_all):
834
+ """P2-4 透传形态:按 node_id 键表把 docs 映射回已打分的
835
+ (card, score) 原对(恰为 _score 对该 docs 的输出,picked 序)。
836
+
837
+ 键口径:scored 元素是 (card, score),card["id"] = fm.get("id")
838
+ or e["path"],恰为 doc_key(doc)[0](V21-6 取 [0] 作键同源)——
839
+ **不可**对 scored 元素调 pooling.doc_key(其形参是 (entry, fm,
840
+ content) 三元组;基类 reach 分支旧模板即此形态错误,MDCG_REACH=1
841
+ 时 KeyError: 1,见 mdcg.py 同修)。**不可**自组 (doc, score) 对
842
+ (_emit 排序键读 x[0]["frontmatter"],x[0] 须是 card dict)。
843
+ docs 必为 scored_all 的子序列(cut_by_relevance 只选不造),
844
+ 键表必命中。
845
+ """
846
+ smap = {c.get("id"): (c, s) for c, s in scored_all}
847
+ return [smap[pooling.doc_key(d)[0]] for d in docs]
848
+
755
849
  if route_bucket:
756
850
  in_bucket = [e for e in entries if e.get("bucket") == route_bucket]
757
851
  if in_bucket:
@@ -790,11 +884,15 @@ class MdCGOS(MdCG):
790
884
  stat["cap"] = GLOBAL_CAP
791
885
  # 与 T2 **无条件**同序调用(不可按 cap 短路:cut_by_relevance 还会写
792
886
  # 池账 pool_taken/cands/lost,短路会让 meta.pools.taken 缺失 → test_p43(13) 红)
793
- hits_r, _rep = cut_by_relevance(hits_r, self._score(hits_r, q, qb, pool_cfg),
887
+ scored_r = self._score(hits_r, q, qb, pool_cfg)
888
+ hits_r, _rep = cut_by_relevance(hits_r, scored_r,
794
889
  GLOBAL_CAP, pools=pool_cfg,
795
890
  key_of=pooling.doc_key, stat=stat)
796
891
  pooling.record_audit(stat, _rep)
797
- out = try_stage(hits_r, TIER_GLOBAL_LIKE)
892
+ # P2-4:打分结果经 scored 透传,不再对同一 hits_r 二次 _score
893
+ # (与基类 reach 分支同模板)。
894
+ out = try_stage(hits_r, TIER_GLOBAL_LIKE,
895
+ scored=_prescored(hits_r, scored_r))
798
896
  if out:
799
897
  return out
800
898
  # 收敛阶段未产出结果 → 继续走下方全量 T2/T3;此时必须**改写 reach 审计**,
@@ -815,22 +913,27 @@ class MdCGOS(MdCG):
815
913
  or (semantic_on() and d[1].get("semantic"))]
816
914
  stat["pre_cap"] = len(hits)
817
915
  stat["cap"] = GLOBAL_CAP
818
- hits, _rep = cut_by_relevance(hits, self._score(hits, q, qb, pool_cfg),
916
+ scored_h = self._score(hits, q, qb, pool_cfg)
917
+ hits, _rep = cut_by_relevance(hits, scored_h,
819
918
  GLOBAL_CAP, pools=pool_cfg,
820
919
  key_of=pooling.doc_key, stat=stat)
821
920
  pooling.record_audit(stat, _rep)
822
- out = try_stage(hits, TIER_GLOBAL_LIKE)
921
+ # P2-4:截断打分结果透传,T2 腿不再对 hits 二次 _score。
922
+ out = try_stage(hits, TIER_GLOBAL_LIKE,
923
+ scored=_prescored(hits, scored_h))
823
924
  if out:
824
925
  return out
825
926
 
826
927
  stat["pre_cap"] = len(docs_all)
827
928
  stat["cap"] = GLOBAL_CAP
828
- picked, _rep = cut_by_relevance(docs_all,
829
- self._score(docs_all, q, qb, pool_cfg),
929
+ scored_a = self._score(docs_all, q, qb, pool_cfg)
930
+ picked, _rep = cut_by_relevance(docs_all, scored_a,
830
931
  GLOBAL_CAP, pools=pool_cfg,
831
932
  key_of=pooling.doc_key, stat=stat)
832
933
  pooling.record_audit(stat, _rep)
833
- scored = self._score(picked, q, qb, pool_cfg)
934
+ # P2-4:T3 不再对 picked 重打(600 池实测此腿冗余 500 文档/查询,
935
+ # GLOBAL_CAP=500 截断后 picked ⊆ docs_all,键表回填即得终榜打分)。
936
+ scored = _prescored(picked, scored_a)
834
937
  return self._emit(scored, k, TIER_GLOBAL_SCAN, stat, route_bucket,
835
938
  record, len(picked), judge,
836
939
  context, neg_coverage, big_domain, big_scores, pool_cfg)
@@ -886,6 +989,16 @@ class MdCGOS(MdCG):
886
989
  return []
887
990
  ctx = context if isinstance(context, dict) else {}
888
991
  b = routing.bucket_dir(routing.route_key(ctx, ctx.get("tags")))
992
+ # 「无域信号」等价于「没声明条件」:条件桶路此时必须**弃权**,不能把
993
+ # route_key 回落的 orphan 当成一个真桶去硬过滤——否则凡是被标了 domain:
994
+ # (因而落在 cond_* 目录)的节点,对这条查询就整片不可见。
995
+ # 实测(带 context 但不含 domain / observation_position):弃权前只扫
996
+ # orphan 桶 10 个节点、目标节点召回丢失(T1_bucket_scan/scanned=10);
997
+ # 弃权后由词法/实体/图路正常召回(T2_global_like/scanned=75)。
998
+ # 与上面 context is None 的既有早返回同构,也贴合 test_p0 的
999
+ # 「桶路由平均 recall@10 ≥ 0.9(加 context 不丢召回)」不变量。
1000
+ if b == routing.ORPHAN:
1001
+ return []
889
1002
  inb = [e for e in entries if e.get("bucket") == b]
890
1003
  if not inb:
891
1004
  return []
@@ -910,7 +1023,7 @@ class MdCGOS(MdCG):
910
1023
  "content": c, "path": e["path"]}, 1.0))
911
1024
  return out
912
1025
 
913
- # 生效条件:当 seeds 非空时,仅取前 5 个种子,从每个种子节点的 frontmatter.edges 取 target(dict 取 target,否则 str(edge)),若 target 在 entries 映射中且不在 seed_ids 中则读取并追加分数 s*0.5;seeds 为空返回 [];depth 默认 1 但本段未使用;
1026
+ # 生效条件:当 seeds 非空时,仅取前 5 个种子,种子的 edges 取法为:其 id 在 entries 的 path 文件名映射(by_id)中时经 self._read(e_seed) 读 frontmatter(读缓存覆盖——get 裸 open 不在缓存包装面),不在映射中(fm.id≠文件名等形态)时回落 self.get(n["id"])(旧口径,冷路径);从 edges 取 target(dict 取 target,否则 str(edge)),若 target 在 entries 映射中且不在 seed_ids 中则读取并追加分数 s*0.5;seeds 为空返回 [];depth 默认 1 但本段未使用;
914
1027
  def _path_graph(self, query, entries, seeds, depth=1):
915
1028
  """图扩展路径:从词法种子沿 edges 一跳扩展。"""
916
1029
  if not seeds:
@@ -922,10 +1035,24 @@ class MdCGOS(MdCG):
922
1035
  by_id[nid] = e
923
1036
  out = []
924
1037
  for n, s in seeds[:5]:
925
- node = self.get(n["id"])
926
- if not node:
927
- continue
928
- for edge in (node["frontmatter"].get("edges") or []):
1038
+ # 种子读改走 self._read(v9:92/v13 留档修复,缺陷迭代第 14 轮):
1039
+ # get 裸 open+nodefile.loads,不在 readcache.install 的包装面
1040
+ #(只包 cg._read)——search_rrf 默认 paths 含 graph,每查询固定
1041
+ # ≤5 次盘读(40 池实测 Q2 增量恰 5,graph 零产出也在边循环前
1042
+ # 发生);边目标(下方 self._read)早已同形。种子只要 frontmatter
1043
+ # 的 edges(content 不解封不用),种子来自 lexical(entries/
1044
+ # _candidates 派生,MdCGSecure._candidates 已过 _readable)——
1045
+ # 绕过 get 的读隔离门无损失,图路扩展产物本由
1046
+ # MdCGSecure.search_rrf 终态二次过滤兜底。by_id 不含(fm.id≠
1047
+ # 文件名等形态)回落 get 保旧口径。
1048
+ e_seed = by_id.get(n["id"])
1049
+ if e_seed is not None:
1050
+ fm_s, _c = self._read(e_seed)
1051
+ edges = (fm_s or {}).get("edges") or []
1052
+ else:
1053
+ node = self.get(n["id"])
1054
+ edges = ((node or {}).get("frontmatter") or {}).get("edges") or []
1055
+ for edge in edges:
929
1056
  tid = edge.get("target") if isinstance(edge, dict) else str(edge)
930
1057
  if tid in by_id and tid not in seed_ids:
931
1058
  e = by_id[tid]
@@ -1175,6 +1302,10 @@ class MdCGOS(MdCG):
1175
1302
  串结果);候选层语义与 search 一致,详见 _candidates。
1176
1303
  """
1177
1304
  q = (query or "").strip()
1305
+ # 批次 15 统一口径(unify.py):任意语言 query → 标准原子序列;
1306
+ # 归一后的 q 进热路径缓存键(归一确定 → 缓存不串味)
1307
+ from .semantic.unify import unify_query
1308
+ q = unify_query(q)
1178
1309
  if not q:
1179
1310
  return [], {"tier": None, "reason": "empty_query", "paths": {}}
1180
1311
  # 热路径:query 结果缓存命中即返回(不改 RRF 核心)
@@ -1195,6 +1326,9 @@ class MdCGOS(MdCG):
1195
1326
  "judge": bool(judge), "judge_ranking": bool(judge_ranking),
1196
1327
  "goal_text": goal_text, "context": context,
1197
1328
  "early_stop_threshold": early_stop_threshold,
1329
+ # 进程级开关也改口径(v14 只补了参数面):MDCG_SEMANTIC /
1330
+ # MDCG_EN_ATOMS / MDCG_UNIFY_QUERY 等不入键即跨口径命中(2026-09-24 修复)。
1331
+ "env_switch": _hc.env_switch_key(),
1198
1332
  }
1199
1333
  # 不可稳定进键的参数(自定义可调用 query_expand):非默认即**绕行**
1200
1334
  # 缓存(读+写双侧闭合)——fail-closed,宁可不用缓存也不串味。
@@ -1485,15 +1619,24 @@ class MdCGOS(MdCG):
1485
1619
 
1486
1620
  # ================= 1. Fix pairs 自动挖掘 =================
1487
1621
 
1488
- # 生效条件:当 events 传入时,兼容显式 {error,fix} 对与含 text 的事件序列;后者在错误行长度 >= min_len 后向后 lookahead 条内寻找 _FIX_RE 命中的行配对并 break;去重后为每对 add 知识节点和 add_rejected,返回 pairs/rejected_ids/knowledge_ids;
1489
- def mine_fix_pairs(self, events, lookahead: int = 4, min_len: int = 6):
1622
+ # 生效条件:当 events 传入时,兼容显式 {error,fix} 对与含 text 的事件序列;后者在错误行长度 >= min_len 后向后 lookahead 条内寻找 _FIX_RE 命中的行配对并 break;去重后为每对产出修复知识(as_proposals 为假→add 直写 knowledge 层;为真→propose 入审核队列,判据指纹随提案落盘,knowledge_ids 为空、pids 收提案 id 并附 as_proposals=True)与 add_rejected 负记忆,返回 pairs/rejected_ids/knowledge_ids(提案模式另附 pids);
1623
+ def mine_fix_pairs(self, events, lookahead: int = 4, min_len: int = 6,
1624
+ as_proposals: bool = False):
1490
1625
  """从行为日志挖掘「错误 → 修复」对。
1491
1626
 
1492
1627
  events: [{"role": ..., "text": ...}] 或 [{"error":..., "fix":...}](显式对)
1493
1628
  返回 {"pairs": [...], "rejected_ids": [...], "knowledge_ids": [...]}
1494
1629
 
1630
+ as_proposals=False(默认,显式调用语义):正记忆直写 knowledge 层——
1631
+ 调用方是有意图的显式动作(如 mdcg_mine_fix_pairs 工具直调)。
1632
+ as_proposals=True(自动管线语义):正记忆改走 propose 审核队列——
1633
+ 自动产物不经收口不入知识层(§5.5 系统纪律化,zcode 外评 break#2);
1634
+ 判据指纹随提案落盘,复核者只能按原判据裁决不能放宽;payload_hash
1635
+ 幂等(重试与崩溃恢复无害)。负记忆一律直写 rejected 层(隔离层,
1636
+ 不存在 knowledge 污染问题)。
1637
+
1495
1638
  产出:
1496
- · knowledge/<fix_xxx>.md —— 可路由的修复知识(CCG 5 要素)
1639
+ · knowledge/<fix_xxx>.md 或审核队列提案 —— 可路由的修复知识(CCG 5 要素)
1497
1640
  · rejected/<rej_xxx>.md —— 负记忆「此错误不必深挖根因」(防重复踩坑)
1498
1641
  """
1499
1642
  pairs = []
@@ -1528,7 +1671,7 @@ class MdCGOS(MdCG):
1528
1671
  seen.add(k)
1529
1672
  uniq.append((err, fix))
1530
1673
 
1531
- rej_ids, kno_ids = [], []
1674
+ rej_ids, kno_ids, pids = [], [], []
1532
1675
  for err, fix in uniq:
1533
1676
  # 1) 可路由的修复知识
1534
1677
  kid = "fix_" + _sig(err + "\x00" + fix)
@@ -1540,17 +1683,31 @@ class MdCGOS(MdCG):
1540
1683
  f"# 验证方式:行为日志(后续同类错误不再出现)\n"
1541
1684
  f"# 不适用条件:与「{err[:30]}」不同的错误\n\n"
1542
1685
  f"错误:{err}\n修复:{fix}\n")
1543
- self.add(kid, content, layer="knowledge", tags=["fix_pair"],
1544
- verification_basis="data", importance=0.7)
1545
- kno_ids.append(kid)
1686
+ if as_proposals:
1687
+ # 自动管线:入审核队列(判据指纹随提案落盘,复核者不能放宽;
1688
+ # payload_hash 幂等——同内容重试返回原 pid)
1689
+ pid = self.propose(
1690
+ kid, content, layer="knowledge", tags=["fix_pair"],
1691
+ verify={"kind": "data",
1692
+ "assertions": ["error/fix 均非空且来自真实行为日志",
1693
+ "与既有 fix_pair 知识不重复"]})
1694
+ pids.append(pid)
1695
+ else:
1696
+ self.add(kid, content, layer="knowledge", tags=["fix_pair"],
1697
+ verification_basis="data", importance=0.7)
1698
+ kno_ids.append(kid)
1546
1699
  # 2) 负记忆:这个错误不是死路(防止重复深挖)
1547
1700
  rid = self.add_rejected(
1548
1701
  hypothesis=f"「{err[:60]}」需要深挖根因(无现成解法)",
1549
1702
  reason=f"已有修复:{fix[:80]}",
1550
1703
  verification_basis="data", tags=["fix_pair"])
1551
1704
  rej_ids.append(rid)
1552
- return {"pairs": [{"error": e, "fix": f} for e, f in uniq],
1553
- "rejected_ids": rej_ids, "knowledge_ids": kno_ids}
1705
+ result = {"pairs": [{"error": e, "fix": f} for e, f in uniq],
1706
+ "rejected_ids": rej_ids, "knowledge_ids": kno_ids}
1707
+ if as_proposals:
1708
+ result["as_proposals"] = True
1709
+ result["pids"] = pids
1710
+ return result
1554
1711
 
1555
1712
  # ================= 4. 审核队列(inbox → decisions) =================
1556
1713
 
@@ -1575,17 +1732,18 @@ class MdCGOS(MdCG):
1575
1732
  """
1576
1733
  phash = _sig(content)
1577
1734
  with FileLock(self.inbox_log, strict=True):
1735
+ # 增量对账(issue #32):原实现每条全量 read_jsonl(inbox) +
1736
+ # read_jsonl(decisions)(锁内 O(M+D)/条、批量 O(M²),旧格式行
1737
+ # 逐条重算 sha1);改走 stat 尾部增量索引——文件序与覆盖语义
1738
+ # 与原全量扫描逐位一致(同 phash 按文件序取,已裁决优先 break)。
1739
+ ph_idx = self._inbox_phash_index()
1578
1740
  st = self._pid_status()
1579
1741
  dup = None
1580
- for r in read_jsonl(self.inbox_log):
1581
- # 存量记录无 payload_hash 字段 → 现算兼容(对账覆盖旧账)
1582
- rh = r.get("payload_hash") or _sig(r.get("content") or "")
1583
- if rh != phash:
1584
- continue
1585
- s = st.get(r.get("pid")) or {}
1586
- dup = {"pid": r.get("pid"),
1742
+ for pid, nid in ph_idx.get(phash, ()):
1743
+ s = st.get(pid) or {}
1744
+ dup = {"pid": pid,
1587
1745
  "status": s.get("status") or "pending",
1588
- "node_id": r.get("id")}
1746
+ "node_id": nid}
1589
1747
  if dup["status"] in ("accepted", "rejected"):
1590
1748
  break # 已裁决的最有信息量,优先返回
1591
1749
  if dup:
@@ -1619,14 +1777,135 @@ class MdCGOS(MdCG):
1619
1777
  "dup_of": None, "dup_status": None}
1620
1778
  return pid
1621
1779
 
1622
- # 生效条件:当 self.decisions_log 可被 read_jsonl 读取时,仅 r.get("pid") 为真值的记录写入 st[r["pid"]],后出现记录覆盖先出现记录,返回 pid → 最新一条裁决记录的字典;
1780
+ # 生效条件:stat decisions_log 失败(OSError)时把 _pid_st 置空 dict、两扫描键置 -1 并返回空 dict;size 与 mtime_ns 均与缓存键一致时直接返回 _pid_st(零解析);缓存键为 -1(首装)或当前 size 小于已扫描偏移(外部非合作重写/回退)时经 read_jsonl_tail(path, 0) 全量重建 _pid_st,否则经 read_jsonl_tail 从已扫描偏移起增量并入,逐条把 r.get("pid") 为真值的记录写入 _pid_st(后并入覆盖先并入);水位存 read_jsonl_tail 返回的真实 EOF 偏移(end 为 None 时回落 stat size),mtime_ns 存读前 stat 值,返回 _pid_st。
1623
1781
  def _pid_status(self):
1624
- """pid → 最新一条裁决记录(多轮再审批时取最后一轮)。"""
1625
- st = {}
1626
- for r in read_jsonl(self.decisions_log):
1782
+ """pid → 最新一条裁决记录(多轮再审批时取最后一轮)。
1783
+
1784
+ 增量缓存(issue #32):原实现每调用全量 read_jsonl(decisions_log)
1785
+ (json.loads 逐条),propose 对账每条 O(D)、批量 O(M×D)。append-only
1786
+ 契约下改为 stat 尾部对账:size/mtime 未变零解析;size 增长只解析
1787
+ 新增字节;size 回退/首装全量重建兜底。mtime 单变 size 不变(touch
1788
+ 类)走增量窗口=0 行,结果不变。跨进程 append 由 stat 自动并入。
1789
+ 水位口径(竞态修复):存 read_jsonl_tail 返回的**真实 EOF 偏移**,
1790
+ 不存读前 stat 的 size——stat→read 窗口内的并发 append 本轮已并入,
1791
+ 水位偏小会让下一轮重读同一段(本缓存是 dict 后写覆盖不受损,但与
1792
+ 列表型缓存共用同一口径)。
1793
+ 边界(与检索读缓存同款诚实声明):外部**非 append 形态**重写文件
1794
+ (同 size 改内容)不在合作写者协议内,不保证可见。
1795
+ """
1796
+ try:
1797
+ sti = os.stat(self.decisions_log)
1798
+ size, mtime = sti.st_size, sti.st_mtime_ns
1799
+ except OSError:
1800
+ self._pid_st, self._pid_st_size, self._pid_st_mtime = {}, -1, -1
1801
+ return self._pid_st
1802
+ if size == self._pid_st_size and mtime == self._pid_st_mtime:
1803
+ return self._pid_st # 无新内容(零解析)
1804
+ full = self._pid_st_size < 0 or size < self._pid_st_size
1805
+ if full:
1806
+ self._pid_st = {}
1807
+ src, end = read_jsonl_tail(self.decisions_log,
1808
+ 0 if full else self._pid_st_size)
1809
+ for r in src:
1627
1810
  if r.get("pid"):
1628
- st[r["pid"]] = r
1629
- return st
1811
+ self._pid_st[r["pid"]] = r
1812
+ self._pid_st_size = size if end is None else end
1813
+ self._pid_st_mtime = mtime
1814
+ return self._pid_st
1815
+
1816
+ # 生效条件:stat inbox_log 失败(OSError)时把 _inbox_idx 置空 dict、两扫描键置 -1 并返回空 dict;size 与 mtime_ns 均与缓存键一致时直接返回 _inbox_idx(零解析);缓存键为 -1(首装)或当前 size 小于已扫描偏移时经 read_jsonl_tail(path, 0) 全量重建,否则经 read_jsonl_tail 增量并入,逐条以 r.get("payload_hash") 或(缺键时)_sig(r.get("content") or "") 为 ph 把 (r.get("pid"), r.get("id")) 追加进 _inbox_idx[ph](文件序);水位存 read_jsonl_tail 返回的真实 EOF 偏移(end 为 None 时回落 stat size),mtime_ns 存读前 stat 值,返回 _inbox_idx。
1817
+ def _inbox_phash_index(self):
1818
+ """inbox 的 phash → [(pid, node_id)](文件序)增量索引(propose 对账用)。
1819
+
1820
+ 与 _pid_status 同款 stat 尾部对账(issue #32)。存量旧格式行
1821
+ (无 payload_hash)在**并入时**现算一次 _sig(此后常驻,不再
1822
+ 每条 propose 重算——旧路径每条重算 M 次 sha1)。
1823
+ 水位口径(竞态修复,同 _pid_status):存真实 EOF 偏移,不存读前
1824
+ stat 的 size——窗口内并发 append 不得在下轮重复并入。
1825
+ """
1826
+ try:
1827
+ sti = os.stat(self.inbox_log)
1828
+ size, mtime = sti.st_size, sti.st_mtime_ns
1829
+ except OSError:
1830
+ self._inbox_idx, self._inbox_idx_size, self._inbox_idx_mtime = \
1831
+ {}, -1, -1
1832
+ return self._inbox_idx
1833
+ if size == self._inbox_idx_size and mtime == self._inbox_idx_mtime:
1834
+ return self._inbox_idx # 无新内容(零解析)
1835
+ full = self._inbox_idx_size < 0 or size < self._inbox_idx_size
1836
+ if full:
1837
+ self._inbox_idx = {}
1838
+ src, end = read_jsonl_tail(self.inbox_log,
1839
+ 0 if full else self._inbox_idx_size)
1840
+ for r in src:
1841
+ ph = r.get("payload_hash") or _sig(r.get("content") or "")
1842
+ self._inbox_idx.setdefault(ph, []).append(
1843
+ (r.get("pid"), r.get("id")))
1844
+ self._inbox_idx_size = size if end is None else end
1845
+ self._inbox_idx_mtime = mtime
1846
+ return self._inbox_idx
1847
+
1848
+ # 生效条件:stat inbox_log 失败(OSError)时把 _inbox_recs 置空列表、两扫描键置 -1 并返回它;size 与 mtime_ns 均与缓存键一致时直接返回 _inbox_recs(零解析);缓存键为 -1(首装)或当前 size 小于已扫描偏移时以 read_jsonl_tail(path, 0) 全量重建,否则以 read_jsonl_tail(offset) 结果 extend 增量并入;水位存 read_jsonl_tail 返回的真实 EOF 偏移(end 为 None 时回落 stat size),mtime_ns 存读前 stat 值,返回 _inbox_recs(内部缓存,调用方只读不得变更)。
1849
+ def _inbox_records(self):
1850
+ """inbox 全记录(文件序)增量缓存——review 治理面单次调用共用一次解析。
1851
+
1852
+ 与 _pid_status 同款 stat 尾部对账(issue #35):原 review_decide 线性
1853
+ 扫 inbox 找 pid、_cascade_dedup 再全量读一遍(2 遍/调用);改走本
1854
+ 缓存后稳态零解析,调用方在内存列表上线性扫(万条 ~1ms 级,无 IO)。
1855
+ 水位口径(竞态修复):存 read_jsonl_tail 返回的**真实 EOF 偏移**,
1856
+ 不存读前 stat 的 size——stat→read 窗口内的并发 append 本轮已并入,
1857
+ 水位偏小会让下一轮 extend 重读同一条(review_list 重复 pid、
1858
+ _cascade_dedup 连写两条 reject),且稳态 size 只增、永不自愈。
1859
+ """
1860
+ try:
1861
+ sti = os.stat(self.inbox_log)
1862
+ size, mtime = sti.st_size, sti.st_mtime_ns
1863
+ except OSError:
1864
+ self._inbox_recs, self._inbox_recs_size, self._inbox_recs_mtime = \
1865
+ [], -1, -1
1866
+ return self._inbox_recs
1867
+ if size == self._inbox_recs_size and mtime == self._inbox_recs_mtime:
1868
+ return self._inbox_recs # 无新内容(零解析)
1869
+ full = self._inbox_recs_size < 0 or size < self._inbox_recs_size
1870
+ if full:
1871
+ self._inbox_recs = []
1872
+ src, end = read_jsonl_tail(self.inbox_log,
1873
+ 0 if full else self._inbox_recs_size)
1874
+ self._inbox_recs.extend(src)
1875
+ self._inbox_recs_size = size if end is None else end
1876
+ self._inbox_recs_mtime = mtime
1877
+ return self._inbox_recs
1878
+
1879
+ # 生效条件:stat decisions_log 失败(OSError)时把 _dec_recs 置空列表、两扫描键置 -1 并返回它;size 与 mtime_ns 均与缓存键一致时直接返回 _dec_recs(零解析);缓存键为 -1(首装)或当前 size 小于已扫描偏移时以 read_jsonl_tail(path, 0) 全量重建,否则以 read_jsonl_tail(offset) 结果 extend 增量并入;水位存 read_jsonl_tail 返回的真实 EOF 偏移(end 为 None 时回落 stat size),mtime_ns 存读前 stat 值,返回 _dec_recs(内部缓存,调用方只读不得变更)。
1880
+ def _decisions_records(self):
1881
+ """decisions 全记录(文件序)增量缓存——review 治理面单次调用共用一次解析。
1882
+
1883
+ issue #35:原 review_stats 全量读 decisions 统计、verify_review_record
1884
+ 全量线性扫找单条 pid(每调用 1 遍整文件 json.loads);改走本缓存后
1885
+ 稳态零解析,调用方内存线性扫。与 _pid_status(pid→最新)互补:
1886
+ 本缓存保全量记录(多轮裁决历史、by_decision 统计口径)。
1887
+ 水位口径(竞态修复,同 _inbox_records):存真实 EOF 偏移——窗口内
1888
+ 并发 append 不得在下一轮重复 extend(review_stats 计数虚高/翻倍、
1889
+ 永不自愈)。
1890
+ """
1891
+ try:
1892
+ sti = os.stat(self.decisions_log)
1893
+ size, mtime = sti.st_size, sti.st_mtime_ns
1894
+ except OSError:
1895
+ self._dec_recs, self._dec_recs_size, self._dec_recs_mtime = \
1896
+ [], -1, -1
1897
+ return self._dec_recs
1898
+ if size == self._dec_recs_size and mtime == self._dec_recs_mtime:
1899
+ return self._dec_recs # 无新内容(零解析)
1900
+ full = self._dec_recs_size < 0 or size < self._dec_recs_size
1901
+ if full:
1902
+ self._dec_recs = []
1903
+ src, end = read_jsonl_tail(self.decisions_log,
1904
+ 0 if full else self._dec_recs_size)
1905
+ self._dec_recs.extend(src)
1906
+ self._dec_recs_size = size if end is None else end
1907
+ self._dec_recs_mtime = mtime
1908
+ return self._dec_recs
1630
1909
 
1631
1910
  # 生效条件:实例已构建(内部先读 _pid_status());返回其 status ∈ TERMINAL_DECISION_STATUS(accepted/rejected/noop)的 pid 集合,status=="needs_reapproval" 视为未关闭、不计入;
1632
1911
  def _closed_pids(self):
@@ -1639,7 +1918,9 @@ class MdCGOS(MdCG):
1639
1918
  """待审核候选(含被红队打回、待再审批的条目)。"""
1640
1919
  st = self._pid_status()
1641
1920
  out = []
1642
- for r in read_jsonl(self.inbox_log):
1921
+ # issue #35:走全记录增量缓存(稳态零解析)——review_stats 链路单次
1922
+ # 调用共用一次解析;rec=dict(r) 复制,不改内部缓存。
1923
+ for r in self._inbox_records():
1643
1924
  s = st.get(r.get("pid")) or {}
1644
1925
  if s.get("status") in TERMINAL_DECISION_STATUS:
1645
1926
  continue
@@ -1660,7 +1941,9 @@ class MdCGOS(MdCG):
1660
1941
  视图复用 review_list/_closed_pids,避免在此复制第二份终态判定逻辑。
1661
1942
  """
1662
1943
  by = {}
1663
- for r in read_jsonl(self.decisions_log):
1944
+ # issue #35:走全记录增量缓存(稳态零解析)——原每次全量逐行
1945
+ # json.loads,与 review_list/_closed_pids 链路重复解析同一文件。
1946
+ for r in self._decisions_records():
1664
1947
  d = str(r.get("decision") or "").strip() or "unknown"
1665
1948
  by[d] = by.get(d, 0) + 1
1666
1949
  return {"records": sum(by.values()), "by_decision": by,
@@ -1789,7 +2072,9 @@ class MdCGOS(MdCG):
1789
2072
  closed = []
1790
2073
  with FileLock(self.decisions_log, strict=True):
1791
2074
  st = self._pid_status()
1792
- for r in read_jsonl(self.inbox_log):
2075
+ # issue #35:走全记录增量缓存(稳态零解析)——原与 review_decide
2076
+ # 开头的找 pid 扫描重复全量读同一 inbox(2 遍/调用)。
2077
+ for r in self._inbox_records():
1793
2078
  bpid = r.get("pid")
1794
2079
  if not bpid or bpid == pid or bpid in st:
1795
2080
  continue # 自己 / 已有裁决记录(含 needs_reapproval)跳过
@@ -1840,7 +2125,9 @@ class MdCGOS(MdCG):
1840
2125
  return {"ok": False, "error": "node_not_found"}
1841
2126
  fm = node.get("frontmatter") or {}
1842
2127
  pid, rnd = fm.get("pid"), fm.get("round")
1843
- src = next((r for r in read_jsonl(self.decisions_log)
2128
+ # issue #35:走全记录增量缓存后内存线性扫(稳态零解析)——原每次
2129
+ # 全量逐行 json.loads 整份 decisions 只为找一条 pid+round。
2130
+ src = next((r for r in self._decisions_records()
1844
2131
  if r.get("pid") == pid
1845
2132
  and int(r.get("round") or 0) == int(rnd or 0)), None)
1846
2133
  if not src:
@@ -1855,10 +2142,11 @@ class MdCGOS(MdCG):
1855
2142
  "verify_hash": src.get("verify_hash"),
1856
2143
  "source": "hippocampus/decisions.jsonl"}
1857
2144
 
1858
- # 生效条件:decision 须为 DECISION_ACTIONS("accept"/"reject"/"edit"/"merge"/"noop")之一(否则 raise ValueError),inbox_log 中须有 pid 匹配记录(否则 {'ok': False, 'error': 'pid_not_found'}),且 pid 不在 self._closed_pids() 中(否则 'already_decided');edits 为真值且含 "verify"、或 redteam 为真值且含 "verify" 时返回 'verify_readonly';last_status=="needs_reapproval" 时须 redteam.verdict 归一化为 "pass" 且 round_no>last_round(否则 'reapproval_required' / 'round_not_advanced');rt_v=="reject" 或(decision=="reject" 且 rt_issues 非空)时记 needs_reapproval 不落节点;decision=="accept" 且 rt_v 为空且 _redteam_required() 为真时返回 'redteam_required';其余 accept/edit 按 item(edit 时用 edits.get 覆盖 content/tags/layer)add+flush 落节点,merge 须 merge_into 或 item.extra.merge_into 指向的节点存在(否则 'merge_target_not_found')后追加内容并 rebuild_index,reject 与 noop 只记裁决(status 分别为 "rejected"/"noop");最后统一 _record_decision + _cascade_dedup + flush 后返回 result。
2145
+ # 生效条件:decision 须为 DECISION_ACTIONS("accept"/"reject"/"edit"/"merge"/"noop")之一(否则 raise ValueError),inbox_log 中须有 pid 匹配记录(否则 {'ok': False, 'error': 'pid_not_found'}),且 pid 不在 self._closed_pids() 中(否则 'already_decided');edits 为真值且含 "verify"、或 redteam 为真值且含 "verify" 时返回 'verify_readonly';last_status=="needs_reapproval" 时须 redteam.verdict 归一化为 "pass" 且 round_no>last_round(否则 'reapproval_required' / 'round_not_advanced');rt_v=="reject" 或(decision=="reject" 且 rt_issues 非空)时记 needs_reapproval 不落节点;decision=="accept" 且 rt_v 为空且 _redteam_required() 为真时返回 'redteam_required';其余 accept/edit 按 item(edit 时用 edits.get 覆盖 content/tags/layer)add+flush 落节点,merge 须 merge_into 或 item.extra.merge_into 指向的节点存在(否则 'merge_target_not_found'),且写入前经与 add 同款双闸(N131,2026-09-25:principal 在位先 require_layer_write(目标层, 目标敏感度)——与 MdCGSecure.add 同序同错型;再 protect.guard_write(override=override)——self/anchor 层、immutable 标记,拒绝抛 ProtectionError/AccessDenied 且提案留 pending 不落库)后追加内容并定向索引 upsert(_node_entry 同源条目写入 index/_dirty)+flush,reject 与 noop 只记裁决(status 分别为 "rejected"/"noop");最后统一 _record_decision + _cascade_dedup + flush 后返回 result。
1859
2146
  def review_decide(self, pid: str, decision: str, edits: dict = None,
1860
2147
  merge_into: str = None, reason: str = "",
1861
- redteam: dict = None, issues=None):
2148
+ redteam: dict = None, issues=None,
2149
+ override: bool = False):
1862
2150
  """审核裁决:accept / reject / edit / merge / noop。
1863
2151
 
1864
2152
  accept → 按 inbox 原样写入
@@ -1880,7 +2168,9 @@ class MdCGOS(MdCG):
1880
2168
  """
1881
2169
  if decision not in DECISION_ACTIONS:
1882
2170
  raise ValueError(f"未知裁决:{decision}")
1883
- item = next((r for r in read_jsonl(self.inbox_log)
2171
+ # issue #35:走全记录增量缓存后内存线性扫(稳态零解析)——原每次
2172
+ # 全量逐行 json.loads 整份 inbox 只为找一条 pid。
2173
+ item = next((r for r in self._inbox_records()
1884
2174
  if r.get("pid") == pid), None)
1885
2175
  if not item:
1886
2176
  return {"ok": False, "error": "pid_not_found"}
@@ -1953,7 +2243,8 @@ class MdCGOS(MdCG):
1953
2243
  # 未达 autoflush(64) 阈值时进程退出即永久丢失——裁决进程(review_cli /
1954
2244
  # MCP op=review)通常只写 1~2 条,不 flush 则「节点在盘上但索引无条目」,
1955
2245
  # 其他进程与重载后的长驻进程都检索不到,只能靠某次全量 rebuild 偶然救回。
1956
- # merge 分支绕开 add 直写节点文件,故其收尾同为索引重建(同因不同法)。
2246
+ # merge 分支同样直写节点文件,其收尾已改为同款定向 upsert+flush
2247
+ # (issue #34,见 else 分支注释)。
1957
2248
  self.flush()
1958
2249
  result.update(ok=True, node_id=nid)
1959
2250
  else: # merge
@@ -1961,6 +2252,22 @@ class MdCGOS(MdCG):
1961
2252
  tgt = self.get(target) if target else None
1962
2253
  if not tgt:
1963
2254
  return {"ok": False, "error": "merge_target_not_found"}
2255
+ # 写保护(N131,2026-09-25):merge = 对目标节点的一次**覆写**
2256
+ # (_write_node 追加正文),与 accept/edit 的 add 落点同受写保护——
2257
+ # 此前直写绕过双闸,orchestr forbidden「anchor/self/goals/knowledge
2258
+ # 层」被打穿。与 add 同序同错型:principal 层写闸在先(对照
2259
+ # MdCGSecure.add),引擎级 guard_write 在后(对照基类 add :1311);
2260
+ # 闸必须在 _record_decision 之前抛出——审计节点写入本就在其
2261
+ # try/except 内,闸放那里会被吞成 record_error 形同虚设。
2262
+ _p = getattr(self, "principal", None)
2263
+ if _p is not None and hasattr(_p, "require_layer_write"):
2264
+ _e = (self.index.get("nodes") or {}).get(target) or {}
2265
+ _p.require_layer_write(
2266
+ _e.get("layer") or tgt["frontmatter"].get("layer")
2267
+ or tgt["path"].split("/")[0],
2268
+ _e.get("sensitivity") or DEFAULT_SENSITIVITY)
2269
+ protect.guard_write(self, target, override=override,
2270
+ actor=getattr(self, "actor", None))
1964
2271
  fm = dict(tgt["frontmatter"])
1965
2272
  merged_content = (tgt["content"].rstrip() + "\n\n" +
1966
2273
  item["content"].strip() + "\n")
@@ -1972,7 +2279,24 @@ class MdCGOS(MdCG):
1972
2279
  fm["non_applicable_conditions"] = neg
1973
2280
  self._write_node(target, os.path.join(self.root, tgt["path"]),
1974
2281
  fm, merged_content)
1975
- self.rebuild_index()
2282
+ # 定向索引 upsert(issue #34):merge 只改一个目标节点,原
2283
+ # rebuild_index 收尾是 _scan_nodes 全库 O(N) 逐文件扫描
2284
+ # (1500 池实测 ~97-247ms/次),与 accept/edit 的 add+flush
2285
+ # O(1) 增量收尾不对称。改为:单文件条目重算(_node_entry 与
2286
+ # _scan_nodes 同源唯一真源——字段集零漂移)+ 内存索引/增量
2287
+ # 日志定向写入 + flush。不走 _stage:目标已在索引,_stage 会
2288
+ # 虚增 bucket 计数;merge 不改 tags/目录,bucket 不变。
2289
+ # _dirty 置数同时推进 readcache 代际(写盘后同实例不陈旧)。
2290
+ try:
2291
+ tp = os.path.join(self.root, tgt["path"])
2292
+ with open(tp, encoding="utf-8") as f:
2293
+ fm2, c2 = nodefile.loads(f.read())
2294
+ e2 = self._node_entry(tp, tgt["path"].split("/")[0], fm2, c2)
2295
+ self.index["nodes"][target] = e2
2296
+ self._dirty[target] = e2
2297
+ except OSError:
2298
+ pass # 单文件重读失败(极罕见):条目留旧值,下次 rebuild 对账
2299
+ self.flush()
1976
2300
  result.update(ok=True, node_id=target)
1977
2301
 
1978
2302
  # status 映射统一出口:reject→"rejected"、noop→"noop"(同为终态,见
@@ -2018,7 +2342,7 @@ class MdCGOS(MdCG):
2018
2342
  h = _sig(content or "", 16)
2019
2343
  dst = os.path.join(self.trash_dir, f"{node_id}.md")
2020
2344
  try:
2021
- os.replace(src, dst)
2345
+ publish(src, dst)
2022
2346
  except OSError as exc:
2023
2347
  return {"ok": False, "error": str(exc)}
2024
2348
  append_jsonl(self.deletions_log, {"t": time.time(), "id": node_id,
@@ -2042,7 +2366,7 @@ class MdCGOS(MdCG):
2042
2366
  def is_tombstoned(self, node_id: str):
2043
2367
  return any(r.get("id") == node_id for r in read_jsonl(self.deletions_log))
2044
2368
 
2045
- # 生效条件:当 node_id 传入时,若 deletions_log 中存在该 id 且 force=False 返回 tombstoned 拒绝;否则检查 trash_dir/{node_id}.md,os.path.exists 为 False 返回 not_in_trash;可打开则读取并 self.add(override=True...) 恢复、移除 trash 源、audit,返回 ok True/id/forced=bool(force);
2369
+ # 生效条件:当 node_id 传入时,若 deletions_log 中存在该 id 且 force=False 返回 tombstoned 拒绝;否则检查 trash_dir/{node_id}.md,os.path.exists 为 False 返回 not_in_trash;可打开则读取并以 add(override=True) + trash frontmatter 元数据全量透传(显式形参逐项传,其余键经 **extra 回写;lifecycle_state 仅在 active→该态合法迁移时透传)恢复、移除 trash 源、audit,返回 ok True/id/forced=bool(force);
2046
2370
  def restore(self, node_id: str, force: bool = False):
2047
2371
  """恢复:若在删除清单中且未 force → 拒绝(恢复时删除检查)。"""
2048
2372
  tomb = [r for r in read_jsonl(self.deletions_log) if r.get("id") == node_id]
@@ -2055,13 +2379,52 @@ class MdCGOS(MdCG):
2055
2379
  with open(src, encoding="utf-8") as f:
2056
2380
  fm, content = nodefile.loads(f.read())
2057
2381
  layer = fm.get("layer", "knowledge")
2058
- tags = fm.get("tags") or []
2059
- self.add(node_id, content, layer=layer, tags=tags, override=True,
2060
- sensitivity=fm.get("sensitivity"),
2382
+ # 恢复 = 原样放回:add 是**全量重建 fm**,只传个位数字段会把 edges/
2383
+ # depends_on/验证态/生命周期/created_at/语义摘要/证据计数等元数据永久
2384
+ # 抹掉(下方 os.remove 删 trash 源后不可再恢复)。故 add 显式形参对应
2385
+ # 的键逐项透传;其余键(created_at/evidence_*/protected/temporal/
2386
+ # derived_relation…)经 **extra 回写(fm.update 通道覆盖默认值)。
2387
+ # _skip 必须覆盖 add 全部显式形参名——**extra 撞形参名直接 TypeError。
2388
+ _skip = ("id", "layer", "tags", "condition_space", "importance",
2389
+ "confidence", "verification_basis", "edges",
2390
+ "non_applicable_conditions", "derived_from", "relation",
2391
+ "semantic", "depends_on", "valid_from", "valid_until",
2392
+ "effective_from", "effective_until", "believed_at",
2393
+ "verification_state", "override", "consistency",
2394
+ "on_conflict", "state", "sensitivity", "derived_relation",
2395
+ "content", "node_id")
2396
+ _extra = {k: v for k, v in fm.items()
2397
+ if k not in _skip and v not in (None, "", [], {})}
2398
+ # 生命周期状态走迁移裁决:restore 时节点已摘索引、按新节点(active)
2399
+ # 起判,active→archived 跳级会被拒——不可迁移时不透传(落 active),
2400
+ # 不让罕见 corner case 阻断恢复主流程。
2401
+ from . import lifecycle as _lc
2402
+ from . import provenance as _prov
2403
+ _ls = fm.get(_lc.STATE_FIELD)
2404
+ if _ls and _lc.can_transition("active", _ls):
2405
+ _extra[_lc.STATE_FIELD] = _ls
2406
+ # 验证态同样按新节点(unverified)起判:非法值透传会被 require_transition
2407
+ # 直接拒掉,过滤为 None(落 unverified)保证恢复不被脏数据卡死。
2408
+ _vs = fm.get("verification_state")
2409
+ self.add(node_id, content, layer=layer, tags=fm.get("tags") or [],
2410
+ override=True, sensitivity=fm.get("sensitivity"),
2061
2411
  condition_space=fm.get("condition_space"),
2062
2412
  importance=fm.get("importance", 0.5),
2063
2413
  confidence=fm.get("confidence", 0.6),
2064
- verification_basis=fm.get("verification_basis"))
2414
+ verification_basis=fm.get("verification_basis"),
2415
+ edges=fm.get("edges"),
2416
+ non_applicable_conditions=fm.get("non_applicable_conditions"),
2417
+ semantic=fm.get("semantic"),
2418
+ depends_on=fm.get("depends_on"),
2419
+ valid_from=fm.get("valid_from"),
2420
+ valid_until=fm.get("valid_until"),
2421
+ effective_from=fm.get("effective_from"),
2422
+ effective_until=fm.get("effective_until"),
2423
+ believed_at=fm.get("believed_at"),
2424
+ verification_state=(_vs if _vs in trust.STATES else None),
2425
+ derived_from=fm.get("derived_from"),
2426
+ relation=fm.get("derived_relation") or _prov.DEFAULT_RELATION,
2427
+ **_extra)
2065
2428
  os.remove(src)
2066
2429
  self._audit("restore", node_id, forced=bool(force))
2067
2430
  return {"ok": True, "id": node_id, "forced": bool(force)}
@@ -3473,10 +3836,10 @@ class MdCGSecure(MdCGOS):
3473
3836
  self._index_sensitivity(nid, sens)
3474
3837
  return nid
3475
3838
 
3476
- # 生效条件:sens 取 sensitivity or DEFAULT_SENSITIVITY,先 principal.require_layer_write(kw.get("layer") or "contextual", sens),再转 super().propose(node_id, content, sensitivity=sens) 并返回其结果。
3839
+ # 生效条件:sens 取 sensitivity or DEFAULT_SENSITIVITY,先 principal.require_layer_write(kw.get("layer", "knowledge"), sens)(缺省与基类 MdCGOS.propose 的 layer="knowledge" 一致——此前误用 or "contextual" 导致校验层与落盘层错位:knowledge-only 身份被误拒 / contextual-only 身份入队成死提案,2026-09-25 缺陷 #5),再转 super().propose(node_id, content, sensitivity=sens) 并返回其结果。
3477
3840
  def propose(self, node_id: str, content: str, sensitivity: str = None, **kw):
3478
3841
  sens = sensitivity or DEFAULT_SENSITIVITY
3479
- self.principal.require_layer_write(kw.get("layer") or "contextual", sens)
3842
+ self.principal.require_layer_write(kw.get("layer", "knowledge"), sens)
3480
3843
  return super().propose(node_id, content, sensitivity=sens, **kw)
3481
3844
 
3482
3845
  # 生效条件:sens 取 sensitivity or DEFAULT_SENSITIVITY,先 _rank(sens) 并 principal.require_layer_write("goals", sens),再转 super().add_goal,最后按 gid 调 _index_sensitivity 并返回 gid。
@@ -3493,6 +3856,16 @@ class MdCGSecure(MdCGOS):
3493
3856
  self.principal.require_layer_write("goals", DEFAULT_SENSITIVITY)
3494
3857
  return super().set_goal_status(node_id, status)
3495
3858
 
3859
+ # 生效条件:verdict(转 str 去空白 lower)为 "falsified" 且索引中存在 node_id 条目时,先取该条目 layer 与 sensitivity(缺省回落 DEFAULT_SENSITIVITY)调 principal.require_layer_write——falsified 删除 = 对原节点层的一次删除写(N130,2026-09-25:verify 角色 layers_allow 本就只含 rejected/contextual,不得改/删被验证内容所在层,tokens.py verify spec forbidden 显式列明),与 add/add_rejected 同一闸口;随后把 override 原样转 super().verify(受保护节点的 guard_forget 快照留痕在基类 falsified 分支内)。verdict 非 falsified 或索引无此节点时不加闸直接透传(not_found 语义由基类维持)。
3860
+ def verify(self, node_id: str, evidence: str, verdict: str,
3861
+ override: bool = False):
3862
+ if str(verdict or "").strip().lower() == "falsified":
3863
+ e = (self.index.get("nodes") or {}).get(node_id)
3864
+ if e is not None:
3865
+ self.principal.require_layer_write(
3866
+ e.get("layer"), e.get("sensitivity") or DEFAULT_SENSITIVITY)
3867
+ return super().verify(node_id, evidence, verdict, override=override)
3868
+
3496
3869
  # 生效条件:sens 取 sensitivity or DEFAULT_SENSITIVITY,经 _rank(sens) 与 principal.require_write(sens) 后把 m 基于 meta 复制并 setdefault tenant/session、harness 与 unit 为真值时补入,再强制 m["sensitivity"]=sens,text 经 _seal_content("_recent", text, sens) 后连 tags=tags 一起转 super().remember_event(window 为 None 时不传该参,否则带上 window)。
3497
3870
  def remember_event(self, role: str, text: str, tags=None, meta=None,
3498
3871
  window=None, sensitivity: str = None):
@@ -3515,15 +3888,57 @@ class MdCGSecure(MdCGOS):
3515
3888
  # ---------- 读:密级过滤 ----------
3516
3889
 
3517
3890
  # 生效条件:e 的 sensitivity 为假值时先 _read 回填为 fm 的 sensitivity or DEFAULT_SENSITIVITY,再以 self.principal.allows(sens) 的布尔结果为准。
3518
- def _readable(self, e) -> bool:
3891
+ def _readable(self, e, session=None) -> bool:
3892
+ """读可见性单点判定:clearance × sensitivity(密级)∧ 会话绑定(归属)。
3893
+
3894
+ 会话绑定(issue #35 设计定稿 2026-09-23,使用者拍板):
3895
+ · public / internal = 跨会话共享档——任何通过 clearance 的会话可见;
3896
+ · private / secret = 会话绑定档——仅归属会话(frontmatter.session)
3897
+ 可见;can_admin(设计者,需整体判断)豁免。
3898
+ · 绑定档无归属(存量节点):fail-closed,仅设计者可见(宁可少读,
3899
+ 不可越权——与下方 sensitivity 回填同款纪律)。
3900
+ · 查询侧 session 参数(含 "*")不参与绑定档判定——身份不可自报;
3901
+ 共享档本就跨会话可见,故该参数在 Secure 层不再改变任何可见性。
3902
+ """
3519
3903
  sens = e.get("sensitivity")
3520
3904
  if not sens:
3521
- # 索引可能被「无密级上下文」的实例重建而丢掉该字段:按盘上真相回填,
3522
- # fail-closed(宁可少读,不可越权)。
3905
+ # 索引可能被「无密级上下文」的实例重建而丢掉该字段:按盘上真相回填。
3906
+ # V21-2(批次 34,外部报告):`_read` 失败(返回 None 头)时**不得**
3907
+ # 回落宽松默认——那会把受保护节点判成 internal 对平级可见
3908
+ # (fail-open)。密级未知 = 按最高档 secret 处理(fail-closed:
3909
+ # 宁可误禁,不可越权)。
3523
3910
  fm, _c = self._read(e)
3524
- sens = (fm or {}).get("sensitivity") or DEFAULT_SENSITIVITY
3911
+ if fm is None:
3912
+ sens = "secret"
3913
+ else:
3914
+ sens = fm.get("sensitivity") or DEFAULT_SENSITIVITY
3525
3915
  e["sensitivity"] = sens
3526
- return self.principal.allows(sens)
3916
+ # restricted = 错误处置标记(批次 28 分型,使用者裁定):不走纯密级
3917
+ # rank——密级底线 internal ∧ 错误处置链路角色(designer/orchestr/
3918
+ # verify,或 can_admin)。平级(record/worker)与下游(output/guest)
3919
+ # 限读,处置链路必读——故意的冲突设计在此消解。
3920
+ if sens == "restricted":
3921
+ from .security import can_read_restricted
3922
+ return (self.principal.allows("internal")
3923
+ and can_read_restricted(self.principal))
3924
+ if not self.principal.allows(sens):
3925
+ return False
3926
+ if _rank(sens) >= _rank("private"):
3927
+ if self.principal.can_admin:
3928
+ return True
3929
+ # 绑定档判定**恒用 principal.session(服务端身份,不可伪造)**——
3930
+ # 查询侧 session 参数(含 "*")不构成豁免:调用方自报的会话不能
3931
+ # 越过身份看别人的 private(与 _attribution「env 固定会话时请求
3932
+ # 声明一律忽略」同款纪律)。
3933
+ nsess = e.get("session")
3934
+ return bool(nsess) and nsess == self.principal.session
3935
+ return True
3936
+
3937
+ # 生效条件:nid 在 index["nodes"] 中有条目且 _readable(e) 为真时返回 True,否则(无条目/不可见)返回 False;作为 chain.adjacency 的读隔离谓词(cg._chain_visible)被消费——关系链面(causal_chain/explain_chain/causal_path/检索 chain 路 provenance 经 expand_from_seeds)由此单点隔离,不可见节点的出边、边条件文本与下游拓扑不再进入链展开;实例身份稳定(_readable 绑定档恒用 principal.session),与 adjacency 缓存无串台。
3938
+ def _chain_visible(self, nid) -> bool:
3939
+ """关系链可见性谓词(chain.adjacency 读隔离闸,2026-09-25)。"""
3940
+ e = (self.index.get("nodes") or {}).get(nid)
3941
+ return bool(e) and self._readable(e)
3527
3942
 
3528
3943
  # 生效条件:先以 limit=None 取 super().list_goals(status=status) 的全量,再只保留 index 中 _readable(e) 为真的目标,limit 为真值时返回 keep[:limit]、否则返回全部 keep。
3529
3944
  def list_goals(self, status=None, limit=None):
@@ -3548,6 +3963,13 @@ class MdCGSecure(MdCGOS):
3548
3963
  continue
3549
3964
  if not self.principal.allows(m.get("sensitivity") or DEFAULT_SENSITIVITY):
3550
3965
  continue
3966
+ # 会话绑定档(private/secret):近期事件同口径——仅归属会话,
3967
+ # 设计者豁免(issue #35 设计定稿,与 _readable 一致)。
3968
+ msens = m.get("sensitivity") or DEFAULT_SENSITIVITY
3969
+ if (_rank(msens) >= _rank("private")
3970
+ and not self.principal.can_admin
3971
+ and m.get("session") != self.principal.session):
3972
+ continue
3551
3973
  if crypto.is_encrypted(r.get("text")):
3552
3974
  t = self._open_content("_recent", m, r["text"])
3553
3975
  if t is None:
@@ -3562,18 +3984,18 @@ class MdCGSecure(MdCGOS):
3562
3984
  self.principal.require_admin("clear_recent")
3563
3985
  return super().clear_recent()
3564
3986
 
3565
- # 生效条件:在 super()._candidates(layer=layer, roles=roles, include_work=include_work, session=session, branch=branch, validity=validity, start_time=start_time, end_time=end_time, start_operator=start_operator, end_operator=end_operator, time_axis=time_axis, view=view) 的结果上,只保留 self._readable(e) 为真的条目(时间算子在父类候选层**单点已过滤**,此处只叠加读可见性、不重复判一次——重复判会让 dropped 计数与 meta 脱钩)。
3987
+ # 生效条件:在 super()._candidates(layer=layer, roles=roles, include_work=include_work, session=None(会话判定上收本层:基类硬等值过滤会误剔共享档的他人节点), branch=branch, ...) 的结果上,只保留 self._readable(e, session=session) 为真的条目(clearance × sensitivity ∧ 会话绑定,见 _readable;时间算子在父类候选层单点已过滤,此处只叠加读可见性)。
3566
3988
  def _candidates(self, layer=None, roles=None, include_work=False,
3567
3989
  session=None, branch=None, validity=None,
3568
3990
  start_time=None, end_time=None, start_operator=None,
3569
3991
  end_operator=None, time_axis=None, view=None):
3570
3992
  out = super()._candidates(layer=layer, roles=roles, include_work=include_work,
3571
- session=session, branch=branch, validity=validity,
3993
+ session=None, branch=branch, validity=validity,
3572
3994
  start_time=start_time, end_time=end_time,
3573
3995
  start_operator=start_operator,
3574
3996
  end_operator=end_operator, time_axis=time_axis,
3575
3997
  view=view)
3576
- return [e for e in out if self._readable(e)]
3998
+ return [e for e in out if self._readable(e, session=session)]
3577
3999
 
3578
4000
  # 生效条件:在 super()._neg_coverage(terms) 的结果上,只保留 self._readable(e) 为真的条目。
3579
4001
  def _neg_coverage(self, terms):
@@ -3603,6 +4025,7 @@ class MdCGSecure(MdCGOS):
3603
4025
  res, meta = super().search_rrf(*a, **kw)
3604
4026
  validity = kw.get("validity")
3605
4027
  view = kw.get("view")
4028
+ session = kw.get("session")
3606
4029
  now = time.time() if validity else None
3607
4030
  _en_t, _ax_t, _why_t = trust.check_time_args(
3608
4031
  kw.get("start_time"), kw.get("end_time"), kw.get("start_operator"),
@@ -3610,8 +4033,11 @@ class MdCGSecure(MdCGOS):
3610
4033
  keep = []
3611
4034
  for r in res:
3612
4035
  e = self.index["nodes"].get(r[0]["id"], r[0])
3613
- if not self._readable(e):
4036
+ if not self._readable(e, session=session):
3614
4037
  continue
4038
+ # 会话归属兜底已并入 _readable(session 传参):共享档跨会话可见、
4039
+ # 绑定档(private/secret)按归属会话判定(图扩展绕过 _candidates
4040
+ # 的串台路径由它一并挡住,与 validity/view 同层兜底)。
3615
4041
  if view is not None and not roleviews.matches(e, view):
3616
4042
  continue
3617
4043
  if validity and trust.is_expired(e, now=now):
@@ -3641,9 +4067,13 @@ class MdCGSecure(MdCGOS):
3641
4067
  self.principal.require_admin("restore")
3642
4068
  return super().restore(node_id, force=force)
3643
4069
 
3644
- # 生效条件:先 principal.require_admin("review_decide"),再把 *a/**kw 原样转给 super().review_decide。
4070
+ # 生效条件:先 principal.require_op("review")(语义修正 2026-09-22:裁决权从
4071
+ # require_admin 拆出——编排者/仲裁位持 review op 即可裁决,存在级管理权
4072
+ # (forget/protect/anchor 写)仍由 require_admin 把守、designer 专属;
4073
+ # designer ops=["*"] 天然兼容),再把 *a/**kw 原样转给 super().review_decide;
4074
+ # accept/edit 落节点时的层写仍由下游 require_layer_write 按裁决者可写层拦截。
3645
4075
  def review_decide(self, *a, **kw):
3646
- self.principal.require_admin("review_decide")
4076
+ self.principal.require_op("review")
3647
4077
  return super().review_decide(*a, **kw)
3648
4078
 
3649
4079
  # 生效条件:先 principal.require_admin("evolution_rollback"),再转 super().evolution_rollback(entry_id, dry_run=dry_run, note=note)。