@furongjun1999/dsh-memory 0.4.8 → 0.4.10

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 (232) hide show
  1. package/README.md +54 -26
  2. package/codebuddy/CODEBUDDY.md +196 -195
  3. package/codebuddy/README.md +13 -1
  4. package/codebuddy/mcp.json +9 -0
  5. package/docs/GBrain/345/217/257/345/200/237/351/211/264/347/202/271_/347/201/265/346/236/242/350/220/275/347/202/271/344/272/244/346/216/245_20260919.md +169 -0
  6. package/docs/README.md +1 -1
  7. package/docs/discipline/harnesses.yaml +18 -7
  8. package/docs/discipline/templates/full.md.tmpl +4 -3
  9. package/docs/experiments/linkref_backfill/candidates_20260917.json +726 -0
  10. package/docs/experiments/linkref_backfill/candidates_internal_20260917.json +602 -0
  11. package/docs/experiments/linkref_backfill/candidates_internal_v2.json +603 -0
  12. package/docs/experiments/linkref_backfill/candidates_secret_20260917.json +884 -0
  13. package/docs/experiments/linkref_backfill/candidates_secret_v2.json +789 -0
  14. package/docs/hive//345/244/232/347/253/257harness/351/200/232/344/277/241/345/245/221/347/272/246_v0.1.md +42 -0
  15. package/docs/hive//346/243/200/347/264/242/346/224/266/346/225/233/345/256/236/346/265/213/344/270/216S1b/350/256/276/350/256/241_v0.1.md +43 -0
  16. package/docs/hive//346/243/200/347/264/242/350/267/257/345/276/204/344/270/216/350/256/244/347/237/245/347/273/223/346/236/204/345/245/221/347/272/246_v0.1.md +102 -0
  17. package/docs/hive//347/234/237/345/256/236/345/272/223/347/253/257/345/210/260/347/253/257/345/256/236/346/265/213_S1b/344/270/216/345/217/254/345/233/236/346/235/203/350/241/241_v0.1.md +57 -0
  18. package/docs/hive//347/234/237/345/256/236/345/272/223/347/253/257/345/210/260/347/253/257/345/256/236/346/265/213_S7/345/200/222/346/216/222/345/200/231/351/200/211/345/261/202_v0.1.md +126 -0
  19. package/docs/hive//350/234/202/345/267/242/345/217/214/345/256/236/344/276/213/344/272/222/351/252/214_/350/256/276/350/256/241/345/256/232/347/250/277.md +503 -0
  20. package/docs/mdcg/D_meta_/345/267/245/347/250/213/345/214/226/346/226/271/346/241/210_v0.2.md +216 -0
  21. package/docs/mdcg/README/350/257/246/347/273/206/347/211/210_v0.4.5.md +631 -625
  22. package/docs/mdcg//344/273/243/347/240/201/350/257/204/345/256/241/344/270/216/346/235/241/344/273/266/345/214/226/346/263/250/351/207/212_/345/245/221/347/272/246_v0.1.md +82 -0
  23. package/docs/mdcg//345/205/250/345/272/223/344/273/243/347/240/201/350/257/204/345/256/241/344/270/216/346/235/241/344/273/266/345/214/226/346/263/250/351/207/212_/350/256/241/345/210/222_v0.1.md +600 -0
  24. 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 +47 -45
  25. package/docs/mdcg//345/255/220/344/273/243/347/220/206/351/205/215/347/275/256/346/240/207/345/207/{206_v0.4.md → 206_v0.5.md} +92 -4
  26. package/docs/mdcg//347/201/265/346/236/242/350/256/260/345/277/206/345/212/250/350/257/215/345/215/217/350/256/256_v1.0-draft.md +172 -0
  27. package/docs/mdcg//347/216/257/344/272/214_/347/231/275/347/256/261/345/241/253/345/205/205/346/265/201/346/260/264/347/272/277_v0.1.md +31 -0
  28. package/docs/mdcg//350/267/250/347/253/257/351/252/214/350/257/201/344/270/216/345/220/214/346/255/245/345/215/217/350/256/256_v0.1.md +93 -0
  29. 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 +2 -2
  30. 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 +3 -3
  31. 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 +2 -2
  32. package/docs//345/267/245/344/275/234/347/272/252/345/276/213_/350/256/244/347/237/245/345/233/276/346/235/241/347/233/256_v1.1.json +434 -433
  33. package/docs//347/201/265/346/236/242/350/207/252/346/210/221/346/224/271/350/277/233/345/267/245/344/275/234/350/256/241/345/210/222_/345/244/226/351/203/250/347/240/224/347/251/266/347/263/273/345/210/227/345/220/270/346/224/266_v1_20260919.md +286 -0
  34. package/dsh/README.md +33 -0
  35. package/dsh/cordis.yml.example +13 -7
  36. package/dsh/hive-mcp-probe.mjs +94 -0
  37. package/dsh/hive-mcp.example.yml +62 -0
  38. package/dsh/update-lingshu.bat +11 -0
  39. package/dsh/update-lingshu.ps1 +337 -0
  40. package/lib/hooks.d.ts +3 -0
  41. package/lib/hooks.js +17 -23
  42. package/lib/index.d.ts +4 -2
  43. package/lib/index.js +29 -6
  44. package/lib/lib/datapath.d.ts +76 -1
  45. package/lib/lib/datapath.js +199 -13
  46. package/lib/lib/mdcg_client.d.ts +40 -3
  47. package/lib/lib/mdcg_client.js +46 -22
  48. package/lib/lib/mutual.js +4 -4
  49. package/lib/lib/token_store.js +4 -5
  50. package/md_cg/audit.py +17 -2
  51. package/md_cg/autonomy.py +86 -15
  52. package/md_cg/backfill.py +36 -1
  53. package/md_cg/backfill_bigdomain.py +34 -0
  54. package/md_cg/bench6_arms.py +28 -1
  55. package/md_cg/bench6_common.py +10 -1
  56. package/md_cg/bench6_competitors.py +6 -1
  57. package/md_cg/bench_axis_domain.py +9 -1
  58. package/md_cg/bench_blind_comp.py +7 -1
  59. package/md_cg/bench_en_atoms_public.py +9 -0
  60. package/md_cg/bench_governance.py +348 -0
  61. package/md_cg/bench_lme_zh.py +16 -1
  62. package/md_cg/bench_locomo.py +2 -1
  63. package/md_cg/bench_locomo_zh.py +16 -1
  64. package/md_cg/bench_locomo_zh_public.py +4 -1
  65. package/md_cg/bench_longmem.py +2 -1
  66. package/md_cg/bench_membench.py +27 -1
  67. package/md_cg/bench_p0.py +4 -1
  68. package/md_cg/bench_progressive.py +13 -1
  69. package/md_cg/bench_role_views.py +238 -0
  70. package/md_cg/bench_task_ab.py +8 -1
  71. package/md_cg/bench_task_ab_llm.py +13 -1
  72. package/md_cg/bench_unified_en.py +6 -1
  73. package/md_cg/bench_zh_mad.py +20 -1
  74. package/md_cg/blindspot_tickets.py +123 -0
  75. package/md_cg/branches.py +12 -1
  76. package/md_cg/build_postings.py +73 -0
  77. package/md_cg/ccgc.py +67 -2
  78. package/md_cg/census.py +5 -1
  79. package/md_cg/chain.py +24 -3
  80. package/md_cg/codeindex.py +134 -17
  81. package/md_cg/coldverify.py +265 -0
  82. package/md_cg/comment_gate.py +338 -0
  83. package/md_cg/cond_compose.py +190 -0
  84. package/md_cg/cond_facts.py +155 -0
  85. package/md_cg/cond_template.json +107 -0
  86. package/md_cg/condition_anchor.py +143 -0
  87. package/md_cg/conformance.py +69 -4
  88. package/md_cg/consistency.py +24 -1
  89. package/md_cg/consolidate.py +53 -2
  90. package/md_cg/corpus.py +4 -0
  91. package/md_cg/crosscheck.py +42 -2
  92. package/md_cg/crypto.py +35 -1
  93. package/md_cg/d_meta.py +310 -0
  94. package/md_cg/datapath.py +201 -26
  95. package/md_cg/docindex.py +122 -1
  96. package/md_cg/eval_common.py +29 -1
  97. package/md_cg/evidence.py +27 -1
  98. package/md_cg/evolution.py +21 -1
  99. package/md_cg/export.py +11 -1
  100. package/md_cg/forgetting.py +23 -1
  101. package/md_cg/fsutil.py +18 -1
  102. package/md_cg/hotcache.py +214 -0
  103. package/md_cg/hyperedge.py +251 -0
  104. package/md_cg/identity.py +18 -1
  105. package/md_cg/insight.py +17 -1
  106. package/md_cg/lexicon/build_cedict_en_zh.py +9 -0
  107. package/md_cg/lexicon/build_standard_en.py +171 -168
  108. package/md_cg/lexicon/expand_en_zh.py +6 -0
  109. package/md_cg/lifecycle.py +12 -1
  110. package/md_cg/linkref.py +281 -0
  111. package/md_cg/links.py +29 -1
  112. package/md_cg/mcp_server.py +362 -43
  113. package/md_cg/md_whitebox.py +53 -1
  114. package/md_cg/mdcg.py +1003 -27
  115. package/md_cg/mdcos.py +558 -36
  116. package/md_cg/metacognition.py +37 -2
  117. package/md_cg/migrate.py +4 -0
  118. package/md_cg/migrate_aeis.py +221 -213
  119. package/md_cg/migrate_roleplay.py +8 -0
  120. package/md_cg/migrate_wisdom_graph.py +14 -1
  121. package/md_cg/mreview/__main__.py +3 -0
  122. package/md_cg/mreview/bundle.py +8 -0
  123. package/md_cg/mreview/candidates.py +9 -0
  124. package/md_cg/mreview/govern.py +21 -1
  125. package/md_cg/mreview/locate.py +34 -0
  126. package/md_cg/mreview/pipeline.py +29 -1
  127. package/md_cg/mreview/ruleset.py +16 -1
  128. package/md_cg/nodefile.py +233 -3
  129. package/md_cg/pooling.py +23 -1
  130. package/md_cg/postings.py +298 -0
  131. package/md_cg/predict.py +89 -9
  132. package/md_cg/progressive.py +3 -0
  133. package/md_cg/protect.py +14 -1
  134. package/md_cg/protocol.py +372 -0
  135. package/md_cg/provenance.py +262 -0
  136. package/md_cg/reach.py +453 -0
  137. package/md_cg/refindex.py +47 -2
  138. package/md_cg/refine.py +20 -1
  139. package/md_cg/roleviews.py +89 -0
  140. package/md_cg/routing.py +76 -0
  141. package/md_cg/scrub.py +63 -2
  142. package/md_cg/security.py +26 -1
  143. package/md_cg/self_state.py +64 -1
  144. package/md_cg/selfreport.py +151 -0
  145. package/md_cg/semantic/canonical.py +5 -0
  146. package/md_cg/semantic/en_normalizer.py +364 -355
  147. package/md_cg/semantic/zh_en_atoms.py +139 -136
  148. package/md_cg/signer.py +41 -1
  149. package/md_cg/sources.py +583 -547
  150. package/md_cg/statushdr.py +179 -0
  151. package/md_cg/stg.py +59 -18
  152. package/md_cg/subgraph.py +23 -0
  153. package/md_cg/sustain.py +56 -1
  154. package/md_cg/tasks.py +26 -2
  155. package/md_cg/test_autonomy.py +26 -0
  156. package/md_cg/test_bench_governance.py +102 -0
  157. package/md_cg/test_blindspot_tickets.py +166 -0
  158. package/md_cg/test_ccgc.py +10 -0
  159. package/md_cg/test_codeindex.py +338 -0
  160. package/md_cg/test_comment_gate.py +187 -0
  161. package/md_cg/test_cond_compose_anchors.py +76 -0
  162. package/md_cg/test_condition_anchor.py +82 -0
  163. package/md_cg/test_d_meta.py +412 -0
  164. package/md_cg/test_datapath_root.py +188 -0
  165. package/md_cg/test_gain_gate.py +47 -1
  166. package/md_cg/test_hot_cold.py +187 -0
  167. package/md_cg/test_hyperedge.py +245 -0
  168. package/md_cg/test_linkref.py +306 -0
  169. package/md_cg/test_md_access_parity.py +15 -3
  170. package/md_cg/test_mr_m1.py +108 -18
  171. package/md_cg/test_mr_m3.py +8 -1
  172. package/md_cg/test_p26_refindex.py +49 -20
  173. package/md_cg/test_p27_docindex.py +236 -2
  174. package/md_cg/test_p2_mcp.py +1 -1
  175. package/md_cg/test_p31_insight.py +24 -0
  176. package/md_cg/test_p44_md_whitebox.py +14 -1
  177. package/md_cg/test_protocol.py +243 -0
  178. package/md_cg/test_reach.py +378 -0
  179. package/md_cg/test_reach_keys.py +201 -0
  180. package/md_cg/test_reach_meta_exits.py +145 -0
  181. package/md_cg/test_read_clip.py +8 -4
  182. package/md_cg/test_retr_s1.py +340 -0
  183. package/md_cg/test_retr_s1b.py +209 -0
  184. package/md_cg/test_retr_s3.py +194 -0
  185. package/md_cg/test_retr_s4.py +163 -0
  186. package/md_cg/test_retr_s5.py +200 -0
  187. package/md_cg/test_retr_s6.py +157 -0
  188. package/md_cg/test_retr_s7.py +385 -0
  189. package/md_cg/test_retr_s8_time.py +316 -0
  190. package/md_cg/test_retr_s9_edges.py +286 -0
  191. package/md_cg/test_retr_s9_entity_ctx.py +175 -0
  192. package/md_cg/test_review_conformance.py +59 -2
  193. package/md_cg/test_role_views.py +354 -0
  194. package/md_cg/test_subproc_encoding.py +188 -0
  195. package/md_cg/test_trust.py +361 -0
  196. package/md_cg/test_units_poll.py +71 -0
  197. package/md_cg/test_v14_fixes.py +397 -0
  198. package/md_cg/test_validity_filter.py +280 -0
  199. package/md_cg/test_wisdom_md_store.py +7 -3
  200. package/md_cg/test_writepipe.py +5 -1
  201. package/md_cg/theory.py +16 -1
  202. package/md_cg/tokens.py +40 -8
  203. package/md_cg/tool_face.py +13 -2
  204. package/md_cg/trust.py +943 -0
  205. package/md_cg/twophase.py +12 -1
  206. package/md_cg/units.py +132 -10
  207. package/md_cg/vision_evidence.py +24 -1
  208. package/md_cg/weights.py +24 -1
  209. package/md_cg/whitebox.py +32 -1
  210. package/md_cg/whitebox_kb/data/verify_cache.json +21210 -365
  211. package/md_cg/whitebox_kb/data/verify_savings.jsonl +5078 -0
  212. package/md_cg/whitebox_kb/wisdom/audit_log/chain_heat.json +10 -10
  213. package/md_cg/whitebox_kb/wisdom/code_compose.py +113 -6
  214. package/md_cg/whitebox_kb/wisdom/code_solidified.json +1 -1
  215. package/md_cg/whitebox_kb/wisdom/verifier.py +340 -55
  216. package/md_cg/whitebox_kb/wisdom/wisdom-book-cloud.db +0 -0
  217. package/md_cg/writelimit.py +18 -4
  218. package/md_cg/writepipe.py +178 -7
  219. package/package.json +2 -2
  220. package/skills/skills/designer-perspective/scripts/__pycache__/designer.cpython-310.pyc +0 -0
  221. package/skills/skills/designer-perspective/scripts/designer.py +17 -1
  222. package/skills/skills/designer-perspective/tests/selftest.py +3 -1
  223. package/src/hooks.ts +17 -21
  224. package/src/index.ts +33 -6
  225. package/src/lib/datapath.ts +211 -13
  226. package/src/lib/mdcg_client.ts +64 -25
  227. package/src/lib/mutual.ts +411 -411
  228. package/src/lib/token_store.ts +4 -5
  229. package/zcode/AGENTS.md +196 -195
  230. package/zcode/README.md +4 -0
  231. package/md_cg/whitebox_kb/wisdom/wisdom-book-cloud.db-shm +0 -0
  232. package/md_cg/whitebox_kb/wisdom/wisdom-book-cloud.db-wal +0 -0
@@ -79,6 +79,7 @@ SYSTEM_PROMPT = """你是记忆评审流水线中的【验证单元】。
79
79
 
80
80
  # ---- 级 4-A · spec 构造 ---------------------------------------------------
81
81
 
82
+ # 生效条件:exe 为真值时返回 str(exe),否则 HIVE_EXE 经 (os.environ.get("HIVE_EXE") or "").strip() 后非空则返回该值,否则按 os.name 返回 os.path.join(_ROOT, "hive", "target", "release", "hive.exe" 或 "hive");
82
83
  def hive_exe(exe=None) -> str:
83
84
  """蜂巢可执行文件:显式参数 > HIVE_EXE > 仓内 release 构建。"""
84
85
  if exe:
@@ -90,10 +91,12 @@ def hive_exe(exe=None) -> str:
90
91
  return os.path.join(_ROOT, "hive", "target", "release", name)
91
92
 
92
93
 
94
+ # 生效条件:name 为假值(None/""/0/False 等)时以 "pkg" 为输入,否则 str(name),结果经 re.sub(r"[^0-9A-Za-z_.\-]", "_", ...) 替换并截取前 120 字符;
93
95
  def _safe(name) -> str:
94
96
  return re.sub(r"[^0-9A-Za-z_.\-]", "_", str(name or "pkg"))[:120]
95
97
 
96
98
 
99
+ # 生效条件:path 与 obj 给定后,若 os.path.dirname(path) 非空则 os.makedirs 该目录,随后以 UTF-8、ensure_ascii=False、indent=2 将 obj 写入 path 并返回 path;
97
100
  def _dump_json(path: str, obj) -> str:
98
101
  d = os.path.dirname(path)
99
102
  if d:
@@ -109,6 +112,7 @@ _ENTRY_KEYS = ("ref", "node_id", "proposal_id", "origin", "layer", "tags", "role
109
112
  "excerpt")
110
113
 
111
114
 
115
+ # 生效条件:pkg/asm/workdir 给定,base 取 _safe(pkg.get("bundle_id"))(bundle_id 假值→"pkg"),entries 取 pkg.get("entries") or [] 的前 int(entry_limit) 项且仅保留 _ENTRY_KEYS,写出 workdir/base.bundle.json 与 workdir/base.rules.json(asm 原样),返回这两个 join(workdir,...) 路径(workdir 相对时不保证绝对);
112
116
  def dump_context(pkg: dict, asm: dict, workdir: str, *, entry_limit=64) -> list:
113
117
  """包 + 规则装配 → context 文件(**绝对路径**,工人执行期内有效)。
114
118
 
@@ -128,6 +132,7 @@ def dump_context(pkg: dict, asm: dict, workdir: str, *, entry_limit=64) -> list:
128
132
  return [bp, rp]
129
133
 
130
134
 
135
+ # 生效条件:pkg/asm 给定,节点清单标题恒输出、节点项取 pkg.get("entries") or [];机械命中取 asm.get("mechanical") or [],非空列前 int(max_mech) 条否则输出「无」,llm 取 asm.get("llm") or [] 非空列前 40 条;返回 join(L);
131
136
  def build_prompt(pkg: dict, asm: dict, *, max_mech=120) -> str:
132
137
  """user_prompt:节点清单 + 机械命中 + 待检问题 + 输出要求。"""
133
138
  ents = pkg.get("entries") or []
@@ -167,6 +172,7 @@ def build_prompt(pkg: dict, asm: dict, *, max_mech=120) -> str:
167
172
  return "\n".join(L)
168
173
 
169
174
 
175
+ # 生效条件:pkg/asm 给定,model 按 model or os.environ.get('HIVE_MODEL') or DEFAULT_MODEL 取值(空串/假值均回落),context_files 按 list(context_files or []),timeout_s 转 int、temperature 转 float、max_tokens 转 int,node_ids 取 pkg.entries 中 node_id 真值的 str,返回含 meta 的 spec 字典;
170
176
  def build_spec(pkg: dict, asm: dict, *, context_files=None, model=None,
171
177
  timeout_s=600, temperature=0.0, max_tokens=4096) -> dict:
172
178
  """(pkg, asm) → hive spec(文件协议的可投递单元)。"""
@@ -186,6 +192,7 @@ def build_spec(pkg: dict, asm: dict, *, context_files=None, model=None,
186
192
  "size": pkg.get("size"), "node_ids": nids}}
187
193
 
188
194
 
195
+ # 生效条件:当模块级常量 CD.SOURCES 可用时,sources 为假值则回落 CD.SOURCES,调用 CD.generate 生成候选,再经 BD.bundle(c.get('candidates') or [], nodes, root, max_per_bundle) 与 RS.assemble_all(b, rules_dir) 组装,最终将 bundles 与 packages 按 zip 配对返回。
189
196
  def build_packages(*, root=None, sources=None, limit=None, nodes=None,
190
197
  max_per_bundle=50, rules_dir=None, now=None) -> dict:
191
198
  """级 1-3 串联:候选 → 捆包 → 规则装配,产出配对的 (pkg, asm) 列表。"""
@@ -202,6 +209,7 @@ def build_packages(*, root=None, sources=None, limit=None, nodes=None,
202
209
 
203
210
  # ---- 级 4-B · 投递 / 收卷 -------------------------------------------------
204
211
 
212
+ # 生效条件:argv 给定,环境变量副本强制 PYTHONUTF8="1",env 真值时用其 str 值更新,随后 subprocess.run(argv, capture_output=True, text=True, encoding="utf-8", errors="replace", env=e, timeout=timeout, cwd=cwd, input=stdin_text, shell=False);
205
213
  def _run(argv, *, env=None, timeout=120, cwd=None, stdin_text=None):
206
214
  """统一子进程入口:argv 列表 + 显式 UTF-8 + PYTHONUTF8=1,不经 shell(第15条)。"""
207
215
  e = dict(os.environ)
@@ -213,6 +221,7 @@ def _run(argv, *, env=None, timeout=120, cwd=None, stdin_text=None):
213
221
  input=stdin_text, shell=False)
214
222
 
215
223
 
224
+ # 生效条件:text 为假值时 (text or "").strip().splitlines() 为空、循环不执行并返回 None;否则从末尾行向前找以 "{" 开头且 json.loads 成功且 isinstance(obj, dict) 的行返回该 dict,未找到返回 None;
216
225
  def _last_json(text: str):
217
226
  """stdout 逐行解析取最后一个 JSON 对象(容忍前导日志行)。"""
218
227
  for line in reversed((text or "").strip().splitlines()):
@@ -228,6 +237,7 @@ def _last_json(text: str):
228
237
  return None
229
238
 
230
239
 
240
+ # 生效条件:exe/jobs_dir/spec 给定,创建 jobs_dir 并在临时目录写 spec.json 后执行 [exe,"submit","--spec",sp,"--jobs",jobs_dir];returncode 非 0 返回 ok False + error,否则 _last_json(p.stdout) 为假(含 None/空 dict)返回 ok False「submit 输出非 JSON」,否则 d.setdefault("ok", True) 后返回 d;
231
241
  def submit(exe: str, jobs_dir: str, spec: dict, *, timeout=120) -> dict:
232
242
  """投递单包 spec → {"ok":True,"job_id":...};失败返回 ok=False + error。"""
233
243
  os.makedirs(jobs_dir, exist_ok=True)
@@ -245,11 +255,13 @@ def submit(exe: str, jobs_dir: str, spec: dict, *, timeout=120) -> dict:
245
255
  return d
246
256
 
247
257
 
258
+ # 生效条件:specs 为可迭代列表时逐项调用 submit(exe, jobs_dir, s, timeout=timeout) 并返回等长列表;specs 为空则返回空列表;
248
259
  def submit_many(exe: str, jobs_dir: str, specs: list, *, timeout=120) -> list:
249
260
  """批量投递(hive 侧并发消费;本函数逐条投递并保序配对)。"""
250
261
  return [submit(exe, jobs_dir, s, timeout=timeout) for s in specs]
251
262
 
252
263
 
264
+ # 生效条件:exe/jobs_dir 给定,job_id 为真值时 argv 追加 str(job_id),假值(None/"")时仅列全部;运行后 returncode 非 0 返回 ok False + error,否则 _last_json(p.stdout) 为真值(非空 dict)时返回该 dict,为假值时返回 ok False「poll 输出非 JSON」;
253
265
  def poll(exe: str, jobs_dir: str, job_id=None, *, timeout=120) -> dict:
254
266
  """查询 job 状态(不传 job_id = 列出全部)。"""
255
267
  argv = [exe, "poll"]
@@ -265,10 +277,12 @@ def poll(exe: str, jobs_dir: str, job_id=None, *, timeout=120) -> dict:
265
277
  "stdout": (p.stdout or "")[:800]}
266
278
 
267
279
 
280
+ # 生效条件:j.get("job_id") 为真值时返回该值,否则返回 j.get("id")(若 id 为 0/""/False 则原样返回该假值,若 id 缺失返回 None);
268
281
  def _job_id(j: dict):
269
282
  return j.get("job_id") or j.get("id")
270
283
 
271
284
 
285
+ # 生效条件:j.get("state") 为真值时返回 str(state);否则 j.get("status") 为 dict 时返回 str(status.get("state") or "");否则返回 str(status or "");
272
286
  def _job_state(j: dict) -> str:
273
287
  """state 读取兼容两种形态(扁平 / status 嵌套);仅供轮询判终态。"""
274
288
  s = j.get("state")
@@ -280,6 +294,7 @@ def _job_state(j: dict) -> str:
280
294
  return str(st or "")
281
295
 
282
296
 
297
+ # 生效条件:exe/jobs_dir 给定,job_ids 真值集合限观测;循环 poll 直到 pending 为空且(want 为 None 或 want <= 已观测)返回 ok=not failed,若 pending 非空或 want 未全观测且到 max(1.0,float(timeout_s)) 截止则返回 ok False + error,每次间隔 sleep(max(0.05,float(interval_s)));
283
298
  def wait_jobs(exe: str, jobs_dir: str, job_ids=None, *, timeout_s=900,
284
299
  interval_s=1.0, on_tick=None) -> dict:
285
300
  """轮询至全部终态(或超时)→ done/failed/pending 三分类 + 原始快照。"""
@@ -321,6 +336,7 @@ def wait_jobs(exe: str, jobs_dir: str, job_ids=None, *, timeout_s=900,
321
336
  time.sleep(max(0.05, float(interval_s)))
322
337
 
323
338
 
339
+ # 生效条件:jobs_dir/job_id 给定,join(jobs_dir,str(job_id),"result.json") 路径缺失时返回 ok False「result.json 不存在」,读取时 OSError/ValueError 返回 ok False「读取失败」,json 对象非 dict 返回 ok False「result 非对象」,否则返回该 dict;
324
340
  def read_result(jobs_dir: str, job_id: str) -> dict:
325
341
  """读工人产出(job 目录的 result.json)。"""
326
342
  p = os.path.join(jobs_dir, str(job_id), "result.json")
@@ -334,6 +350,7 @@ def read_result(jobs_dir: str, job_id: str) -> dict:
334
350
  return obj if isinstance(obj, dict) else {"ok": False, "error": "result 非对象"}
335
351
 
336
352
 
353
+ # 生效条件:jobs_dir/job_ids 给定,job_ids 为假值(None/[])时返回空 dict,否则返回 {str(j): read_result(jobs_dir, j) for j in job_ids};
337
354
  def collect(jobs_dir: str, job_ids) -> dict:
338
355
  """按 job_id 收卷(读 result.json),返回 {job_id: result} 保序映射。"""
339
356
  return {str(j): read_result(jobs_dir, j) for j in (job_ids or [])}
@@ -341,6 +358,7 @@ def collect(jobs_dir: str, job_ids) -> dict:
341
358
 
342
359
  # ---- 级 5-A · 意见解析与格式校验(校验率 100% 是验收口径)-----------------
343
360
 
361
+ # 生效条件:text 去空格后为空返回 None;否则先 try json.loads(整体),成功返回;否则找 ```(?:json)?...``` 围栏内 json.loads 成功返回;否则取 s.find("{") 与 s.rfind("}") 且 0<=i<k 的切片 json.loads 成功返回;均失败返回 None;
344
362
  def _extract_json(text: str):
345
363
  """从模型文本抽 JSON 对象:整体 → ```围栏``` → 首尾大括号切片。"""
346
364
  s = (text or "").strip()
@@ -365,6 +383,7 @@ def _extract_json(text: str):
365
383
  return None
366
384
 
367
385
 
386
+ # 生效条件:result 非 dict 返回 ok False;result.get("error") 真值返回 ok False「工人报错」;v=result.get("verdicts"),v is None 时从 result.get("content") or "" 经 _extract_json 取 dict,非 dict 返回 ok False,否则 v=obj.get("verdicts") 且 summary=obj.get("summary") or summary;v 非 list 返回 ok False;否则返回 ok True、verdicts=v、summary=str(summary);
368
387
  def parse_opinions(result: dict) -> dict:
369
388
  """result.json → {"ok":True,"verdicts":[...],"summary":...}。
370
389
 
@@ -389,11 +408,13 @@ def parse_opinions(result: dict) -> dict:
389
408
  return {"ok": True, "verdicts": v, "summary": str(summary)}
390
409
 
391
410
 
411
+ # 生效条件:v 经 str(v or "").strip().upper() 得 s,若 s 在模块级 VERDICTS 中则返回 s,否则返回 None;
392
412
  def _norm_verdict(v):
393
413
  s = str(v or "").strip().upper()
394
414
  return s if s in VERDICTS else None
395
415
 
396
416
 
417
+ # 生效条件:op 非 dict 返回「意见非对象」;nid 为空返回「缺 node_id」;node_ids 非 None 且 nid 不在其中返回越界错误;_norm_verdict(op.get("verdict")) 为 None 返回「verdict 越界」;reason 去空格空返回「缺 reason」;vd=="REJECT" 且 evidence 去空格空返回「REJECT 缺 evidence」;否则返回 None;
397
418
  def validate_opinion(op, node_ids=None):
398
419
  """单条意见格式校验:合法返回 None,非法返回错误串。"""
399
420
  if not isinstance(op, dict):
@@ -413,6 +434,7 @@ def validate_opinion(op, node_ids=None):
413
434
  return None
414
435
 
415
436
 
437
+ # 生效条件:verdicts 为假值(None/[])时 total=0、rate=1.0、ok=False;否则逐条 validate_opinion,无效记入 invalid,有效项复制并写入 _norm_verdict(op.get("verdict")),最后返回 total、valid、invalid、rate=len(valid)/total、ok=bool(valid) and not invalid;
416
438
  def validate_opinions(verdicts, node_ids=None) -> dict:
417
439
  """批量校验:valid/invalid 明细 + 通过率(用于 100% 口径断言)。"""
418
440
  total = len(verdicts or [])
@@ -434,6 +456,7 @@ def validate_opinions(verdicts, node_ids=None) -> dict:
434
456
 
435
457
  # ---- 级 5-B · 落库(verify 令牌 + writepipe 六道闸;不自造通道)-----------
436
458
 
459
+ # 生效条件:当 actor 给出时,从 TK.role_spec('verify') 读取 clearance_cap/can_write/can_admin/layers_allow 构造 Principal;session 为假值时生成 'mrev_' + uuid 前 12 位;layers_allow 为假值时回落 spec.get('layers_allow') 或 [];ops_allow 原样传入。
437
460
  def verifier_principal(*, actor="mreview-verifier", session=None,
438
461
  layers_allow=None, ops_allow=None) -> Principal:
439
462
  """验证单元 Principal——角色规格取自 tokens 真源(避免手写漂移)。"""
@@ -447,6 +470,7 @@ def verifier_principal(*, actor="mreview-verifier", session=None,
447
470
  ops_allow=ops_allow, auth_mode="mreview")
448
471
 
449
472
 
473
+ # 生效条件:pkg、opinions、reviewer 给定时恒返回 "\n".join(L);summary 为真值才追加「整包结论」行,invalid 为真值才追加「【未落库条目】」行,二者取默认 "" / None 时不追加;
450
474
  def opinion_doc(pkg: dict, opinions: list, *, reviewer: str, summary="",
451
475
  invalid=None) -> str:
452
476
  """评审意见节点正文(核心修改四要素:内容/原因/位置/验证)。"""
@@ -487,6 +511,7 @@ def opinion_doc(pkg: dict, opinions: list, *, reviewer: str, summary="",
487
511
  return "\n".join(L)
488
512
 
489
513
 
514
+ # 生效条件:cg/pkg/opinions/reviewer/applier 给定,CC.detect_self_verify 判 reviewer 与 applier 同一→refused self_verify_disallowed;opinions 为空→ok True skipped no_opinions;dry_run 真→ok True committed False;否则 (pipe or WP.default_pipeline()).execute,AccessDenied→refused layer_denied,其他异常→write_error,成功→ok 取 out.ok、committed 取 out.committed、moved_to 取 out.moved_to、deferred=bool(out.moved_to) and not out.committed;
490
515
  def apply_opinions(cg, pkg: dict, opinions: list, *, reviewer, applier,
491
516
  dry_run=False, pipe=None, layer=OPINION_LAYER,
492
517
  importance=0.4, tags=None, node_id=None,
@@ -550,6 +575,7 @@ def apply_opinions(cg, pkg: dict, opinions: list, *, reviewer, applier,
550
575
 
551
576
  # ---- 编排 ----------------------------------------------------------------
552
577
 
578
+ # 生效条件:pkg/asm/cg 给定,exe=hive_exe(exe),jobs_dir/ctx_dir 假值回落到 tempfile.gettempdir() 下路径;dump_context 后 build_spec、submit,若 submit 无 ok 或无 _job_id 返回 stage submit;wait_jobs 后若 failed 或 done 空返回 stage wait;read_result+parse_opinions 失败返回 stage parse;否则 validate_opinions 并 apply_opinions,返回 ok=app.ok 及计数;
553
579
  def run_package(*, pkg, asm, cg, exe=None, jobs_dir=None, ctx_dir=None,
554
580
  reviewer="mreview-worker", applier="mreview-applier",
555
581
  model=None, timeout_s=900, interval_s=1.0, dry_run=False,
@@ -590,6 +616,7 @@ def run_package(*, pkg, asm, cg, exe=None, jobs_dir=None, ctx_dir=None,
590
616
  "apply": app, "stages": {"submit": True, "wait": True, "parse": True}}
591
617
 
592
618
 
619
+ # 生效条件:pairs/cg 给定,先对每对 pkg,asm 执行 dump_context/build_spec/submit,收集 ok 且有 job_id 的 jids;wait_jobs 后逐 jid 若 state!="done" 记 stage wait,否则 read_result/parse_opinions/validate_opinions/apply_opinions 并累计;返回 ok=all(results.ok) and bool(results) 及聚合计数;
593
620
  def run_batch(*, pairs, cg, exe=None, jobs_dir=None, ctx_dir=None,
594
621
  reviewer="mreview-worker", applier="mreview-applier", model=None,
595
622
  timeout_s=1800, interval_s=1.0, dry_run=False, on_tick=None) -> dict:
@@ -666,6 +693,7 @@ def run_batch(*, pairs, cg, exe=None, jobs_dir=None, ctx_dir=None,
666
693
 
667
694
  # ---- CLI -----------------------------------------------------------------
668
695
 
696
+ # 生效条件:当 argv 给出时,argparse 解析命令行;root 取 a.root 或 os.environ.get('MDCG_ROOT'),若 root 为假值则 SystemExit;否则用 verifier_principal(actor=a.applier) 构造 MdCGSecure,build_packages(root=root, limit=None) 得到 pairs,按 a.packages(假值 0 则取全部)切片,run_batch 以 dry_run=(a.dry_run or not a.apply) 运行,打印 JSON 报告,返回 0(rep['ok'] 为真)或 1。
669
697
  def main(argv=None): # pragma: no cover
670
698
  import argparse
671
699
  ap = argparse.ArgumentParser(prog="python -m md_cg.mreview.pipeline",
@@ -698,4 +726,4 @@ def main(argv=None): # pragma: no cover
698
726
 
699
727
 
700
728
  if __name__ == "__main__": # pragma: no cover
701
- raise SystemExit(main())
729
+ raise SystemExit(main())
@@ -35,12 +35,14 @@ SEVERITIES = ("INFO", "WARN", "HIGH")
35
35
  _BLANK = (None, "", "None", "[]", "{}")
36
36
 
37
37
 
38
+ # 生效条件:v 为 list/tuple/dict 时返回 not v(空容器为 True、非空为 False),其余类型返回 v is None 或 str(v).strip() 属于 ("", "None")。
38
39
  def _blank(v) -> bool:
39
40
  if isinstance(v, (list, tuple, dict)):
40
41
  return not v
41
42
  return v is None or str(v).strip() in ("", "None")
42
43
 
43
44
 
45
+ # 生效条件:int(v) 可转换时返回该整数值,v 触发 TypeError 或 ValueError 时返回 0(源码未校验正负或范围)。
44
46
  def _as_int(v) -> int:
45
47
  try:
46
48
  return int(v)
@@ -48,6 +50,7 @@ def _as_int(v) -> int:
48
50
  return 0
49
51
 
50
52
 
53
+ # 生效条件:以 entry.get("ref")/entry.get("node_id")(缺键得 None)、field、kind、evidence、rule["id"](rule 缺 id 键抛 KeyError)组装 dict,severity 取 severity or rule.get("severity") or "WARN",故 severity 传 None/空串时依次回落到规则 severity、再回落 "WARN"。
51
54
  def _issue(entry, rule, kind, field, evidence, severity=None) -> dict:
52
55
  return {"ref": entry.get("ref"), "node_id": entry.get("node_id"),
53
56
  "field": field, "issue_kind": kind, "evidence": evidence,
@@ -56,18 +59,21 @@ def _issue(entry, rule, kind, field, evidence, severity=None) -> dict:
56
59
 
57
60
  # ---------------------------- 机械检查器注册表 ----------------------------
58
61
 
62
+ # 生效条件:field 取 spec.get("field")(缺键为 None),对每个 _blank(e.get(field)) 为真的条目生成一条 issue,issue_kind 取 spec.get("issue_kind") or "missing_field"(空串/None 回落 "missing_field"),无此类条目时返回空列表。
59
63
  def _chk_field_absent(entries, spec, rule):
60
64
  field = spec.get("field")
61
65
  return [_issue(e, rule, spec.get("issue_kind") or "missing_field", field,
62
66
  "字段 %s 为空" % field) for e in entries if _blank(e.get(field))]
63
67
 
64
68
 
69
+ # 生效条件:issue_kind 取 spec.get("issue_kind") or "missing_field",field 固定为 "evidence_count",对每个 _as_int(e.get("evidence_count")) == 0 的条目生成一行证据计数 issue(缺键、空串、非法值均折算为 0 而命中),无此类条目时返回空列表。
65
70
  def _chk_evidence_zero(entries, spec, rule):
66
71
  return [_issue(e, rule, spec.get("issue_kind") or "missing_field", "evidence_count",
67
72
  "evidence_count=0(无验证证据计数)")
68
73
  for e in entries if _as_int(e.get("evidence_count")) == 0]
69
74
 
70
75
 
76
+ # 生效条件:field 取 spec.get("field")、thr 取 float(spec.get("ratio", 1.0))(缺 ratio 键默认 1.0),非空 field 条目数除以 max(len(entries), 1) 得 ratio,ratio < thr 时返回一条 ref/node_id 为 None、issue_kind="package_ratio"、severity 取 rule.get("severity") or "INFO" 的 issue,ratio >= thr 时返回 []。
71
77
  def _chk_field_ratio_below(entries, spec, rule):
72
78
  field, thr = spec.get("field"), float(spec.get("ratio", 1.0))
73
79
  ratio = sum(1 for e in entries if not _blank(e.get(field))) / max(len(entries), 1)
@@ -78,6 +84,7 @@ def _chk_field_ratio_below(entries, spec, rule):
78
84
  "rule_id": rule["id"], "severity": rule.get("severity") or "INFO"}]
79
85
 
80
86
 
87
+ # 生效条件:h 取 str(e.get("content_hash") or "")(缺键或假值均为 ""),_blank(h) 或 h == "None" 的条目跳过,h 已在 seen 中时按 dup_content/"content_hash" 出 issue,首次出现则以 seen[h]=e.get("ref") 记录,返回累积的 out。
81
88
  def _chk_dup_hash_group(entries, spec, rule):
82
89
  seen, out = {}, []
83
90
  for e in entries:
@@ -92,6 +99,7 @@ def _chk_dup_hash_group(entries, spec, rule):
92
99
  return out
93
100
 
94
101
 
102
+ # 生效条件:骨架 sk 取 WL._skeleton(e.get("excerpt") or ""),sk 为空或 len(sk) < WL.MIN_SKELETON 的条目跳过,sk 已在 seen 中时按 template_flow/"content" 出 issue,首次出现则记 seen[sk]=e.get("ref"),返回累积的 out。
95
103
  def _chk_template_flow(entries, spec, rule):
96
104
  seen, out = {}, []
97
105
  for e in entries:
@@ -106,6 +114,7 @@ def _chk_template_flow(entries, spec, rule):
106
114
  return out
107
115
 
108
116
 
117
+ # 生效条件:basis 取 e.get("verification_basis"),_blank(basis) 的条目跳过;其余条目以 CC.classify_track({"layer": e.get("layer"), "tags": e.get("tags") or []}, e.get("excerpt") or "") 判 CC.basis_licensed(track, basis),为假时按 weak_source/"verification_basis" 出 issue,返回 out。
109
118
  def _chk_basis_licensed(entries, spec, rule):
110
119
  out = []
111
120
  for e in entries:
@@ -134,6 +143,7 @@ MECH_CHECKS = {
134
143
 
135
144
  # ---------------------------- 规则库加载 ----------------------------
136
145
 
146
+ # 生效条件:d 取 rules_dir or RULES_DIR(rules_dir 为 None 或空串时回落 RULES_DIR),os.path.isdir(d) 为假则 files 为空列表;为目录时读其中 *.json,任一规则缺 id/matcher/severity、severity 不在 SEVERITIES、id 重复、mechanical 项 check 不在 MECH_CHECKS 均抛 ValueError,全部合法则返回 {"rules_dir": d, "files": files, "rules": rules, "count": len(rules)}。
137
147
  def load_rules(rules_dir=None) -> dict:
138
148
  """读 `rules/*.json`。格式非法/字段缺失/id 重复 → 抛 ValueError(不静默降级)。"""
139
149
  d = rules_dir or RULES_DIR
@@ -159,6 +169,7 @@ def load_rules(rules_dir=None) -> dict:
159
169
  return {"rules_dir": d, "files": files, "rules": rules, "count": len(rules)}
160
170
 
161
171
 
172
+ # 生效条件:m 取 rule.get("matcher") or {};m["layer"] 为真且与 ctx["layers"] 无交集时返回 (False, "包内无 X 层条目"),m["tags_any"] 为真且与 ctx["tag_prefixes"] 无交集时返回 (False, 标签提示),两者均不触发时返回 (True, "matcher 命中")。
162
173
  def _match(rule, ctx) -> tuple:
163
174
  """matcher 语义:包内**存在**该类条目即入包(执行时按同条件过滤)。"""
164
175
  m = rule.get("matcher") or {}
@@ -169,6 +180,7 @@ def _match(rule, ctx) -> tuple:
169
180
  return True, "matcher 命中"
170
181
 
171
182
 
183
+ # 生效条件:rule.get("matcher") or {} 的 "layer" 为真时,返回 str(e.get("layer")) 落在 set(m["layer"]) 内的条目列表;matcher 无 layer 或为假值时返回 list(entries) 全量。
172
184
  def _scope(entries, rule) -> list:
173
185
  m = rule.get("matcher") or {}
174
186
  if m.get("layer"):
@@ -177,6 +189,7 @@ def _scope(entries, rule) -> list:
177
189
  return list(entries)
178
190
 
179
191
 
192
+ # 生效条件:rules 为 None 时返回 load_rules(rules_dir)["rules"];rules 为 dict 且不含 "rules" 键时抛 ValueError,含该键时返回 list(rules.get("rules") or []);其余形态返回 list(rules)。
180
193
  def _as_rules(rules, rules_dir=None) -> list:
181
194
  """规则入参防呆:None→读规则库;{"rules":[...]}→取内层;列表→原样。
182
195
 
@@ -191,6 +204,7 @@ def _as_rules(rules, rules_dir=None) -> list:
191
204
  return list(rules)
192
205
 
193
206
 
207
+ # 生效条件:pkg 为 dict 时 entries 取 pkg.get("entries") or [],规则先经 _as_rules(rules, rules_dir) 归一,ctx 由 entries 的 layer 与 tags 冒号前缀集构成,逐条规则 _match 命中后经 _scope 过滤并对 mechanical 项调 MECH_CHECKS[spec["check"]]、对 llm 项列出 scope_refs 前 200,返回含 bundle_id/group_kind/group_key/size/matched/mechanical/llm/mechanical_by_kind/mechanical_flagged 的 dict。
194
208
  def assemble(pkg: dict, *, rules=None, rules_dir=None) -> dict:
195
209
  """单个待评包 → 规则集 + 机械检查结果 + 待 LLM 检查清单。"""
196
210
  rl = _as_rules(rules, rules_dir)
@@ -222,6 +236,7 @@ def assemble(pkg: dict, *, rules=None, rules_dir=None) -> dict:
222
236
  "mechanical_flagged": len({i.get("ref") for i in mech if i.get("ref")})}
223
237
 
224
238
 
239
+ # 生效条件:result 为 dict 时规则先经 _as_rules(rules, rules_dir) 得 rl,再对 result.get("bundles") or [] 逐包调用 assemble(p, rules=rl)(rules_dir 不向 assemble 透传),返回 {"packages": out, "stats": {bundles/mechanical_total/mechanical_by_kind/llm_checks/rules}, "bundles_input": result.get("stats")}。
225
240
  def assemble_all(result: dict, *, rules=None, rules_dir=None) -> dict:
226
241
  """全部包装配 + 汇总(包级统计供 M2 spec 与 M4 量化复跑)。"""
227
242
  rl = _as_rules(rules, rules_dir)
@@ -235,4 +250,4 @@ def assemble_all(result: dict, *, rules=None, rules_dir=None) -> dict:
235
250
  "mechanical_by_kind": dict(kinds),
236
251
  "llm_checks": sum(len(a["llm"]) for a in out),
237
252
  "rules": [r["id"] for r in rl]},
238
- "bundles_input": result.get("stats")}
253
+ "bundles_input": result.get("stats")}