@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/tasks.py CHANGED
@@ -1,471 +1,471 @@
1
- """结构层任务实体 —— structural 层正式业务写入口(2026-09-16)。
2
-
3
- 使用者裁定(按既有记忆分层,三层分工):
4
-
5
- 任务 → structural 层(与「协议/自我/信任」同级;
6
- 跨会话稳定、不可遗忘——`DEMOTE_CONFIDENCE`
7
- 的降级面只覆盖 knowledge 层)
8
- 执行任务的中间信息 → contextual 层(不落库,由 AI 上下文自理)
9
- 任务知识 / 外部参考 / 交接文档 → knowledge 层(被验证的重要信息,按需调用;
10
- 节点 tags 记 `task:<slug>` 实现反向关联)
11
-
12
- 消除 AI 失忆四症状:
13
-
14
- ① 忘记已实现的工程 → `session_tasks()` 供 `session_recall` 装配
15
- 「进行中 + 近期完成」,新会话开机即见
16
- ② 计划与实际不符 → `plan_add()` 累积「计划变更」节,偏差可追溯
17
- ③ 缺核验 → 状态迁 `done` 时「结果」节必填(缺一不收,
18
- 对齐 `branches.BRANCH_MARKS` 的同款闸)
19
- ④ 换表述即新任务 → 身份判据 = 语义命名 slug(**刻意不用内容哈希**);
20
- `find_similar()` 只提示疑似同族,不自动合并
21
-
22
- 为什么身份不用内容哈希:`add_goal` 的 `goal_<sha1(text)>` 正是第 ④ 点的病根——
23
- 同一目标换个说法就变成新 goal、重复开工。任务名是使用者给的稳定标识,
24
- 重复登记必须更新原卡而不是再开一张。
25
-
26
- 与 goals 的关系:**不双写**。goals 是「检索定向槽」(谁该被检索到),任务是
27
- 「工程台账」(做到哪一步、结果是什么)——语义不同,双写必漂移(既有实证:
28
- 覆盖写会让库正文回退首版)。会话装配面各自成段。
29
-
30
- 与 branches 同哲学:库层函数收 `cg`;写盘走 `cg.add`(`require_layer_write`
31
- 权限闸自动生效,库层不绕闸);生命周期事件走 `cg._audit`(不新造日志格式,
32
- 审计失败绝不阻断主流程)。
33
-
34
- 正文格式 = CCG 六要素 + 三节(计划 / 计划变更 / 结果):
35
-
36
- # 功能名:<任务名>
37
- # 生效条件:<任务适用范围;填「无条件」则豁免资格判定中的正条件确认>
38
- # 子功能:<任务目标>
39
- # 执行:<状态>|<进度说明>
40
- # 验证方式:<验收判据>
41
- # 不适用条件:<边界>
42
-
43
- ## 计划
44
- ## 计划变更
45
- ## 结果
46
- """
47
- from __future__ import annotations
48
-
49
- import re
50
- import time
51
-
52
- TASK_STATUSES = ("active", "blocked", "done", "dropped")
53
-
54
- #: 状态中文投影(只用于正文可读性,机械判据始终用英文值)
55
- STATUS_ZH = {"active": "进行中", "blocked": "受阻", "done": "完成", "dropped": "放弃"}
56
-
57
- #: 任务节点统一标签(第一个是身份标记,第二个是任务族标记)
58
- TASK_TAG = "task"
59
- TASK_PREFIX = "task_"
60
-
61
- #: ≥0.7 会被 protect 自动打「不可遗忘」标记——即「任务结果必须得到维护」的
62
- #: 架构层保障:普通 forget 搬不走它,只有显式解保护才动得了。
63
- DEFAULT_IMPORTANCE = 0.8
64
-
65
- #: 正文节名
66
- SEC_PLAN = "计划"
67
- SEC_CHANGE = "计划变更"
68
- SEC_RESULT = "结果"
69
-
70
- #: 语义 slug 守卫——首字符须为中文/字母/数字,其后允许 `_ . -`,总长 ≤64。
71
- #: 与 `branches._BRANCH_RE` 同风格(防路径穿越与非法文件名),但放宽到 Unicode:
72
- #: 任务名多为中文,缩到 ASCII 会逼使用者起英文别名,反而制造第二个身份。
73
- _SLUG_RE = re.compile(r"^[0-9A-Za-z\u4e00-\u9fff][0-9A-Za-z\u4e00-\u9fff_.-]{0,63}$")
74
- _ILLEGAL_RE = re.compile(r"[^0-9A-Za-z\u4e00-\u9fff_.-]+")
75
-
76
- # ---------------------------------------------------------------- 命名与解析
77
-
78
- # 生效条件:name 为 None/空串或 strip 后为空时返回空串,否则把 `/`、`\`、`..` 及 _ILLEGAL_RE 命中字符折叠为 `-`、压缩连续 `-` 并去首尾 `-.` 后取前 64 字符再去首尾 `-.` 返回;
79
- def slugify(name: str) -> str:
80
- """任务名 → 语义 slug(稳定标识;同 slug 即同任务)。
81
-
82
- 只做「可安全落文件名」的归一:路径分隔符与非法字符折叠为 `-`,连续 `-`
83
- 压成一个。**不做语义改写**(不翻译、不去停用词)——slug 是对外可见的
84
- 身份,擅自改写会让使用者按原名检索时对不上号。
85
- """
86
- s = (name or "").strip()
87
- if not s:
88
- return ""
89
- s = s.replace("/", "-").replace("\\", "-").replace("..", "-")
90
- s = _ILLEGAL_RE.sub("-", s)
91
- s = re.sub(r"-{2,}", "-", s).strip("-.")
92
- return s[:64].strip("-.")
93
-
94
-
95
- # 生效条件:name(`name or ""` 后 strip)先剥掉已有的 TASK_PREFIX 再 slugify,结果非空且被 _SLUG_RE.match 命中时返回 TASK_PREFIX + slug;name 为假值或归一化后为空、不匹配时返回 ""。
96
- def task_node_id(name: str) -> str:
97
- """任务名或节点 id → 规范节点 id(`task_<slug>`)。非法名返回空串。"""
98
- s = (name or "").strip()
99
- if s.startswith(TASK_PREFIX):
100
- s = s[len(TASK_PREFIX):]
101
- slug = slugify(s)
102
- if not slug or not _SLUG_RE.match(slug):
103
- return ""
104
- return TASK_PREFIX + slug
105
-
106
-
107
- # 生效条件:v 为 None 或 str(v).strip() 为 "" 时返回 False,否则返回 True。
108
- def _has(v) -> bool:
109
- """「本次调用是否提供了该字段」——空串/None 一律视为未提供(不静默清空)。"""
110
- return v is not None and str(v).strip() != ""
111
-
112
-
113
- # 生效条件:无必需形参且无模块级常量约束,恒返回 time.strftime("%Y-%m-%d %H:%M") 的当前时间文本。
114
- def _today() -> str:
115
- return time.strftime("%Y-%m-%d %H:%M")
116
-
117
-
118
- # 生效条件:content(`content or ""` 后 splitlines)中有某行 strip 后以 `# ` + field + `:` 开头时,返回该行该前缀之后的去空白内容;content 为假值或无此匹配行时返回 ""。
119
- def _field_line(content: str, field: str) -> str:
120
- """从正文抽 `# <字段>:` 行的值(与 `mdcos._ccg_field` 同源口径,
121
- 本模块不反向 import mdcos,避免包内循环依赖)。"""
122
- pre = "# " + field + ":"
123
- for line in (content or "").splitlines():
124
- s = line.strip()
125
- if s.startswith(pre):
126
- return s[len(pre):].strip()
127
- return ""
128
-
129
-
130
- # 生效条件:(content or "") 的行中 strip 后以 "## " 开头者成为节名 s[3:].strip() 并切换当前节,其余行累入当前节,返回各节内容以 "\n" join 后 strip 的字典;无任何标题行时仅返回 {"__body__": 全篇 strip};content 为 None/空串时返回 {"__body__": ""}。
131
- def sections(content: str) -> dict:
132
- """正文 → `{节名: 节内容}`;无标题部分归入 `__body__`。"""
133
- out: dict = {"__body__": []}
134
- cur = "__body__"
135
- for line in (content or "").splitlines():
136
- s = line.strip()
137
- if s.startswith("## "):
138
- cur = s[3:].strip()
139
- out.setdefault(cur, [])
140
- continue
141
- out.setdefault(cur, []).append(line)
142
- return {k: "\n".join(v).strip() for k, v in out.items()}
143
-
144
-
145
- # 生效条件:t = (text or "").strip(),t 为 ""/"(无)"/"(未填)" 时返回 "",否则 len(t) <= n(默认 200)时返回 t,超出时返回 t[:n].rstrip() + "…"。
146
- def _brief(text: str, n: int = 200) -> str:
147
- t = (text or "").strip()
148
- if t in ("", "(无)", "(未填)"):
149
- return ""
150
- return t if len(t) <= n else t[:n].rstrip() + "…"
151
-
152
-
153
- # 生效条件:以 (old_text or "").strip() 为 base(base 为「(无)」或「(未填)」时置空),_has(change) 判定为假时返回 base or "(无)",为真时拼出 `- [今日] change.strip()`,base 非空返回 base+"\n"+该行再 strip,base 为空只返回该行;
154
- def _append_change(old_text: str, change: str) -> str:
155
- """「计划变更」节追加一行(累积式,不覆盖历史)。"""
156
- base = (old_text or "").strip()
157
- if base in ("(无)", "(未填)"):
158
- base = ""
159
- if not _has(change):
160
- return base or "(无)"
161
- line = "- [%s] %s" % (_today(), str(change).strip())
162
- return (base + "\n" + line).strip() if base else line
163
-
164
-
165
- # 生效条件:name 为必需形参(`name or ""` 后 strip 填 `# 功能名:` 行);condition、goal、acceptance、boundary、plan、changes、result 各经 _has 判定,未提供时分别落「无条件」「(未填:任务目标待补)」「other」「任务转 done/dropped 终态后不再作为进行中任务参与装配」「(未填)」「(无)」与空串;status 经 STATUS_ZH.get(status, status) 映射、未命中时原样输出,note 经 _has 为真时以 `|` 拼在执行行后。
166
- def render(name: str, *, condition: str = "", goal: str = "", status: str = "active",
167
- note: str = "", acceptance: str = "", boundary: str = "",
168
- plan: str = "", changes: str = "", result: str = "") -> str:
169
- """按 CCG 六要素 + 三节渲染任务正文。"""
170
- exec_line = STATUS_ZH.get(status, status)
171
- if _has(note):
172
- exec_line = exec_line + "|" + str(note).strip()
173
- return "\n".join([
174
- "# 功能名:%s" % (name or "").strip(),
175
- "# 生效条件:%s" % (condition.strip() if _has(condition) else "无条件"),
176
- "# 子功能:%s" % (goal.strip() if _has(goal) else "(未填:任务目标待补)"),
177
- "# 执行:%s" % exec_line,
178
- "# 验证方式:%s" % (acceptance.strip() if _has(acceptance) else "other"),
179
- "# 不适用条件:%s" % (boundary.strip() if _has(boundary)
180
- else "任务转 done/dropped 终态后不再作为进行中任务参与装配"),
181
- "",
182
- "## %s" % SEC_PLAN,
183
- (plan.strip() if _has(plan) else "(未填)"),
184
- "",
185
- "## %s" % SEC_CHANGE,
186
- (changes.strip() if _has(changes) else "(无)"),
187
- "",
188
- "## %s" % SEC_RESULT,
189
- (result.strip() if _has(result) else ""),
190
- "",
191
- ])
192
-
193
-
194
- # ---------------------------------------------------------------- 读写
195
-
196
- # 生效条件:nid 为假值(None/空串)时返回 None;否则 cg.get(nid) 命中且其 frontmatter.layer == "structural"、TASK_TAG 在 frontmatter.tags(`or []`)中时,返回 {id, fm, content, path, sec}(sec 为 sections(content));记录缺失或层/标签不符时返回 None。
197
- def _read_task(cg, nid: str):
198
- """读回任务卡;层或标签不符一律视为不存在(防串号:别的节点占用了同 id)。"""
199
- if not nid:
200
- return None
201
- rec = cg.get(nid)
202
- if not rec:
203
- return None
204
- fm = rec.get("frontmatter") or {}
205
- if fm.get("layer") != "structural" or TASK_TAG not in (fm.get("tags") or []):
206
- return None
207
- content = rec.get("content") or ""
208
- return {"id": nid, "fm": fm, "content": content,
209
- "path": rec.get("path"), "sec": sections(content)}
210
-
211
-
212
- # 生效条件:以 layer="structural"、tags=list(tags)、importance=float(importance)、override=True、task_name=name、task_status=status、task_updated_at=time.time()、actor=actor or "task" 等构成 kw,extra 为真值时经 kw.update(extra) 追加覆盖,随后调用 cg.add(nid, content, **kw) 并返回其结果;
213
- def _write(cg, nid: str, name: str, content: str, status: str, tags,
214
- importance: float, actor, extra: dict = None):
215
- """唯一写盘点——走 `cg.add`(权限闸 / 归属注入全部复用既有链路)。
216
-
217
- `override=True` 的含义:任务更新是**系统自身的幂等更新**(同 slug 即同任务),
218
- 显式声明该意图后,若节点已带 `protected`/`self_state` 标记,protect 会走
219
- 「旧版本快照 + 审计留痕」的放行分支而不是静默改写——任务台账天然获得版本快照。
220
- """
221
- kw = dict(layer="structural", tags=list(tags), importance=float(importance),
222
- confidence=1.0, verification_basis="other",
223
- override=True, consistency=False,
224
- task_name=name, task_status=status,
225
- task_updated_at=time.time(), actor=actor or "task")
226
- if extra:
227
- kw.update(extra)
228
- return cg.add(nid, content, **kw)
229
-
230
-
231
- # 生效条件:cg 具有 _audit 属性(getattr(cg, "_audit", None) 非 None)时以 (op, nid, **meta) 调用它,且其中抛出的任何异常被吞掉;cg 无该属性时不调用,两种路径均不返回内容。
232
- def _audit(cg, op: str, nid: str, **meta) -> None:
233
- """生命周期留痕;审计失败绝不阻断主流程(与 branches._audit 同哲学)。"""
234
- a = getattr(cg, "_audit", None)
235
- if a is None:
236
- return
237
- try:
238
- a(op, nid, **meta)
239
- except Exception: # noqa: BLE001
240
- pass
241
-
242
-
243
- # 生效条件:_read_task(cg, nid) 命中任务卡时返回摘要条目——id 取 rec['id'],name 取 fm.task_name 或(缺失/假值时)rec['id'],status 取 fm.task_status 或 "active",plan/changes/result 取对应节的 _brief 摘要(缺节回落 ""),并带 created_at/updated_at/path;未命中时返回 None。
244
- def _entry(cg, nid: str):
245
- """节点 id → 对外任务条目(摘要形态;全字段查 `get_task`)。"""
246
- rec = _read_task(cg, nid)
247
- if not rec:
248
- return None
249
- fm, sec = rec["fm"], rec["sec"]
250
- return {"id": rec["id"],
251
- "name": fm.get("task_name") or rec["id"],
252
- "status": fm.get("task_status") or "active",
253
- "plan": _brief(sec.get(SEC_PLAN, "")),
254
- "changes": _brief(sec.get(SEC_CHANGE, "")),
255
- "result": _brief(sec.get(SEC_RESULT, "")),
256
- "created_at": fm.get("task_created_at"),
257
- "updated_at": fm.get("task_updated_at"),
258
- "path": rec["path"]}
259
-
260
-
261
- # ---------------------------------------------------------------- 写操作
262
-
263
- # 生效条件:slugify(name) 为空串或不匹配 _SLUG_RE 时返回 {'ok': False, 含 slug 的非法名 error};否则 st = str(status if _has(status) else (旧卡 task_status or "active")).strip().lower(),st 不在 TASK_STATUSES 时返回未知状态错误,st == "done" 且合并后结果节 _has(new_res) 为假时返回「转 done 必须填结果」拒收,其余情况渲染写入并返回按 old 是否为 None 区分新建/更新的 out。
264
- def upsert(cg, name: str, *, plan: str = None, status: str = None,
265
- result: str = None, condition: str = None, goal: str = None,
266
- acceptance: str = None, boundary: str = None, change: str = None,
267
- note: str = None, tags=None, importance: float = None,
268
- actor: str = None) -> dict:
269
- """登记或更新一张任务卡(同 slug 即同任务;重复登记不新建卡)。
270
-
271
- 未提供的字段**沿用旧值**(不静默清空);`status="done"` 且合并后「结果」节
272
- 仍为空 → 拒收,盘上内容不变。
273
-
274
- :param note: 进度说明,落 `# 执行:` 行(`|` 之后)。
275
- :param change: 本次发现的新问题/计划偏差,追加到「计划变更」节。
276
- """
277
- slug = slugify(name)
278
- if not slug or not _SLUG_RE.match(slug):
279
- return {"ok": False,
280
- "error": "任务名非法(slug=%r);要求:中文/字母/数字起头,"
281
- "仅含中文/字母/数字/_ . -,长度 ≤64" % (slug,)}
282
-
283
- nid = TASK_PREFIX + slug
284
- old = _read_task(cg, nid)
285
- prev_fm = old["fm"] if old else {}
286
- prev = old["sec"] if old else {}
287
-
288
- display = (name or "").strip() or prev_fm.get("task_name") or slug
289
- st = str(status if _has(status) else (prev_fm.get("task_status") or "active")).strip().lower()
290
- if st not in TASK_STATUSES:
291
- return {"ok": False, "node_id": nid,
292
- "error": "未知任务状态:%r(可选 %s)" % (st, list(TASK_STATUSES))}
293
-
294
- # 未提供 → 沿用旧值(结果与计划绝不被静默清空)
295
- new_plan = plan if _has(plan) else prev.get(SEC_PLAN, "")
296
- new_res = result if _has(result) else prev.get(SEC_RESULT, "")
297
- new_cond = condition if _has(condition) else _field_line(old["content"], "生效条件") if old else ""
298
- new_goal = goal if _has(goal) else _field_line(old["content"], "子功能") if old else ""
299
- new_acc = acceptance if _has(acceptance) else _field_line(old["content"], "验证方式") if old else ""
300
- new_bnd = boundary if _has(boundary) else _field_line(old["content"], "不适用条件") if old else ""
301
- new_note = note if _has(note) else _exec_note(old["content"]) if old else ""
302
- new_changes = _append_change(prev.get(SEC_CHANGE, ""), change)
303
-
304
- if st == "done" and not _has(new_res):
305
- return {"ok": False, "node_id": nid,
306
- "error": "任务转 done 必须填写「结果」——结果不可缺失(缺一不收)",
307
- "hint": "补 result 后重试;盘上内容未变更"}
308
-
309
- content = render(display, condition=new_cond, goal=new_goal, status=st,
310
- note=new_note, acceptance=new_acc, boundary=new_bnd,
311
- plan=new_plan, changes=new_changes, result=new_res)
312
- tg = list(dict.fromkeys([TASK_TAG, "%s:%s" % (TASK_TAG, slug)]
313
- + [str(t) for t in (tags or []) if str(t).strip()]))
314
- imp = DEFAULT_IMPORTANCE if importance is None else float(importance)
315
- created = prev_fm.get("task_created_at") or time.time()
316
- _write(cg, nid, display, content, st, tg, imp, actor,
317
- extra={"task_created_at": created, "task_slug": slug})
318
- _audit(cg, "task_open" if old is None else "task_update", nid,
319
- status=st, slug=slug, name=display)
320
- out = {"ok": True, "node_id": nid, "name": display, "status": st,
321
- "created": old is None,
322
- "result_present": _has(new_res),
323
- "hint": "新建任务卡" if old is None else "已更新原卡(同 slug 即同任务)"}
324
- if slug != (name or "").strip():
325
- # 身份被归一化了就必须说出来——否则调用方按原名去找会找不到卡
326
- out["slug_normalized"] = slug
327
- return out
328
-
329
-
330
- # 生效条件:content 中 `# 执行:` 行的值含 "|" 时返回第一个 "|" 之后去空白的内容;content 为假值、无该行或该行值不含 "|" 时返回 ""。
331
- def _exec_note(content: str) -> str:
332
- """从 `# 执行:` 行取 `|` 之后的进度说明。"""
333
- val = _field_line(content, "执行")
334
- return val.split("|", 1)[1].strip() if "|" in val else ""
335
-
336
-
337
- # 生效条件:node_id 经 task_node_id 得到非空 nid 且 _read_task 命中该卡时,以旧卡 task_name(缺失/假值回落 nid)、status、result、note、actor 转调 upsert 并返回其结果;node_id 非法时返回 ok=False「非法任务标识」,卡不存在时返回 ok=False「任务不存在」。
338
- def set_status(cg, node_id: str, status: str, *, result: str = None,
339
- note: str = None, actor: str = None) -> dict:
340
- """任务状态迁移(进行中/受阻/完成/放弃)。迁 done 且无结果 → 拒收。"""
341
- nid = task_node_id(node_id)
342
- if not nid:
343
- return {"ok": False, "error": "非法任务标识:%r" % (node_id,)}
344
- old = _read_task(cg, nid)
345
- if not old:
346
- return {"ok": False, "node_id": nid, "error": "任务不存在:%s" % nid}
347
- return upsert(cg, old["fm"].get("task_name") or nid, status=status,
348
- result=result, note=note, actor=actor)
349
-
350
-
351
- # 生效条件:change 经 _has 判定为已提供、且 node_id 经 task_node_id 非空、_read_task 命中该卡时,以旧卡 task_name(缺失/假值回落 nid)与 change、actor 转调 upsert;change 为 None/空串/纯空白时返回 ok=False「计划变更内容为空」,node_id 非法或卡不存在时返回 ok=False 对应错误。
352
- def plan_add(cg, node_id: str, change: str, *, actor: str = None) -> dict:
353
- """计划变更追加——执行中发现的错误/新问题/偏差,累积进「计划变更」节。"""
354
- if not _has(change):
355
- return {"ok": False, "error": "计划变更内容为空"}
356
- nid = task_node_id(node_id)
357
- if not nid:
358
- return {"ok": False, "error": "非法任务标识:%r" % (node_id,)}
359
- old = _read_task(cg, nid)
360
- if not old:
361
- return {"ok": False, "node_id": nid, "error": "任务不存在:%s" % nid}
362
- return upsert(cg, old["fm"].get("task_name") or nid, change=change, actor=actor)
363
-
364
-
365
- # ---------------------------------------------------------------- 读操作
366
-
367
- # 生效条件:node_id 经 task_node_id 得到的 nid 对应一张 _read_task 命中的任务卡时返回 ok=True 的全字段(condition/goal/acceptance/boundary 由 _field_line 抽取、note 由 _exec_note 抽取、plan/changes/result 取节原文不截断、tags 取 fm.tags 或 []);否则返回 ok=False,error 中的标识取 nid 或原 node_id。
368
- def get_task(cg, node_id: str) -> dict:
369
- """单卡全字段读回(计划/变更/结果不截断)。"""
370
- nid = task_node_id(node_id)
371
- rec = _read_task(cg, nid)
372
- if not rec:
373
- return {"ok": False, "error": "任务不存在:%s" % (nid or node_id)}
374
- fm, sec = rec["fm"], rec["sec"]
375
- return {"ok": True, "id": rec["id"], "name": fm.get("task_name") or rec["id"],
376
- "status": fm.get("task_status") or "active",
377
- "condition": _field_line(rec["content"], "生效条件"),
378
- "goal": _field_line(rec["content"], "子功能"),
379
- "note": _exec_note(rec["content"]),
380
- "acceptance": _field_line(rec["content"], "验证方式"),
381
- "boundary": _field_line(rec["content"], "不适用条件"),
382
- "plan": sec.get(SEC_PLAN, ""),
383
- "changes": sec.get(SEC_CHANGE, ""),
384
- "result": sec.get(SEC_RESULT, ""),
385
- "tags": list(fm.get("tags") or []),
386
- "created_at": fm.get("task_created_at"),
387
- "updated_at": fm.get("task_updated_at"),
388
- "path": rec["path"]}
389
-
390
-
391
- # 生效条件:status 经 `str(status or "").strip().lower()` 为非空且不在 TASK_STATUSES 时返回 ok=False 未知状态;否则收集 cg.index["nodes"] 中各 _entry 可读任务、按 updated_at 倒序,status 非空时再按该状态过滤,total 为过滤后截断前的条数,limit 为真值时取前 max(int(limit), 1) 条——limit 为 None、0 或空串(假值)时不截断而返回全量 filtered 列表。
392
- def list_tasks(cg, status: str = None, limit: int = None) -> dict:
393
- """任务清单(按 updated_at 倒序);status 可选 active/blocked/done/dropped。"""
394
- st = str(status or "").strip().lower() or None
395
- if st and st not in TASK_STATUSES:
396
- return {"ok": False, "error": "未知任务状态:%r(可选 %s)" % (st, list(TASK_STATUSES))}
397
- items = []
398
- for nid in list((cg.index.get("nodes") or {}).keys()):
399
- t = _entry(cg, nid)
400
- if t:
401
- items.append(t)
402
- items.sort(key=lambda x: -(x.get("updated_at") or 0))
403
- if st:
404
- items = [t for t in items if t["status"] == st]
405
- total = len(items)
406
- if limit:
407
- items = items[:max(int(limit), 1)]
408
- return {"ok": True, "count": len(items), "total": total, "tasks": items}
409
-
410
-
411
- # 生效条件:遍历 cg.index["nodes"],把 _entry 可读且 status 为 active/blocked 的归入 active、status 为 done 的归入 done,各自按 updated_at 倒序;返回 active[:max(int(active_limit), 0)] 与 done[:max(int(done_limit), 0)](active_limit/done_limit 传 0 或负数时对应列表为空)以及两组截断前的 active_total/done_total。
412
- def session_tasks(cg, active_limit: int = 5, done_limit: int = 5) -> dict:
413
- """会话装配用:进行中(active/blocked)+ 近期完成(done,按 updated_at 倒序)。
414
-
415
- 这是第 ① 点(忘记已实现的工程)的机制解:上下文装配面在会话开始时把
416
- 「还没做完的」与「刚做完的」一起带出来,agent 不必先想到去查。
417
- """
418
- active, done = [], []
419
- for nid in list((cg.index.get("nodes") or {}).keys()):
420
- t = _entry(cg, nid)
421
- if not t:
422
- continue
423
- if t["status"] in ("active", "blocked"):
424
- active.append(t)
425
- elif t["status"] == "done":
426
- done.append(t)
427
- # 终键 id:同 updated_at 并列时定序,否则顺序回落到 cg.index["nodes"]
428
- # 的物理序(增量路径=写入序,重建路径=nid 序)。
429
- key = lambda x: (-(x.get("updated_at") or 0), # noqa: E731
430
- str(x.get("id") or ""))
431
- active.sort(key=key)
432
- done.sort(key=key)
433
- return {"ok": True,
434
- "active": active[:max(int(active_limit), 0)],
435
- "done": done[:max(int(done_limit), 0)],
436
- "active_total": len(active), "done_total": len(done)}
437
-
438
-
439
- # 生效条件:name 经 task_node_id 得 nid,exists 为同 id 任务卡是否被 _read_task 命中;k 经 `int(k or 5)` 再 max(…, 1)(k 为 None/0/空串回落 5,负数取 1)得到 kk,cg.search 抛异常时按无结果处理,只保留元素长度 ≥3、frontmatter.tags 含 TASK_TAG 且 node.id != nid 的条目,返回 out[:kk] 与 note。
440
- def find_similar(cg, name: str, k: int = 5) -> dict:
441
- """同族任务提示(**只提示,不自动合并**——是否同一任务由调用方裁决)。
442
-
443
- 身份判据是命名而非相似度:这里只回答「已经有一张同名的卡吗」与
444
- 「还有哪些卡看起来像」。相似度只能产生候选,不能授予「同一任务」的资格。
445
- """
446
- nid = task_node_id(name)
447
- exact = _read_task(cg, nid) is not None
448
- kk = max(int(k or 5), 1)
449
- res = []
450
- try:
451
- res, _meta = cg.search(name, layer="structural", k=kk + (1 if exact else 0),
452
- record=False)
453
- except Exception: # noqa: BLE001
454
- res = []
455
- out = []
456
- for item in res or []:
457
- if not (isinstance(item, (tuple, list)) and len(item) >= 3):
458
- continue
459
- node, score, qual = item[0], item[1], item[2]
460
- fm = (node.get("frontmatter") or {}) if isinstance(node, dict) else {}
461
- if TASK_TAG not in (fm.get("tags") or []):
462
- continue
463
- if node.get("id") == nid:
464
- continue
465
- out.append({"id": node.get("id"),
466
- "name": fm.get("task_name") or node.get("id"),
467
- "status": fm.get("task_status"),
468
- "score": round(float(score), 4),
469
- "qualification": (qual or {}).get("state") if isinstance(qual, dict) else None})
470
- return {"ok": True, "node_id": nid, "exists": exact, "similar": out[:kk],
1
+ """结构层任务实体 —— structural 层正式业务写入口(2026-09-16)。
2
+
3
+ 使用者裁定(按既有记忆分层,三层分工):
4
+
5
+ 任务 → structural 层(与「协议/自我/信任」同级;
6
+ 跨会话稳定、不可遗忘——`DEMOTE_CONFIDENCE`
7
+ 的降级面只覆盖 knowledge 层)
8
+ 执行任务的中间信息 → contextual 层(不落库,由 AI 上下文自理)
9
+ 任务知识 / 外部参考 / 交接文档 → knowledge 层(被验证的重要信息,按需调用;
10
+ 节点 tags 记 `task:<slug>` 实现反向关联)
11
+
12
+ 消除 AI 失忆四症状:
13
+
14
+ ① 忘记已实现的工程 → `session_tasks()` 供 `session_recall` 装配
15
+ 「进行中 + 近期完成」,新会话开机即见
16
+ ② 计划与实际不符 → `plan_add()` 累积「计划变更」节,偏差可追溯
17
+ ③ 缺核验 → 状态迁 `done` 时「结果」节必填(缺一不收,
18
+ 对齐 `branches.BRANCH_MARKS` 的同款闸)
19
+ ④ 换表述即新任务 → 身份判据 = 语义命名 slug(**刻意不用内容哈希**);
20
+ `find_similar()` 只提示疑似同族,不自动合并
21
+
22
+ 为什么身份不用内容哈希:`add_goal` 的 `goal_<sha1(text)>` 正是第 ④ 点的病根——
23
+ 同一目标换个说法就变成新 goal、重复开工。任务名是使用者给的稳定标识,
24
+ 重复登记必须更新原卡而不是再开一张。
25
+
26
+ 与 goals 的关系:**不双写**。goals 是「检索定向槽」(谁该被检索到),任务是
27
+ 「工程台账」(做到哪一步、结果是什么)——语义不同,双写必漂移(既有实证:
28
+ 覆盖写会让库正文回退首版)。会话装配面各自成段。
29
+
30
+ 与 branches 同哲学:库层函数收 `cg`;写盘走 `cg.add`(`require_layer_write`
31
+ 权限闸自动生效,库层不绕闸);生命周期事件走 `cg._audit`(不新造日志格式,
32
+ 审计失败绝不阻断主流程)。
33
+
34
+ 正文格式 = CCG 六要素 + 三节(计划 / 计划变更 / 结果):
35
+
36
+ # 功能名:<任务名>
37
+ # 生效条件:<任务适用范围;填「无条件」则豁免资格判定中的正条件确认>
38
+ # 子功能:<任务目标>
39
+ # 执行:<状态>|<进度说明>
40
+ # 验证方式:<验收判据>
41
+ # 不适用条件:<边界>
42
+
43
+ ## 计划
44
+ ## 计划变更
45
+ ## 结果
46
+ """
47
+ from __future__ import annotations
48
+
49
+ import re
50
+ import time
51
+
52
+ TASK_STATUSES = ("active", "blocked", "done", "dropped")
53
+
54
+ #: 状态中文投影(只用于正文可读性,机械判据始终用英文值)
55
+ STATUS_ZH = {"active": "进行中", "blocked": "受阻", "done": "完成", "dropped": "放弃"}
56
+
57
+ #: 任务节点统一标签(第一个是身份标记,第二个是任务族标记)
58
+ TASK_TAG = "task"
59
+ TASK_PREFIX = "task_"
60
+
61
+ #: ≥0.7 会被 protect 自动打「不可遗忘」标记——即「任务结果必须得到维护」的
62
+ #: 架构层保障:普通 forget 搬不走它,只有显式解保护才动得了。
63
+ DEFAULT_IMPORTANCE = 0.8
64
+
65
+ #: 正文节名
66
+ SEC_PLAN = "计划"
67
+ SEC_CHANGE = "计划变更"
68
+ SEC_RESULT = "结果"
69
+
70
+ #: 语义 slug 守卫——首字符须为中文/字母/数字,其后允许 `_ . -`,总长 ≤64。
71
+ #: 与 `branches._BRANCH_RE` 同风格(防路径穿越与非法文件名),但放宽到 Unicode:
72
+ #: 任务名多为中文,缩到 ASCII 会逼使用者起英文别名,反而制造第二个身份。
73
+ _SLUG_RE = re.compile(r"^[0-9A-Za-z\u4e00-\u9fff][0-9A-Za-z\u4e00-\u9fff_.-]{0,63}$")
74
+ _ILLEGAL_RE = re.compile(r"[^0-9A-Za-z\u4e00-\u9fff_.-]+")
75
+
76
+ # ---------------------------------------------------------------- 命名与解析
77
+
78
+ # 生效条件:name 为 None/空串或 strip 后为空时返回空串,否则把 `/`、`\`、`..` 及 _ILLEGAL_RE 命中字符折叠为 `-`、压缩连续 `-` 并去首尾 `-.` 后取前 64 字符再去首尾 `-.` 返回;
79
+ def slugify(name: str) -> str:
80
+ """任务名 → 语义 slug(稳定标识;同 slug 即同任务)。
81
+
82
+ 只做「可安全落文件名」的归一:路径分隔符与非法字符折叠为 `-`,连续 `-`
83
+ 压成一个。**不做语义改写**(不翻译、不去停用词)——slug 是对外可见的
84
+ 身份,擅自改写会让使用者按原名检索时对不上号。
85
+ """
86
+ s = (name or "").strip()
87
+ if not s:
88
+ return ""
89
+ s = s.replace("/", "-").replace("\\", "-").replace("..", "-")
90
+ s = _ILLEGAL_RE.sub("-", s)
91
+ s = re.sub(r"-{2,}", "-", s).strip("-.")
92
+ return s[:64].strip("-.")
93
+
94
+
95
+ # 生效条件:name(`name or ""` 后 strip)先剥掉已有的 TASK_PREFIX 再 slugify,结果非空且被 _SLUG_RE.match 命中时返回 TASK_PREFIX + slug;name 为假值或归一化后为空、不匹配时返回 ""。
96
+ def task_node_id(name: str) -> str:
97
+ """任务名或节点 id → 规范节点 id(`task_<slug>`)。非法名返回空串。"""
98
+ s = (name or "").strip()
99
+ if s.startswith(TASK_PREFIX):
100
+ s = s[len(TASK_PREFIX):]
101
+ slug = slugify(s)
102
+ if not slug or not _SLUG_RE.match(slug):
103
+ return ""
104
+ return TASK_PREFIX + slug
105
+
106
+
107
+ # 生效条件:v 为 None 或 str(v).strip() 为 "" 时返回 False,否则返回 True。
108
+ def _has(v) -> bool:
109
+ """「本次调用是否提供了该字段」——空串/None 一律视为未提供(不静默清空)。"""
110
+ return v is not None and str(v).strip() != ""
111
+
112
+
113
+ # 生效条件:无必需形参且无模块级常量约束,恒返回 time.strftime("%Y-%m-%d %H:%M") 的当前时间文本。
114
+ def _today() -> str:
115
+ return time.strftime("%Y-%m-%d %H:%M")
116
+
117
+
118
+ # 生效条件:content(`content or ""` 后 splitlines)中有某行 strip 后以 `# ` + field + `:` 开头时,返回该行该前缀之后的去空白内容;content 为假值或无此匹配行时返回 ""。
119
+ def _field_line(content: str, field: str) -> str:
120
+ """从正文抽 `# <字段>:` 行的值(与 `mdcos._ccg_field` 同源口径,
121
+ 本模块不反向 import mdcos,避免包内循环依赖)。"""
122
+ pre = "# " + field + ":"
123
+ for line in (content or "").splitlines():
124
+ s = line.strip()
125
+ if s.startswith(pre):
126
+ return s[len(pre):].strip()
127
+ return ""
128
+
129
+
130
+ # 生效条件:(content or "") 的行中 strip 后以 "## " 开头者成为节名 s[3:].strip() 并切换当前节,其余行累入当前节,返回各节内容以 "\n" join 后 strip 的字典;无任何标题行时仅返回 {"__body__": 全篇 strip};content 为 None/空串时返回 {"__body__": ""}。
131
+ def sections(content: str) -> dict:
132
+ """正文 → `{节名: 节内容}`;无标题部分归入 `__body__`。"""
133
+ out: dict = {"__body__": []}
134
+ cur = "__body__"
135
+ for line in (content or "").splitlines():
136
+ s = line.strip()
137
+ if s.startswith("## "):
138
+ cur = s[3:].strip()
139
+ out.setdefault(cur, [])
140
+ continue
141
+ out.setdefault(cur, []).append(line)
142
+ return {k: "\n".join(v).strip() for k, v in out.items()}
143
+
144
+
145
+ # 生效条件:t = (text or "").strip(),t 为 ""/"(无)"/"(未填)" 时返回 "",否则 len(t) <= n(默认 200)时返回 t,超出时返回 t[:n].rstrip() + "…"。
146
+ def _brief(text: str, n: int = 200) -> str:
147
+ t = (text or "").strip()
148
+ if t in ("", "(无)", "(未填)"):
149
+ return ""
150
+ return t if len(t) <= n else t[:n].rstrip() + "…"
151
+
152
+
153
+ # 生效条件:以 (old_text or "").strip() 为 base(base 为「(无)」或「(未填)」时置空),_has(change) 判定为假时返回 base or "(无)",为真时拼出 `- [今日] change.strip()`,base 非空返回 base+"\n"+该行再 strip,base 为空只返回该行;
154
+ def _append_change(old_text: str, change: str) -> str:
155
+ """「计划变更」节追加一行(累积式,不覆盖历史)。"""
156
+ base = (old_text or "").strip()
157
+ if base in ("(无)", "(未填)"):
158
+ base = ""
159
+ if not _has(change):
160
+ return base or "(无)"
161
+ line = "- [%s] %s" % (_today(), str(change).strip())
162
+ return (base + "\n" + line).strip() if base else line
163
+
164
+
165
+ # 生效条件:name 为必需形参(`name or ""` 后 strip 填 `# 功能名:` 行);condition、goal、acceptance、boundary、plan、changes、result 各经 _has 判定,未提供时分别落「无条件」「(未填:任务目标待补)」「other」「任务转 done/dropped 终态后不再作为进行中任务参与装配」「(未填)」「(无)」与空串;status 经 STATUS_ZH.get(status, status) 映射、未命中时原样输出,note 经 _has 为真时以 `|` 拼在执行行后。
166
+ def render(name: str, *, condition: str = "", goal: str = "", status: str = "active",
167
+ note: str = "", acceptance: str = "", boundary: str = "",
168
+ plan: str = "", changes: str = "", result: str = "") -> str:
169
+ """按 CCG 六要素 + 三节渲染任务正文。"""
170
+ exec_line = STATUS_ZH.get(status, status)
171
+ if _has(note):
172
+ exec_line = exec_line + "|" + str(note).strip()
173
+ return "\n".join([
174
+ "# 功能名:%s" % (name or "").strip(),
175
+ "# 生效条件:%s" % (condition.strip() if _has(condition) else "无条件"),
176
+ "# 子功能:%s" % (goal.strip() if _has(goal) else "(未填:任务目标待补)"),
177
+ "# 执行:%s" % exec_line,
178
+ "# 验证方式:%s" % (acceptance.strip() if _has(acceptance) else "other"),
179
+ "# 不适用条件:%s" % (boundary.strip() if _has(boundary)
180
+ else "任务转 done/dropped 终态后不再作为进行中任务参与装配"),
181
+ "",
182
+ "## %s" % SEC_PLAN,
183
+ (plan.strip() if _has(plan) else "(未填)"),
184
+ "",
185
+ "## %s" % SEC_CHANGE,
186
+ (changes.strip() if _has(changes) else "(无)"),
187
+ "",
188
+ "## %s" % SEC_RESULT,
189
+ (result.strip() if _has(result) else ""),
190
+ "",
191
+ ])
192
+
193
+
194
+ # ---------------------------------------------------------------- 读写
195
+
196
+ # 生效条件:nid 为假值(None/空串)时返回 None;否则 cg.get(nid) 命中且其 frontmatter.layer == "structural"、TASK_TAG 在 frontmatter.tags(`or []`)中时,返回 {id, fm, content, path, sec}(sec 为 sections(content));记录缺失或层/标签不符时返回 None。
197
+ def _read_task(cg, nid: str):
198
+ """读回任务卡;层或标签不符一律视为不存在(防串号:别的节点占用了同 id)。"""
199
+ if not nid:
200
+ return None
201
+ rec = cg.get(nid)
202
+ if not rec:
203
+ return None
204
+ fm = rec.get("frontmatter") or {}
205
+ if fm.get("layer") != "structural" or TASK_TAG not in (fm.get("tags") or []):
206
+ return None
207
+ content = rec.get("content") or ""
208
+ return {"id": nid, "fm": fm, "content": content,
209
+ "path": rec.get("path"), "sec": sections(content)}
210
+
211
+
212
+ # 生效条件:以 layer="structural"、tags=list(tags)、importance=float(importance)、override=True、task_name=name、task_status=status、task_updated_at=time.time()、actor=actor or "task" 等构成 kw,extra 为真值时经 kw.update(extra) 追加覆盖,随后调用 cg.add(nid, content, **kw) 并返回其结果;
213
+ def _write(cg, nid: str, name: str, content: str, status: str, tags,
214
+ importance: float, actor, extra: dict = None):
215
+ """唯一写盘点——走 `cg.add`(权限闸 / 归属注入全部复用既有链路)。
216
+
217
+ `override=True` 的含义:任务更新是**系统自身的幂等更新**(同 slug 即同任务),
218
+ 显式声明该意图后,若节点已带 `protected`/`self_state` 标记,protect 会走
219
+ 「旧版本快照 + 审计留痕」的放行分支而不是静默改写——任务台账天然获得版本快照。
220
+ """
221
+ kw = dict(layer="structural", tags=list(tags), importance=float(importance),
222
+ confidence=1.0, verification_basis="other",
223
+ override=True, consistency=False,
224
+ task_name=name, task_status=status,
225
+ task_updated_at=time.time(), actor=actor or "task")
226
+ if extra:
227
+ kw.update(extra)
228
+ return cg.add(nid, content, **kw)
229
+
230
+
231
+ # 生效条件:cg 具有 _audit 属性(getattr(cg, "_audit", None) 非 None)时以 (op, nid, **meta) 调用它,且其中抛出的任何异常被吞掉;cg 无该属性时不调用,两种路径均不返回内容。
232
+ def _audit(cg, op: str, nid: str, **meta) -> None:
233
+ """生命周期留痕;审计失败绝不阻断主流程(与 branches._audit 同哲学)。"""
234
+ a = getattr(cg, "_audit", None)
235
+ if a is None:
236
+ return
237
+ try:
238
+ a(op, nid, **meta)
239
+ except Exception: # noqa: BLE001
240
+ pass
241
+
242
+
243
+ # 生效条件:_read_task(cg, nid) 命中任务卡时返回摘要条目——id 取 rec['id'],name 取 fm.task_name 或(缺失/假值时)rec['id'],status 取 fm.task_status 或 "active",plan/changes/result 取对应节的 _brief 摘要(缺节回落 ""),并带 created_at/updated_at/path;未命中时返回 None。
244
+ def _entry(cg, nid: str):
245
+ """节点 id → 对外任务条目(摘要形态;全字段查 `get_task`)。"""
246
+ rec = _read_task(cg, nid)
247
+ if not rec:
248
+ return None
249
+ fm, sec = rec["fm"], rec["sec"]
250
+ return {"id": rec["id"],
251
+ "name": fm.get("task_name") or rec["id"],
252
+ "status": fm.get("task_status") or "active",
253
+ "plan": _brief(sec.get(SEC_PLAN, "")),
254
+ "changes": _brief(sec.get(SEC_CHANGE, "")),
255
+ "result": _brief(sec.get(SEC_RESULT, "")),
256
+ "created_at": fm.get("task_created_at"),
257
+ "updated_at": fm.get("task_updated_at"),
258
+ "path": rec["path"]}
259
+
260
+
261
+ # ---------------------------------------------------------------- 写操作
262
+
263
+ # 生效条件:slugify(name) 为空串或不匹配 _SLUG_RE 时返回 {'ok': False, 含 slug 的非法名 error};否则 st = str(status if _has(status) else (旧卡 task_status or "active")).strip().lower(),st 不在 TASK_STATUSES 时返回未知状态错误,st == "done" 且合并后结果节 _has(new_res) 为假时返回「转 done 必须填结果」拒收,其余情况渲染写入并返回按 old 是否为 None 区分新建/更新的 out。
264
+ def upsert(cg, name: str, *, plan: str = None, status: str = None,
265
+ result: str = None, condition: str = None, goal: str = None,
266
+ acceptance: str = None, boundary: str = None, change: str = None,
267
+ note: str = None, tags=None, importance: float = None,
268
+ actor: str = None) -> dict:
269
+ """登记或更新一张任务卡(同 slug 即同任务;重复登记不新建卡)。
270
+
271
+ 未提供的字段**沿用旧值**(不静默清空);`status="done"` 且合并后「结果」节
272
+ 仍为空 → 拒收,盘上内容不变。
273
+
274
+ :param note: 进度说明,落 `# 执行:` 行(`|` 之后)。
275
+ :param change: 本次发现的新问题/计划偏差,追加到「计划变更」节。
276
+ """
277
+ slug = slugify(name)
278
+ if not slug or not _SLUG_RE.match(slug):
279
+ return {"ok": False,
280
+ "error": "任务名非法(slug=%r);要求:中文/字母/数字起头,"
281
+ "仅含中文/字母/数字/_ . -,长度 ≤64" % (slug,)}
282
+
283
+ nid = TASK_PREFIX + slug
284
+ old = _read_task(cg, nid)
285
+ prev_fm = old["fm"] if old else {}
286
+ prev = old["sec"] if old else {}
287
+
288
+ display = (name or "").strip() or prev_fm.get("task_name") or slug
289
+ st = str(status if _has(status) else (prev_fm.get("task_status") or "active")).strip().lower()
290
+ if st not in TASK_STATUSES:
291
+ return {"ok": False, "node_id": nid,
292
+ "error": "未知任务状态:%r(可选 %s)" % (st, list(TASK_STATUSES))}
293
+
294
+ # 未提供 → 沿用旧值(结果与计划绝不被静默清空)
295
+ new_plan = plan if _has(plan) else prev.get(SEC_PLAN, "")
296
+ new_res = result if _has(result) else prev.get(SEC_RESULT, "")
297
+ new_cond = condition if _has(condition) else _field_line(old["content"], "生效条件") if old else ""
298
+ new_goal = goal if _has(goal) else _field_line(old["content"], "子功能") if old else ""
299
+ new_acc = acceptance if _has(acceptance) else _field_line(old["content"], "验证方式") if old else ""
300
+ new_bnd = boundary if _has(boundary) else _field_line(old["content"], "不适用条件") if old else ""
301
+ new_note = note if _has(note) else _exec_note(old["content"]) if old else ""
302
+ new_changes = _append_change(prev.get(SEC_CHANGE, ""), change)
303
+
304
+ if st == "done" and not _has(new_res):
305
+ return {"ok": False, "node_id": nid,
306
+ "error": "任务转 done 必须填写「结果」——结果不可缺失(缺一不收)",
307
+ "hint": "补 result 后重试;盘上内容未变更"}
308
+
309
+ content = render(display, condition=new_cond, goal=new_goal, status=st,
310
+ note=new_note, acceptance=new_acc, boundary=new_bnd,
311
+ plan=new_plan, changes=new_changes, result=new_res)
312
+ tg = list(dict.fromkeys([TASK_TAG, "%s:%s" % (TASK_TAG, slug)]
313
+ + [str(t) for t in (tags or []) if str(t).strip()]))
314
+ imp = DEFAULT_IMPORTANCE if importance is None else float(importance)
315
+ created = prev_fm.get("task_created_at") or time.time()
316
+ _write(cg, nid, display, content, st, tg, imp, actor,
317
+ extra={"task_created_at": created, "task_slug": slug})
318
+ _audit(cg, "task_open" if old is None else "task_update", nid,
319
+ status=st, slug=slug, name=display)
320
+ out = {"ok": True, "node_id": nid, "name": display, "status": st,
321
+ "created": old is None,
322
+ "result_present": _has(new_res),
323
+ "hint": "新建任务卡" if old is None else "已更新原卡(同 slug 即同任务)"}
324
+ if slug != (name or "").strip():
325
+ # 身份被归一化了就必须说出来——否则调用方按原名去找会找不到卡
326
+ out["slug_normalized"] = slug
327
+ return out
328
+
329
+
330
+ # 生效条件:content 中 `# 执行:` 行的值含 "|" 时返回第一个 "|" 之后去空白的内容;content 为假值、无该行或该行值不含 "|" 时返回 ""。
331
+ def _exec_note(content: str) -> str:
332
+ """从 `# 执行:` 行取 `|` 之后的进度说明。"""
333
+ val = _field_line(content, "执行")
334
+ return val.split("|", 1)[1].strip() if "|" in val else ""
335
+
336
+
337
+ # 生效条件:node_id 经 task_node_id 得到非空 nid 且 _read_task 命中该卡时,以旧卡 task_name(缺失/假值回落 nid)、status、result、note、actor 转调 upsert 并返回其结果;node_id 非法时返回 ok=False「非法任务标识」,卡不存在时返回 ok=False「任务不存在」。
338
+ def set_status(cg, node_id: str, status: str, *, result: str = None,
339
+ note: str = None, actor: str = None) -> dict:
340
+ """任务状态迁移(进行中/受阻/完成/放弃)。迁 done 且无结果 → 拒收。"""
341
+ nid = task_node_id(node_id)
342
+ if not nid:
343
+ return {"ok": False, "error": "非法任务标识:%r" % (node_id,)}
344
+ old = _read_task(cg, nid)
345
+ if not old:
346
+ return {"ok": False, "node_id": nid, "error": "任务不存在:%s" % nid}
347
+ return upsert(cg, old["fm"].get("task_name") or nid, status=status,
348
+ result=result, note=note, actor=actor)
349
+
350
+
351
+ # 生效条件:change 经 _has 判定为已提供、且 node_id 经 task_node_id 非空、_read_task 命中该卡时,以旧卡 task_name(缺失/假值回落 nid)与 change、actor 转调 upsert;change 为 None/空串/纯空白时返回 ok=False「计划变更内容为空」,node_id 非法或卡不存在时返回 ok=False 对应错误。
352
+ def plan_add(cg, node_id: str, change: str, *, actor: str = None) -> dict:
353
+ """计划变更追加——执行中发现的错误/新问题/偏差,累积进「计划变更」节。"""
354
+ if not _has(change):
355
+ return {"ok": False, "error": "计划变更内容为空"}
356
+ nid = task_node_id(node_id)
357
+ if not nid:
358
+ return {"ok": False, "error": "非法任务标识:%r" % (node_id,)}
359
+ old = _read_task(cg, nid)
360
+ if not old:
361
+ return {"ok": False, "node_id": nid, "error": "任务不存在:%s" % nid}
362
+ return upsert(cg, old["fm"].get("task_name") or nid, change=change, actor=actor)
363
+
364
+
365
+ # ---------------------------------------------------------------- 读操作
366
+
367
+ # 生效条件:node_id 经 task_node_id 得到的 nid 对应一张 _read_task 命中的任务卡时返回 ok=True 的全字段(condition/goal/acceptance/boundary 由 _field_line 抽取、note 由 _exec_note 抽取、plan/changes/result 取节原文不截断、tags 取 fm.tags 或 []);否则返回 ok=False,error 中的标识取 nid 或原 node_id。
368
+ def get_task(cg, node_id: str) -> dict:
369
+ """单卡全字段读回(计划/变更/结果不截断)。"""
370
+ nid = task_node_id(node_id)
371
+ rec = _read_task(cg, nid)
372
+ if not rec:
373
+ return {"ok": False, "error": "任务不存在:%s" % (nid or node_id)}
374
+ fm, sec = rec["fm"], rec["sec"]
375
+ return {"ok": True, "id": rec["id"], "name": fm.get("task_name") or rec["id"],
376
+ "status": fm.get("task_status") or "active",
377
+ "condition": _field_line(rec["content"], "生效条件"),
378
+ "goal": _field_line(rec["content"], "子功能"),
379
+ "note": _exec_note(rec["content"]),
380
+ "acceptance": _field_line(rec["content"], "验证方式"),
381
+ "boundary": _field_line(rec["content"], "不适用条件"),
382
+ "plan": sec.get(SEC_PLAN, ""),
383
+ "changes": sec.get(SEC_CHANGE, ""),
384
+ "result": sec.get(SEC_RESULT, ""),
385
+ "tags": list(fm.get("tags") or []),
386
+ "created_at": fm.get("task_created_at"),
387
+ "updated_at": fm.get("task_updated_at"),
388
+ "path": rec["path"]}
389
+
390
+
391
+ # 生效条件:status 经 `str(status or "").strip().lower()` 为非空且不在 TASK_STATUSES 时返回 ok=False 未知状态;否则收集 cg.index["nodes"] 中各 _entry 可读任务、按 updated_at 倒序,status 非空时再按该状态过滤,total 为过滤后截断前的条数,limit 为真值时取前 max(int(limit), 1) 条——limit 为 None、0 或空串(假值)时不截断而返回全量 filtered 列表。
392
+ def list_tasks(cg, status: str = None, limit: int = None) -> dict:
393
+ """任务清单(按 updated_at 倒序);status 可选 active/blocked/done/dropped。"""
394
+ st = str(status or "").strip().lower() or None
395
+ if st and st not in TASK_STATUSES:
396
+ return {"ok": False, "error": "未知任务状态:%r(可选 %s)" % (st, list(TASK_STATUSES))}
397
+ items = []
398
+ for nid in list((cg.index.get("nodes") or {}).keys()):
399
+ t = _entry(cg, nid)
400
+ if t:
401
+ items.append(t)
402
+ items.sort(key=lambda x: -(x.get("updated_at") or 0))
403
+ if st:
404
+ items = [t for t in items if t["status"] == st]
405
+ total = len(items)
406
+ if limit:
407
+ items = items[:max(int(limit), 1)]
408
+ return {"ok": True, "count": len(items), "total": total, "tasks": items}
409
+
410
+
411
+ # 生效条件:遍历 cg.index["nodes"],把 _entry 可读且 status 为 active/blocked 的归入 active、status 为 done 的归入 done,各自按 updated_at 倒序;返回 active[:max(int(active_limit), 0)] 与 done[:max(int(done_limit), 0)](active_limit/done_limit 传 0 或负数时对应列表为空)以及两组截断前的 active_total/done_total。
412
+ def session_tasks(cg, active_limit: int = 5, done_limit: int = 5) -> dict:
413
+ """会话装配用:进行中(active/blocked)+ 近期完成(done,按 updated_at 倒序)。
414
+
415
+ 这是第 ① 点(忘记已实现的工程)的机制解:上下文装配面在会话开始时把
416
+ 「还没做完的」与「刚做完的」一起带出来,agent 不必先想到去查。
417
+ """
418
+ active, done = [], []
419
+ for nid in list((cg.index.get("nodes") or {}).keys()):
420
+ t = _entry(cg, nid)
421
+ if not t:
422
+ continue
423
+ if t["status"] in ("active", "blocked"):
424
+ active.append(t)
425
+ elif t["status"] == "done":
426
+ done.append(t)
427
+ # 终键 id:同 updated_at 并列时定序,否则顺序回落到 cg.index["nodes"]
428
+ # 的物理序(增量路径=写入序,重建路径=nid 序)。
429
+ key = lambda x: (-(x.get("updated_at") or 0), # noqa: E731
430
+ str(x.get("id") or ""))
431
+ active.sort(key=key)
432
+ done.sort(key=key)
433
+ return {"ok": True,
434
+ "active": active[:max(int(active_limit), 0)],
435
+ "done": done[:max(int(done_limit), 0)],
436
+ "active_total": len(active), "done_total": len(done)}
437
+
438
+
439
+ # 生效条件:name 经 task_node_id 得 nid,exists 为同 id 任务卡是否被 _read_task 命中;k 经 `int(k or 5)` 再 max(…, 1)(k 为 None/0/空串回落 5,负数取 1)得到 kk,cg.search 抛异常时按无结果处理,只保留元素长度 ≥3、frontmatter.tags 含 TASK_TAG 且 node.id != nid 的条目,返回 out[:kk] 与 note。
440
+ def find_similar(cg, name: str, k: int = 5) -> dict:
441
+ """同族任务提示(**只提示,不自动合并**——是否同一任务由调用方裁决)。
442
+
443
+ 身份判据是命名而非相似度:这里只回答「已经有一张同名的卡吗」与
444
+ 「还有哪些卡看起来像」。相似度只能产生候选,不能授予「同一任务」的资格。
445
+ """
446
+ nid = task_node_id(name)
447
+ exact = _read_task(cg, nid) is not None
448
+ kk = max(int(k or 5), 1)
449
+ res = []
450
+ try:
451
+ res, _meta = cg.search(name, layer="structural", k=kk + (1 if exact else 0),
452
+ record=False)
453
+ except Exception: # noqa: BLE001
454
+ res = []
455
+ out = []
456
+ for item in res or []:
457
+ if not (isinstance(item, (tuple, list)) and len(item) >= 3):
458
+ continue
459
+ node, score, qual = item[0], item[1], item[2]
460
+ fm = (node.get("frontmatter") or {}) if isinstance(node, dict) else {}
461
+ if TASK_TAG not in (fm.get("tags") or []):
462
+ continue
463
+ if node.get("id") == nid:
464
+ continue
465
+ out.append({"id": node.get("id"),
466
+ "name": fm.get("task_name") or node.get("id"),
467
+ "status": fm.get("task_status"),
468
+ "score": round(float(score), 4),
469
+ "qualification": (qual or {}).get("state") if isinstance(qual, dict) else None})
470
+ return {"ok": True, "node_id": nid, "exists": exact, "similar": out[:kk],
471
471
  "note": "仅提示疑似同族任务,不自动合并——终裁权在调用方"}