@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.
- package/README.md +54 -26
- package/codebuddy/CODEBUDDY.md +196 -195
- package/codebuddy/README.md +13 -1
- package/codebuddy/mcp.json +9 -0
- 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
- package/docs/README.md +1 -1
- package/docs/discipline/harnesses.yaml +18 -7
- package/docs/discipline/templates/full.md.tmpl +4 -3
- package/docs/experiments/linkref_backfill/candidates_20260917.json +726 -0
- package/docs/experiments/linkref_backfill/candidates_internal_20260917.json +602 -0
- package/docs/experiments/linkref_backfill/candidates_internal_v2.json +603 -0
- package/docs/experiments/linkref_backfill/candidates_secret_20260917.json +884 -0
- package/docs/experiments/linkref_backfill/candidates_secret_v2.json +789 -0
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- package/docs/mdcg/README/350/257/246/347/273/206/347/211/210_v0.4.5.md +631 -625
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- package/dsh/README.md +33 -0
- package/dsh/cordis.yml.example +13 -7
- package/dsh/hive-mcp-probe.mjs +94 -0
- package/dsh/hive-mcp.example.yml +62 -0
- package/dsh/update-lingshu.bat +11 -0
- package/dsh/update-lingshu.ps1 +337 -0
- package/lib/hooks.d.ts +3 -0
- package/lib/hooks.js +17 -23
- package/lib/index.d.ts +4 -2
- package/lib/index.js +29 -6
- package/lib/lib/datapath.d.ts +76 -1
- package/lib/lib/datapath.js +199 -13
- package/lib/lib/mdcg_client.d.ts +40 -3
- package/lib/lib/mdcg_client.js +46 -22
- package/lib/lib/mutual.js +4 -4
- package/lib/lib/token_store.js +4 -5
- package/md_cg/audit.py +17 -2
- package/md_cg/autonomy.py +86 -15
- package/md_cg/backfill.py +36 -1
- package/md_cg/backfill_bigdomain.py +34 -0
- package/md_cg/bench6_arms.py +28 -1
- package/md_cg/bench6_common.py +10 -1
- package/md_cg/bench6_competitors.py +6 -1
- package/md_cg/bench_axis_domain.py +9 -1
- package/md_cg/bench_blind_comp.py +7 -1
- package/md_cg/bench_en_atoms_public.py +9 -0
- package/md_cg/bench_governance.py +348 -0
- package/md_cg/bench_lme_zh.py +16 -1
- package/md_cg/bench_locomo.py +2 -1
- package/md_cg/bench_locomo_zh.py +16 -1
- package/md_cg/bench_locomo_zh_public.py +4 -1
- package/md_cg/bench_longmem.py +2 -1
- package/md_cg/bench_membench.py +27 -1
- package/md_cg/bench_p0.py +4 -1
- package/md_cg/bench_progressive.py +13 -1
- package/md_cg/bench_role_views.py +238 -0
- package/md_cg/bench_task_ab.py +8 -1
- package/md_cg/bench_task_ab_llm.py +13 -1
- package/md_cg/bench_unified_en.py +6 -1
- package/md_cg/bench_zh_mad.py +20 -1
- package/md_cg/blindspot_tickets.py +123 -0
- package/md_cg/branches.py +12 -1
- package/md_cg/build_postings.py +73 -0
- package/md_cg/ccgc.py +67 -2
- package/md_cg/census.py +5 -1
- package/md_cg/chain.py +24 -3
- package/md_cg/codeindex.py +134 -17
- package/md_cg/coldverify.py +265 -0
- package/md_cg/comment_gate.py +338 -0
- package/md_cg/cond_compose.py +190 -0
- package/md_cg/cond_facts.py +155 -0
- package/md_cg/cond_template.json +107 -0
- package/md_cg/condition_anchor.py +143 -0
- package/md_cg/conformance.py +69 -4
- package/md_cg/consistency.py +24 -1
- package/md_cg/consolidate.py +53 -2
- package/md_cg/corpus.py +4 -0
- package/md_cg/crosscheck.py +42 -2
- package/md_cg/crypto.py +35 -1
- package/md_cg/d_meta.py +310 -0
- package/md_cg/datapath.py +201 -26
- package/md_cg/docindex.py +122 -1
- package/md_cg/eval_common.py +29 -1
- package/md_cg/evidence.py +27 -1
- package/md_cg/evolution.py +21 -1
- package/md_cg/export.py +11 -1
- package/md_cg/forgetting.py +23 -1
- package/md_cg/fsutil.py +18 -1
- package/md_cg/hotcache.py +214 -0
- package/md_cg/hyperedge.py +251 -0
- package/md_cg/identity.py +18 -1
- package/md_cg/insight.py +17 -1
- package/md_cg/lexicon/build_cedict_en_zh.py +9 -0
- package/md_cg/lexicon/build_standard_en.py +171 -168
- package/md_cg/lexicon/expand_en_zh.py +6 -0
- package/md_cg/lifecycle.py +12 -1
- package/md_cg/linkref.py +281 -0
- package/md_cg/links.py +29 -1
- package/md_cg/mcp_server.py +362 -43
- package/md_cg/md_whitebox.py +53 -1
- package/md_cg/mdcg.py +1003 -27
- package/md_cg/mdcos.py +558 -36
- package/md_cg/metacognition.py +37 -2
- package/md_cg/migrate.py +4 -0
- package/md_cg/migrate_aeis.py +221 -213
- package/md_cg/migrate_roleplay.py +8 -0
- package/md_cg/migrate_wisdom_graph.py +14 -1
- package/md_cg/mreview/__main__.py +3 -0
- package/md_cg/mreview/bundle.py +8 -0
- package/md_cg/mreview/candidates.py +9 -0
- package/md_cg/mreview/govern.py +21 -1
- package/md_cg/mreview/locate.py +34 -0
- package/md_cg/mreview/pipeline.py +29 -1
- package/md_cg/mreview/ruleset.py +16 -1
- package/md_cg/nodefile.py +233 -3
- package/md_cg/pooling.py +23 -1
- package/md_cg/postings.py +298 -0
- package/md_cg/predict.py +89 -9
- package/md_cg/progressive.py +3 -0
- package/md_cg/protect.py +14 -1
- package/md_cg/protocol.py +372 -0
- package/md_cg/provenance.py +262 -0
- package/md_cg/reach.py +453 -0
- package/md_cg/refindex.py +47 -2
- package/md_cg/refine.py +20 -1
- package/md_cg/roleviews.py +89 -0
- package/md_cg/routing.py +76 -0
- package/md_cg/scrub.py +63 -2
- package/md_cg/security.py +26 -1
- package/md_cg/self_state.py +64 -1
- package/md_cg/selfreport.py +151 -0
- package/md_cg/semantic/canonical.py +5 -0
- package/md_cg/semantic/en_normalizer.py +364 -355
- package/md_cg/semantic/zh_en_atoms.py +139 -136
- package/md_cg/signer.py +41 -1
- package/md_cg/sources.py +583 -547
- package/md_cg/statushdr.py +179 -0
- package/md_cg/stg.py +59 -18
- package/md_cg/subgraph.py +23 -0
- package/md_cg/sustain.py +56 -1
- package/md_cg/tasks.py +26 -2
- package/md_cg/test_autonomy.py +26 -0
- package/md_cg/test_bench_governance.py +102 -0
- package/md_cg/test_blindspot_tickets.py +166 -0
- package/md_cg/test_ccgc.py +10 -0
- package/md_cg/test_codeindex.py +338 -0
- package/md_cg/test_comment_gate.py +187 -0
- package/md_cg/test_cond_compose_anchors.py +76 -0
- package/md_cg/test_condition_anchor.py +82 -0
- package/md_cg/test_d_meta.py +412 -0
- package/md_cg/test_datapath_root.py +188 -0
- package/md_cg/test_gain_gate.py +47 -1
- package/md_cg/test_hot_cold.py +187 -0
- package/md_cg/test_hyperedge.py +245 -0
- package/md_cg/test_linkref.py +306 -0
- package/md_cg/test_md_access_parity.py +15 -3
- package/md_cg/test_mr_m1.py +108 -18
- package/md_cg/test_mr_m3.py +8 -1
- package/md_cg/test_p26_refindex.py +49 -20
- package/md_cg/test_p27_docindex.py +236 -2
- package/md_cg/test_p2_mcp.py +1 -1
- package/md_cg/test_p31_insight.py +24 -0
- package/md_cg/test_p44_md_whitebox.py +14 -1
- package/md_cg/test_protocol.py +243 -0
- package/md_cg/test_reach.py +378 -0
- package/md_cg/test_reach_keys.py +201 -0
- package/md_cg/test_reach_meta_exits.py +145 -0
- package/md_cg/test_read_clip.py +8 -4
- package/md_cg/test_retr_s1.py +340 -0
- package/md_cg/test_retr_s1b.py +209 -0
- package/md_cg/test_retr_s3.py +194 -0
- package/md_cg/test_retr_s4.py +163 -0
- package/md_cg/test_retr_s5.py +200 -0
- package/md_cg/test_retr_s6.py +157 -0
- package/md_cg/test_retr_s7.py +385 -0
- package/md_cg/test_retr_s8_time.py +316 -0
- package/md_cg/test_retr_s9_edges.py +286 -0
- package/md_cg/test_retr_s9_entity_ctx.py +175 -0
- package/md_cg/test_review_conformance.py +59 -2
- package/md_cg/test_role_views.py +354 -0
- package/md_cg/test_subproc_encoding.py +188 -0
- package/md_cg/test_trust.py +361 -0
- package/md_cg/test_units_poll.py +71 -0
- package/md_cg/test_v14_fixes.py +397 -0
- package/md_cg/test_validity_filter.py +280 -0
- package/md_cg/test_wisdom_md_store.py +7 -3
- package/md_cg/test_writepipe.py +5 -1
- package/md_cg/theory.py +16 -1
- package/md_cg/tokens.py +40 -8
- package/md_cg/tool_face.py +13 -2
- package/md_cg/trust.py +943 -0
- package/md_cg/twophase.py +12 -1
- package/md_cg/units.py +132 -10
- package/md_cg/vision_evidence.py +24 -1
- package/md_cg/weights.py +24 -1
- package/md_cg/whitebox.py +32 -1
- package/md_cg/whitebox_kb/data/verify_cache.json +21210 -365
- package/md_cg/whitebox_kb/data/verify_savings.jsonl +5078 -0
- package/md_cg/whitebox_kb/wisdom/audit_log/chain_heat.json +10 -10
- package/md_cg/whitebox_kb/wisdom/code_compose.py +113 -6
- package/md_cg/whitebox_kb/wisdom/code_solidified.json +1 -1
- package/md_cg/whitebox_kb/wisdom/verifier.py +340 -55
- package/md_cg/whitebox_kb/wisdom/wisdom-book-cloud.db +0 -0
- package/md_cg/writelimit.py +18 -4
- package/md_cg/writepipe.py +178 -7
- package/package.json +2 -2
- package/skills/skills/designer-perspective/scripts/__pycache__/designer.cpython-310.pyc +0 -0
- package/skills/skills/designer-perspective/scripts/designer.py +17 -1
- package/skills/skills/designer-perspective/tests/selftest.py +3 -1
- package/src/hooks.ts +17 -21
- package/src/index.ts +33 -6
- package/src/lib/datapath.ts +211 -13
- package/src/lib/mdcg_client.ts +64 -25
- package/src/lib/mutual.ts +411 -411
- package/src/lib/token_store.ts +4 -5
- package/zcode/AGENTS.md +196 -195
- package/zcode/README.md +4 -0
- package/md_cg/whitebox_kb/wisdom/wisdom-book-cloud.db-shm +0 -0
- 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())
|
package/md_cg/mreview/ruleset.py
CHANGED
|
@@ -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")}
|