@furongjun1999/dsh-memory 0.4.8 → 0.4.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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 +6 -3
- package/lib/lib/mdcg_client.js +6 -5
- 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_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 +129 -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 +12 -8
- 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
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""bench_governance 评估器断言测试:语料守卫/指标定义/确定性/输出结构。"""
|
|
3
|
+
import json
|
|
4
|
+
import os
|
|
5
|
+
import shutil
|
|
6
|
+
import tempfile
|
|
7
|
+
import time
|
|
8
|
+
|
|
9
|
+
from . import bench_governance as bg
|
|
10
|
+
|
|
11
|
+
_FAILED = []
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def ok(cond, label):
|
|
15
|
+
if not cond:
|
|
16
|
+
print("FAIL", label)
|
|
17
|
+
_FAILED.append(label)
|
|
18
|
+
else:
|
|
19
|
+
print("pass", label)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def main():
|
|
23
|
+
# ① 语料守卫:当前语料必须合法
|
|
24
|
+
try:
|
|
25
|
+
bg._assert_material(time.time())
|
|
26
|
+
ok(True, "① 语料守卫通过(当前语料合法)")
|
|
27
|
+
except AssertionError as exc:
|
|
28
|
+
ok(False, f"① 语料守卫意外失败:{exc}")
|
|
29
|
+
|
|
30
|
+
# ② 篡改语料必须被守卫拦截(不可答题泄漏语料词面)
|
|
31
|
+
saved = list(bg.UNANSWERABLE)
|
|
32
|
+
try:
|
|
33
|
+
bg.UNANSWERABLE.append("蜂巢心跳间隔是多少")
|
|
34
|
+
try:
|
|
35
|
+
bg._assert_material(time.time())
|
|
36
|
+
ok(False, "② 篡改语料未被拦截")
|
|
37
|
+
except AssertionError:
|
|
38
|
+
ok(True, "② 篡改语料被守卫拦截")
|
|
39
|
+
finally:
|
|
40
|
+
bg.UNANSWERABLE[:] = saved
|
|
41
|
+
|
|
42
|
+
# ③ 全流程跑通 + 指标定义断言
|
|
43
|
+
report, root = bg.run()
|
|
44
|
+
try:
|
|
45
|
+
g1 = report["g1_correction_propagation"]
|
|
46
|
+
ok(g1["value"] == 1.0, f"③a g1 完美传播=1.0(got {g1['value']})")
|
|
47
|
+
ok(g1["detail"]["dependents"] == 4 and g1["detail"]["updated"] == 4,
|
|
48
|
+
"③b g1 依赖索引=4 且全部同步")
|
|
49
|
+
|
|
50
|
+
g2 = report["g2_contradiction_handling"]
|
|
51
|
+
ok(g2["value"] == 1.0, f"③c g2 矛盾裁决全对=1.0(got {g2['value']},detail={g2['detail']})")
|
|
52
|
+
ok(all(j["state"] == "REJECT" for j in g2["detail"]["judged"]),
|
|
53
|
+
"③d 否定节点定点裁决全 REJECT")
|
|
54
|
+
ok(g2["detail"]["retrieval"]["leaked_negatives"] == [],
|
|
55
|
+
"③e 检索防线 top-k 无否定节点冒充")
|
|
56
|
+
|
|
57
|
+
g3 = report["g3_expired_recall"]
|
|
58
|
+
ok(g3["value"] == 0.0, f"③f g3 validity 防线残留=0(got {g3['value']})")
|
|
59
|
+
ok(g3["detail"]["raw"]["stale_hits"], "③g 默认面确实暴露过期节点(同题竞争成立)")
|
|
60
|
+
ok(not g3["detail"]["guard"]["stale_hits"], "③h guard 面 top-k 无过期节点")
|
|
61
|
+
|
|
62
|
+
g4 = report["g4_refusal_f1"]
|
|
63
|
+
d4 = g4["detail"]
|
|
64
|
+
ok(d4["tp"] + d4["fn"] == len(bg.UNANSWERABLE), "③i 不可答集计数守恒")
|
|
65
|
+
ok(d4["fp"] + d4["tn"] == len(bg.FACTS), "③j 可答集计数守恒")
|
|
66
|
+
ok(g4["value"] is not None and 0.0 <= g4["value"] <= 1.0,
|
|
67
|
+
f"③k g4 F1 在 [0,1](got {g4['value']})")
|
|
68
|
+
|
|
69
|
+
ok(report["meta"]["corpus_nodes"] >= 20, "③l 语料规模守卫")
|
|
70
|
+
ok(all(report[g]["value"] is not None for g in
|
|
71
|
+
("g1_correction_propagation", "g2_contradiction_handling",
|
|
72
|
+
"g3_expired_recall", "g4_refusal_f1")), "③m 四指标均可计算")
|
|
73
|
+
finally:
|
|
74
|
+
shutil.rmtree(root, ignore_errors=True)
|
|
75
|
+
|
|
76
|
+
# ④ 确定性:重跑逐位一致
|
|
77
|
+
r1, root1 = bg.run()
|
|
78
|
+
r2, root2 = bg.run()
|
|
79
|
+
shutil.rmtree(root1, ignore_errors=True)
|
|
80
|
+
shutil.rmtree(root2, ignore_errors=True)
|
|
81
|
+
strip = lambda r: {g: r[g]["value"] for g in r if g.startswith("g")} # noqa: E731
|
|
82
|
+
ok(strip(r1) == strip(r2), "④ 两次运行四指标逐位一致")
|
|
83
|
+
|
|
84
|
+
# ⑤ main 输出 JSON 可写且结构完整
|
|
85
|
+
tmp_dir = tempfile.mkdtemp(prefix="gov_out_")
|
|
86
|
+
try:
|
|
87
|
+
tmp_out = os.path.join(tmp_dir, "baseline.json")
|
|
88
|
+
rc = bg.main(["--out", tmp_out])
|
|
89
|
+
with open(tmp_out, encoding="utf-8") as f:
|
|
90
|
+
data = json.load(f)
|
|
91
|
+
ok(rc == 0 and {"meta", "g1_correction_propagation", "g2_contradiction_handling",
|
|
92
|
+
"g3_expired_recall", "g4_refusal_f1",
|
|
93
|
+
"baseline_reference"} <= set(data), "⑤ 输出 JSON 结构完整且 exit 0")
|
|
94
|
+
finally:
|
|
95
|
+
shutil.rmtree(tmp_dir, ignore_errors=True)
|
|
96
|
+
|
|
97
|
+
print("\nbench_governance selftest: %d failed" % len(_FAILED))
|
|
98
|
+
return 1 if _FAILED else 0
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
if __name__ == "__main__":
|
|
102
|
+
raise SystemExit(main())
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""盲区消解票据(blindspot_tickets.make_tickets)验证。
|
|
3
|
+
|
|
4
|
+
断言面:
|
|
5
|
+
① 空库诚实:无反思记录 → 零票据、零落库(不编造消解价值)
|
|
6
|
+
② 预演:apply=False 全 dry_run、零落库
|
|
7
|
+
③ 四类分类:task(query 精确命中 unresolved 首行,优先级最高)/
|
|
8
|
+
grilling(defer 主导)/ research(blindspot>=3)/ prototype(其余)
|
|
9
|
+
④ apply 落卡:tasks.list_tasks 可见、tags 带 ticket:<type>
|
|
10
|
+
⑤ 幂等:重复 apply → created=0、updated=4(不重复建卡)
|
|
11
|
+
⑥ types 仅作分类后落卡过滤(判据恒定不变)
|
|
12
|
+
⑦ 非法 types → ok=False、零落库
|
|
13
|
+
⑧ grilling 卡如实声明「执行通道=宿主侧访谈」(库内无访谈工具,不越权代答)
|
|
14
|
+
⑨ task 卡正文携带关联 unresolved node_id
|
|
15
|
+
⑩ min_blindspot 候选资格过滤(b 低且无 defer 的簇不入池)
|
|
16
|
+
|
|
17
|
+
运行:python -m md_cg.test_blindspot_tickets
|
|
18
|
+
"""
|
|
19
|
+
from __future__ import annotations
|
|
20
|
+
|
|
21
|
+
import os
|
|
22
|
+
import tempfile
|
|
23
|
+
|
|
24
|
+
from . import blindspot_tickets, tasks
|
|
25
|
+
from .fsutil import append_jsonl
|
|
26
|
+
from .mdcos import MdCGOS
|
|
27
|
+
|
|
28
|
+
PASS = FAIL = 0
|
|
29
|
+
FAILS = []
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def ok(cond, label):
|
|
33
|
+
global PASS, FAIL
|
|
34
|
+
if cond:
|
|
35
|
+
PASS += 1
|
|
36
|
+
else:
|
|
37
|
+
FAIL += 1
|
|
38
|
+
FAILS.append(label)
|
|
39
|
+
print(f" FAIL {label}")
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def _sig(cg, t, query, states):
|
|
43
|
+
"""直写一条反思记录(与 test_autonomy 同构先例:只造信号面,不动检索面)。"""
|
|
44
|
+
append_jsonl(cg.reflection_log,
|
|
45
|
+
{"t": t, "query": query, "d_prev": 1.0, "d_curr": 1.0,
|
|
46
|
+
"d_delta": 0.0, "d2": 0.1, "states": states,
|
|
47
|
+
"n_results": 1, "feedback": None})
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def _card(cg, ttype, q):
|
|
51
|
+
nid = tasks.TASK_PREFIX + tasks.slugify("票据:%s:%s" % (ttype, q[:24]))
|
|
52
|
+
node = cg.get(nid) or {}
|
|
53
|
+
return node, nid
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def main():
|
|
57
|
+
tmp = tempfile.mkdtemp(prefix="mdcg_tickets_")
|
|
58
|
+
try:
|
|
59
|
+
root = os.path.join(tmp, "root")
|
|
60
|
+
os.makedirs(root, exist_ok=True)
|
|
61
|
+
cg = MdCGOS(root)
|
|
62
|
+
|
|
63
|
+
Q_R = "如何审计蜂巢的 gossip 收敛性" # blindspot×3 → research
|
|
64
|
+
Q_G = "上次说的那个修复方案细节" # defer×2 → grilling
|
|
65
|
+
Q_P = "数据迁移的回滚口径" # blindspot×1 → prototype
|
|
66
|
+
Q_T = "跨库事务一致性边界" # blindspot×1 + unresolved 首行命中 → task
|
|
67
|
+
|
|
68
|
+
# ---------- ① 空库诚实 ----------
|
|
69
|
+
r0 = blindspot_tickets.make_tickets(cg)
|
|
70
|
+
ok(r0["ok"] and r0["scanned"] == 0 and r0["created"] == 0
|
|
71
|
+
and not r0["tickets"],
|
|
72
|
+
"①空反思日志 → 零票据(不编造消解价值)")
|
|
73
|
+
r0a = blindspot_tickets.make_tickets(cg, apply=True)
|
|
74
|
+
ok(r0a["ok"] and r0a["created"] == 0 and len(tasks.list_tasks(cg)["tasks"]) == 0,
|
|
75
|
+
"①b空库 apply → 零落卡")
|
|
76
|
+
|
|
77
|
+
# ---------- ② 造四类信号(t 递增:task/prototype 同 b,last_t 定序) ----------
|
|
78
|
+
t = 1.0
|
|
79
|
+
for i in range(3):
|
|
80
|
+
t += 1
|
|
81
|
+
_sig(cg, t, Q_R, {"BLINDSPOT": 1})
|
|
82
|
+
for i in range(2):
|
|
83
|
+
t += 1
|
|
84
|
+
_sig(cg, t, Q_G, {"DEFER": 1})
|
|
85
|
+
_sig(cg, 10.0, Q_P, {"BLINDSPOT": 1})
|
|
86
|
+
_sig(cg, 11.0, Q_T, {"BLINDSPOT": 1})
|
|
87
|
+
cg.add("t_unres", Q_T + "\n悬挂:等待架构裁决", layer="unresolved")
|
|
88
|
+
|
|
89
|
+
# ---------- ③ 预演 ----------
|
|
90
|
+
dry = blindspot_tickets.make_tickets(cg)
|
|
91
|
+
ok(dry["ok"] and dry["scanned"] == 4 and dry["applied"] is False,
|
|
92
|
+
"③四簇入池、预演不落库")
|
|
93
|
+
by_q = {tk["query"]: tk for tk in dry["tickets"]}
|
|
94
|
+
ok(all(tk.get("dry_run") for tk in dry["tickets"]),
|
|
95
|
+
"③b预演票据全 dry_run 标记")
|
|
96
|
+
ok(len(tasks.list_tasks(cg)["tasks"]) == 0,
|
|
97
|
+
"③c预演零落库")
|
|
98
|
+
ok(by_q[Q_T]["type"] == "task" and by_q[Q_R]["type"] == "research"
|
|
99
|
+
and by_q[Q_G]["type"] == "grilling" and by_q[Q_P]["type"] == "prototype",
|
|
100
|
+
"③d四类分类各就位(task 判据优先于 b/d 计数)")
|
|
101
|
+
|
|
102
|
+
# ---------- ④ apply 落卡 ----------
|
|
103
|
+
r1 = blindspot_tickets.make_tickets(cg, apply=True, actor="test")
|
|
104
|
+
ok(r1["ok"] and r1["created"] == 4 and r1["errors"] == 0,
|
|
105
|
+
"④apply 建 4 卡、零错误")
|
|
106
|
+
lt = tasks.list_tasks(cg, status="active")
|
|
107
|
+
ok(len(lt["tasks"]) == 4, "④b任务清单可见 4 张 active 卡")
|
|
108
|
+
tags_ok = True
|
|
109
|
+
for ttype, q in (("research", Q_R), ("grilling", Q_G),
|
|
110
|
+
("prototype", Q_P), ("task", Q_T)):
|
|
111
|
+
node_i, _ = _card(cg, ttype, q)
|
|
112
|
+
fm_i = (node_i or {}).get("frontmatter") or {}
|
|
113
|
+
if "ticket:%s" % ttype not in (fm_i.get("tags") or []):
|
|
114
|
+
tags_ok = False
|
|
115
|
+
ok(tags_ok, "④c四卡 fm tags 带 ticket:<type> 族标")
|
|
116
|
+
# 分类落卡实体抽查
|
|
117
|
+
node_r, _ = _card(cg, "research", Q_R)
|
|
118
|
+
ok(node_r and "ticket:research" in (node_r["frontmatter"].get("tags") or []),
|
|
119
|
+
"④dresearch 卡以 ticket:research 落库")
|
|
120
|
+
|
|
121
|
+
# ---------- ⑤ 幂等 ----------
|
|
122
|
+
r2 = blindspot_tickets.make_tickets(cg, apply=True)
|
|
123
|
+
ok(r2["ok"] and r2["created"] == 0 and r2["updated"] == 4,
|
|
124
|
+
"⑤重复 apply → created=0、updated=4(同 slug 不重复建卡)")
|
|
125
|
+
ok(len(tasks.list_tasks(cg)["tasks"]) == 4, "⑤b卡数不增")
|
|
126
|
+
|
|
127
|
+
# ---------- ⑥ types 过滤(仅过滤,不改判据) ----------
|
|
128
|
+
r3 = blindspot_tickets.make_tickets(cg, types=["research"], apply=True)
|
|
129
|
+
tk3 = {tk["query"]: tk for tk in r3["tickets"]}
|
|
130
|
+
ok(r3["ok"] and tk3[Q_R].get("node_id") and tk3[Q_G].get("skipped")
|
|
131
|
+
and tk3[Q_T].get("skipped") and tk3[Q_P].get("skipped"),
|
|
132
|
+
"⑥types=research → 其余三类 skipped(判据未被 types 改写)")
|
|
133
|
+
ok(tk3[Q_R].get("created") is False,
|
|
134
|
+
"⑥bresearch 卡幂等更新(created=False,重复过滤轮不重复建)")
|
|
135
|
+
|
|
136
|
+
# ---------- ⑦ 非法 types ----------
|
|
137
|
+
r4 = blindspot_tickets.make_tickets(cg, types=["nope"], apply=True)
|
|
138
|
+
ok(r4["ok"] is False and "nope" in r4["error"]
|
|
139
|
+
and len(tasks.list_tasks(cg)["tasks"]) == 4,
|
|
140
|
+
"⑦非法类型 → ok=False 点名可选集、零落库")
|
|
141
|
+
|
|
142
|
+
# ---------- ⑧⑨ 卡正文边界与关联 ----------
|
|
143
|
+
node_g, _ = _card(cg, "grilling", Q_G)
|
|
144
|
+
plan_g = node_g["content"] if node_g else ""
|
|
145
|
+
ok(node_g and "宿主侧访谈" in plan_g and "不越权代答" in plan_g,
|
|
146
|
+
"⑧grilling 卡如实声明执行通道=宿主侧访谈")
|
|
147
|
+
node_t, _ = _card(cg, "task", Q_T)
|
|
148
|
+
plan_t = node_t["content"] if node_t else ""
|
|
149
|
+
ok(node_t and "t_unres" in plan_t and "关联悬挂" in plan_t,
|
|
150
|
+
"⑨task 卡携带关联 unresolved node_id")
|
|
151
|
+
|
|
152
|
+
# ---------- ⑩ min_blindspot 资格过滤 ----------
|
|
153
|
+
r5 = blindspot_tickets.make_tickets(cg, min_blindspot=3)
|
|
154
|
+
qs5 = {tk["query"] for tk in r5["tickets"] if not tk.get("skipped")}
|
|
155
|
+
ok(Q_R in qs5 and Q_G in qs5 and Q_P not in qs5 and Q_T not in qs5,
|
|
156
|
+
"⑩min_blindspot=3 → 仅 research(b=3) 与 grilling(d≥1) 入池")
|
|
157
|
+
|
|
158
|
+
print("[test_blindspot_tickets] PASS=%d FAIL=%d" % (PASS, FAIL))
|
|
159
|
+
if FAILS:
|
|
160
|
+
raise SystemExit(1)
|
|
161
|
+
finally:
|
|
162
|
+
pass
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
if __name__ == "__main__":
|
|
166
|
+
main()
|
package/md_cg/test_ccgc.py
CHANGED
|
@@ -338,6 +338,12 @@ def main():
|
|
|
338
338
|
# ---------- V19/V20 端到端:假蜂巢 → 编外裁决 → 签章 → 落库 ----------
|
|
339
339
|
jobs = os.path.join(tmp, "jobs")
|
|
340
340
|
os.makedirs(jobs, exist_ok=True)
|
|
341
|
+
# 假 serve 是**本进程内的线程**,故「该 pid 是本程序」如实为真——把本程序
|
|
342
|
+
# 口径指向本进程映像名即可(v14 缺陷 E 修复后 units 判活含**身份层**:
|
|
343
|
+
# 旧写法心跳 pid=本进程却让 EXE 口径指向 hive.exe,身份层判假 → 通道不可用,
|
|
344
|
+
# 那不是被测缺陷而是测试自身要与新口径对齐)。
|
|
345
|
+
_old_exe = os.environ.get(units.ENV_EXE)
|
|
346
|
+
os.environ[units.ENV_EXE] = sys.executable
|
|
341
347
|
with open(os.path.join(jobs, units.SERVE_FILE), "w", encoding="utf-8") as f:
|
|
342
348
|
json.dump({"ts": int(time.time() * 1000), "pid": os.getpid()}, f)
|
|
343
349
|
seen = {}
|
|
@@ -409,6 +415,10 @@ def main():
|
|
|
409
415
|
os.environ.pop(units.ENV_MODEL, None)
|
|
410
416
|
else:
|
|
411
417
|
os.environ[units.ENV_MODEL] = _old_model
|
|
418
|
+
if _old_exe is None:
|
|
419
|
+
os.environ.pop(units.ENV_EXE, None)
|
|
420
|
+
else:
|
|
421
|
+
os.environ[units.ENV_EXE] = _old_exe
|
|
412
422
|
|
|
413
423
|
finally:
|
|
414
424
|
print()
|
|
@@ -0,0 +1,338 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""代码索引(codeindex)验收 · Phase 0 契约裁决 + 行序压制修复。
|
|
3
|
+
|
|
4
|
+
对应计划 docs/mdcg/全库代码评审与条件化注释_计划_v0.1.md 的
|
|
5
|
+
codeindex-render-fix / codeindex-tests;契约为
|
|
6
|
+
docs/mdcg/代码评审与条件化注释_契约_v0.1.md。
|
|
7
|
+
|
|
8
|
+
验收口径(改造前 → 改造后):
|
|
9
|
+
1. 行序压制:源码里人工写的生效条件行曾被合成行(同名字段、且排在源码注释
|
|
10
|
+
之前)永久压制——检索侧 mdcos._ccg_field 取首个匹配,人工声明不可见。
|
|
11
|
+
现在合成区与源码区**分区**、源码区**置首**,人工声明胜出并逐字保留。
|
|
12
|
+
2. 字段零重名:机械推导的索引元条件不再占用生效条件字段,改由
|
|
13
|
+
nodefile.INDEX_META_MARK 承载;合成区字段名与 CCG 六要素的交集必须是空集
|
|
14
|
+
(机械判据 nodefile.is_ccg_mark)。
|
|
15
|
+
3. 诚实缺证据:源码未声明生效条件的条目,ccg_completeness 必须
|
|
16
|
+
complete=False 且**恰缺**「生效条件」——不许用索引元条件冒充。这是 Phase 0
|
|
17
|
+
的**预期代价面**:真实库既有 2930 条代码节点在重索引后由 DEFER 降为
|
|
18
|
+
BLINDSPOT,待环二补注释闸门逐步补回(代价面见契约 §四)。
|
|
19
|
+
4. extract / condition_space 既有契约不回归;index_dir 确定性排序与截断显式
|
|
20
|
+
上报维持(index-dir-integrity 属 Phase 1,本文件只建基线护栏)。
|
|
21
|
+
"""
|
|
22
|
+
from __future__ import annotations
|
|
23
|
+
|
|
24
|
+
import ast
|
|
25
|
+
import os
|
|
26
|
+
import sys
|
|
27
|
+
import tempfile
|
|
28
|
+
import traceback
|
|
29
|
+
|
|
30
|
+
from . import codeindex, nodefile
|
|
31
|
+
from .mdcos import _ccg_field
|
|
32
|
+
|
|
33
|
+
_ok = 0
|
|
34
|
+
_bad = []
|
|
35
|
+
_skip = 0
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def _check(name, cond, detail=""):
|
|
39
|
+
global _ok
|
|
40
|
+
if cond:
|
|
41
|
+
_ok += 1
|
|
42
|
+
print(" ok %s" % name)
|
|
43
|
+
else:
|
|
44
|
+
_bad.append("%s %s" % (name, detail))
|
|
45
|
+
print(" FAIL %s %s" % (name, detail))
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
PY_SRC = '''"""示例模块。"""
|
|
49
|
+
import os
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
# 生效条件:入参 path 为已存在的本地文件
|
|
53
|
+
# 子功能:读取文件首行
|
|
54
|
+
def first_line(path):
|
|
55
|
+
"""读取首行。"""
|
|
56
|
+
return open(path, encoding="utf-8").readline()
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def bare(name):
|
|
60
|
+
"""无源码 CCG 注释。"""
|
|
61
|
+
return name
|
|
62
|
+
'''
|
|
63
|
+
|
|
64
|
+
JS_SRC = '''/** 弱提取样本 */
|
|
65
|
+
export function ping(url) {
|
|
66
|
+
return url;
|
|
67
|
+
}
|
|
68
|
+
'''
|
|
69
|
+
|
|
70
|
+
HUMAN_COND = "# 生效条件:入参 path 为已存在的本地文件"
|
|
71
|
+
|
|
72
|
+
# 两窗口样本(裁决 A):leading 在定义行之上,body 在定义行之下/体首语句之前
|
|
73
|
+
BODY_SRC = '''"""两窗口样本。"""
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
# 生效条件:外层声明(leading 窗口)
|
|
77
|
+
def with_body(seq):
|
|
78
|
+
# 生效条件:入参 seq 为可迭代且元素可比较(body 窗口)
|
|
79
|
+
# 子功能:返回排序副本
|
|
80
|
+
out = sorted(seq)
|
|
81
|
+
return out
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def body_trap(seq):
|
|
85
|
+
out = sorted(seq)
|
|
86
|
+
# 实现注释:位于体首语句【之后】→ 不属于声明头窗口,不应被收
|
|
87
|
+
return out
|
|
88
|
+
'''
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def _item_of(src, path, name):
|
|
92
|
+
items = codeindex.extract(src, path)
|
|
93
|
+
return next((x for x in items if x["name"] == name), None)
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def main():
|
|
97
|
+
print("=" * 68)
|
|
98
|
+
print("md_cg codeindex 验收 · Phase 0(行序压制修复 + 契约裁决)")
|
|
99
|
+
print("=" * 68)
|
|
100
|
+
tmp = tempfile.mkdtemp(prefix="mdcg_codeidx_")
|
|
101
|
+
try:
|
|
102
|
+
# ---------------- ① 契约常量 ----------------
|
|
103
|
+
print()
|
|
104
|
+
print("【1】契约常量:合成区字段名与 CCG 零重名")
|
|
105
|
+
_check("索引元条件字段名不在 CCG 六要素内",
|
|
106
|
+
nodefile.INDEX_META_MARK not in nodefile.CCG_MARKS
|
|
107
|
+
and not nodefile.is_ccg_mark(nodefile.INDEX_META_MARK),
|
|
108
|
+
nodefile.INDEX_META_MARK)
|
|
109
|
+
_check("is_ccg_mark 对六要素逐一为真",
|
|
110
|
+
all(nodefile.is_ccg_mark(m) for m in nodefile.CCG_MARKS))
|
|
111
|
+
_check("is_ccg_mark 对元条件与位置为假",
|
|
112
|
+
not nodefile.is_ccg_mark(nodefile.INDEX_META_MARK)
|
|
113
|
+
and not nodefile.is_ccg_mark("位置"))
|
|
114
|
+
|
|
115
|
+
# ---------------- ② 分区与行序:人工优先 ----------------
|
|
116
|
+
print()
|
|
117
|
+
print("【2】render 三分区:源码区置首,人工声明不被压制")
|
|
118
|
+
it_ann = _item_of(PY_SRC, "demo.py", "first_line")
|
|
119
|
+
_check("extract 命中带注释的函数", bool(it_ann))
|
|
120
|
+
r_ann = codeindex.render(it_ann)
|
|
121
|
+
_check("源码注释逐字保留(不被丢弃)", HUMAN_COND in r_ann)
|
|
122
|
+
r_lines = r_ann.splitlines()
|
|
123
|
+
cond_i = r_lines.index(HUMAN_COND)
|
|
124
|
+
head_i = next(i for i, ln in enumerate(r_lines)
|
|
125
|
+
if ln.startswith("# 功能名:"))
|
|
126
|
+
_check("源码区先于合成区(人工优先的确定性序)", cond_i < head_i,
|
|
127
|
+
"cond@" + str(cond_i) + " head@" + str(head_i))
|
|
128
|
+
_check("检索侧取到人工条件(行序压制已修)",
|
|
129
|
+
_ccg_field(r_ann, "生效条件") == "入参 path 为已存在的本地文件",
|
|
130
|
+
_ccg_field(r_ann, "生效条件"))
|
|
131
|
+
_check("生效条件行恰一条(合成区不再产出该行)",
|
|
132
|
+
sum(1 for ln in r_lines
|
|
133
|
+
if ln.startswith("# 生效条件:")) == 1)
|
|
134
|
+
_check("补了注释 → 五要素齐备(complete=True)",
|
|
135
|
+
nodefile.ccg_completeness(r_ann)["complete"] is True,
|
|
136
|
+
str(nodefile.ccg_completeness(r_ann)["required_present"]))
|
|
137
|
+
|
|
138
|
+
it_bare = _item_of(PY_SRC, "demo.py", "bare")
|
|
139
|
+
r_bare = codeindex.render(it_bare)
|
|
140
|
+
_check("未声明生效条件的条目:合成区不冒充该字段",
|
|
141
|
+
"# 生效条件:" not in r_bare)
|
|
142
|
+
cpl = nodefile.ccg_completeness(r_bare)
|
|
143
|
+
missing = [m for m in nodefile.CCG_REQUIRED
|
|
144
|
+
if m not in cpl["required_present"]]
|
|
145
|
+
_check("未声明 → 恰缺生效条件(诚实缺证据,非整体失效)",
|
|
146
|
+
cpl["complete"] is False and missing == ["生效条件"],
|
|
147
|
+
str(missing))
|
|
148
|
+
_check("合成区其余 5 要素仍齐备",
|
|
149
|
+
all(m in cpl["required_present"] for m in nodefile.CCG_MARKS
|
|
150
|
+
if m != "生效条件"), str(cpl["required_present"]))
|
|
151
|
+
|
|
152
|
+
# ---------------- ③ 索引元信息区:零重名 + 同源 ----------------
|
|
153
|
+
print()
|
|
154
|
+
print("【3】索引元信息区(非 CCG 字段名,与 frontmatter 同源)")
|
|
155
|
+
meta_head = "# " + nodefile.INDEX_META_MARK + ":"
|
|
156
|
+
meta_line = next((ln for ln in r_lines if ln.startswith(meta_head)), "")
|
|
157
|
+
_check("索引元条件独立成行", bool(meta_line), meta_line[:60])
|
|
158
|
+
cs = codeindex.condition_space(it_ann)
|
|
159
|
+
_check("元条件 = condition_space 四槽同源(require_full=False)",
|
|
160
|
+
meta_line == meta_head + nodefile.condition_space_text(
|
|
161
|
+
cs, require_full=False), meta_line[:90])
|
|
162
|
+
_check("元条件行不被误判为旧口径单槽冒充",
|
|
163
|
+
not nodefile.is_legacy_position_condition(meta_line))
|
|
164
|
+
heads = []
|
|
165
|
+
for ln in r_bare.splitlines():
|
|
166
|
+
if not ln.startswith("#"):
|
|
167
|
+
continue
|
|
168
|
+
body = ln.lstrip("#").strip()
|
|
169
|
+
heads.append(body.split(":")[0].split(":")[0].strip())
|
|
170
|
+
used = sorted({h for h in heads if nodefile.is_ccg_mark(h)})
|
|
171
|
+
allowed = sorted(m for m in nodefile.CCG_MARKS if m != "生效条件")
|
|
172
|
+
_check("合成区用到的 CCG 字段名 = 其余五要素(排除生效条件)",
|
|
173
|
+
used == allowed, str(used))
|
|
174
|
+
_check("合成区非 CCG 字段名 = 索引元条件 + 位置",
|
|
175
|
+
sorted(h for h in heads if not nodefile.is_ccg_mark(h))
|
|
176
|
+
== sorted([nodefile.INDEX_META_MARK, "位置"]), str(heads))
|
|
177
|
+
|
|
178
|
+
# ---------------- ④ condition_space 既有契约不回归 ----------------
|
|
179
|
+
print()
|
|
180
|
+
print("【4】condition_space(四槽/全时窗)不回归")
|
|
181
|
+
_check("四槽齐备",
|
|
182
|
+
set(cs) == set(nodefile.CONDITION_SLOTS_REQUIRED),
|
|
183
|
+
str(sorted(cs)))
|
|
184
|
+
_check("时间槽用全时窗哨兵(不把写入时刻伪造成条件)",
|
|
185
|
+
nodefile.is_full_time_window(cs.get("time_window")),
|
|
186
|
+
str(cs.get("time_window")))
|
|
187
|
+
js_it = _item_of(JS_SRC, "sample.js", "ping")
|
|
188
|
+
weak = codeindex.condition_space(js_it)
|
|
189
|
+
_check("弱提取在方法槽诚实降级(不冒充编译器)",
|
|
190
|
+
codeindex.LANG_WEAK in weak["observation_tool"]
|
|
191
|
+
and codeindex.LANG_COMPILER not in weak["observation_tool"],
|
|
192
|
+
weak["observation_tool"])
|
|
193
|
+
|
|
194
|
+
# ---------------- ⑤ extract / 幂等 / 确定性 ----------------
|
|
195
|
+
print()
|
|
196
|
+
print("【5】extract 分派 / 幂等 / node_id / region_hash")
|
|
197
|
+
_check("Python → AST 精确(precise=True / basis=compiler)",
|
|
198
|
+
it_ann["precise"] is True and it_ann["basis"] == "compiler")
|
|
199
|
+
_check("JS → 弱提取(precise=False / basis=other)",
|
|
200
|
+
js_it["precise"] is False and js_it["basis"] == "other")
|
|
201
|
+
raised = False
|
|
202
|
+
try:
|
|
203
|
+
codeindex.extract("x = 1", "foo.xyz")
|
|
204
|
+
except ValueError as exc:
|
|
205
|
+
raised = "无提取器" in str(exc)
|
|
206
|
+
_check("无提取器后缀显式报错(不静默降级成 Python 解析)", raised)
|
|
207
|
+
_check("render 幂等(两次逐字节相同)",
|
|
208
|
+
codeindex.render(it_ann) == r_ann)
|
|
209
|
+
_check("node_id 稳定(path::name 短哈希,重复索引幂等)",
|
|
210
|
+
codeindex.node_id(it_ann) == codeindex.node_id(dict(it_ann)))
|
|
211
|
+
_check("region_hash 为 12 位十六进制(索引/回读共用口径)",
|
|
212
|
+
len(it_ann["hash"]) == 12
|
|
213
|
+
and all(c in "0123456789abcdef" for c in it_ann["hash"]),
|
|
214
|
+
it_ann["hash"])
|
|
215
|
+
|
|
216
|
+
# ---------------- ⑥ index_dir 基线护栏(Phase 1 前只建基线) -------
|
|
217
|
+
print()
|
|
218
|
+
print("【6】index_dir:确定性排序 + 截断显式上报(基线护栏)")
|
|
219
|
+
d = os.path.join(tmp, "pkg")
|
|
220
|
+
os.makedirs(d)
|
|
221
|
+
for fn, body in (("a.py", PY_SRC), ("b.js", JS_SRC)):
|
|
222
|
+
with open(os.path.join(d, fn), "w", encoding="utf-8") as f:
|
|
223
|
+
f.write(body)
|
|
224
|
+
_i1, _er1, s1 = codeindex.index_dir(d)
|
|
225
|
+
_i2, _er2, s2 = codeindex.index_dir(d)
|
|
226
|
+
_check("两次运行条目序逐项一致(walk/文件名确定性排序)",
|
|
227
|
+
[x["name"] for x in _i1] == [x["name"] for x in _i2],
|
|
228
|
+
str([x["name"] for x in _i1]))
|
|
229
|
+
_check("相同输入 stats 一致(幂等)", s1 == s2)
|
|
230
|
+
_check("未越限 → truncated=False", s1["truncated"] is False)
|
|
231
|
+
_check("skipped_suffixes 上报(无提取器后缀可见)",
|
|
232
|
+
isinstance(s1["skipped_suffixes"], list))
|
|
233
|
+
_i3, _er3, s3 = codeindex.index_dir(d, max_files=1)
|
|
234
|
+
_check("越限 → truncated=True 且原因非空(显式上报,不静默)",
|
|
235
|
+
s3["truncated"] is True and bool(s3["truncated_reason"]),
|
|
236
|
+
str(s3.get("truncated_reason")))
|
|
237
|
+
_check("越限时仍返回已索引条目(不丢已有产出)",
|
|
238
|
+
isinstance(_i3, list) and len(_i3) >= 1
|
|
239
|
+
and len(s3["skip_dirs"]) == len(s1["skip_dirs"]),
|
|
240
|
+
"items=" + str(len(_i3)))
|
|
241
|
+
# ---- index-dir-integrity(Phase 1):截断可复算 + 目录序确定 ----
|
|
242
|
+
_check("截断计数可复算:hit_files/indexed_items 就位且与条目数一致",
|
|
243
|
+
s1.get("hit_files", 0) >= 1 and s1.get("indexed_items") == len(_i1),
|
|
244
|
+
"hit=%s items=%s len=%s" % (s1.get("hit_files"),
|
|
245
|
+
s1.get("indexed_items"), len(_i1)))
|
|
246
|
+
_check("越限快照同样带 hit_files/indexed_items(与 files 同源)",
|
|
247
|
+
s3.get("hit_files", 0) >= 1 and s3.get("indexed_items") == len(_i3),
|
|
248
|
+
"hit=%s items=%s len=%s" % (s3.get("hit_files"),
|
|
249
|
+
s3.get("indexed_items"), len(_i3)))
|
|
250
|
+
nested = os.path.join(tmp, "nested")
|
|
251
|
+
for sub in ("z_sub", "a_sub"):
|
|
252
|
+
os.makedirs(os.path.join(nested, sub))
|
|
253
|
+
with open(os.path.join(nested, sub, "x.py"), "w", encoding="utf-8") as f:
|
|
254
|
+
f.write(PY_SRC)
|
|
255
|
+
_n1, _ne1, sn1 = codeindex.index_dir(nested)
|
|
256
|
+
_n2, _ne2, sn2 = codeindex.index_dir(nested)
|
|
257
|
+
_p1 = [x["path"] for x in _n1]
|
|
258
|
+
_p2 = [x["path"] for x in _n2]
|
|
259
|
+
_check("嵌套目录:两次运行条目路径序逐项一致(dirnames 已排序)",
|
|
260
|
+
_p1 == _p2 and len(_p1) > 1, str(_p1)[:120])
|
|
261
|
+
_check("嵌套目录:a_sub 先于 z_sub(与文件系统返回序无关)",
|
|
262
|
+
_p1.index(next(p for p in _p1 if p.startswith("a_sub/")))
|
|
263
|
+
< _p1.index(next(p for p in _p1 if p.startswith("z_sub/"))),
|
|
264
|
+
str(_p1)[:140])
|
|
265
|
+
_check("嵌套目录 stats 幂等(同输入两次一致)", sn1 == sn2)
|
|
266
|
+
|
|
267
|
+
# ---------------- ⑦ 载体两窗口(裁决 A) ----------------
|
|
268
|
+
print()
|
|
269
|
+
print("【7】注释载体两窗口:leading + body(裁决 A · 索引侧收口)")
|
|
270
|
+
_check("codeindex._body_comments 存在(历史悬空引用由此坐实)",
|
|
271
|
+
callable(getattr(codeindex, "_body_comments", None))
|
|
272
|
+
and callable(getattr(codeindex, "_symbol_comments", None)))
|
|
273
|
+
it2 = _item_of(BODY_SRC, "demo2.py", "with_body")
|
|
274
|
+
want_cmts = ["# 生效条件:外层声明(leading 窗口)",
|
|
275
|
+
"# 生效条件:入参 seq 为可迭代且元素可比较(body 窗口)",
|
|
276
|
+
"# 子功能:返回排序副本"]
|
|
277
|
+
_check("两窗口按物理行序合并(leading 在前、body 在后)",
|
|
278
|
+
it2["comments"] == want_cmts, repr(it2["comments"])[:200])
|
|
279
|
+
r2w = codeindex.render(it2)
|
|
280
|
+
_check("body 窗口的 CCG 注释在正文可见(不再是索引盲区)",
|
|
281
|
+
"# 生效条件:入参 seq 为可迭代且元素可比较(body 窗口)" in r2w)
|
|
282
|
+
_check("冲突时靠前者胜出(_ccg_field 取首匹配 = 物理序)",
|
|
283
|
+
_ccg_field(r2w, "生效条件") == "外层声明(leading 窗口)",
|
|
284
|
+
_ccg_field(r2w, "生效条件"))
|
|
285
|
+
_check("补了 body 注释 → 五要素齐备(complete=True)",
|
|
286
|
+
nodefile.ccg_completeness(r2w)["complete"] is True,
|
|
287
|
+
str(nodefile.ccg_completeness(r2w)["required_present"]))
|
|
288
|
+
# 非循环断言:窗口产物**逐字**固定(不依赖 verifier 委托是否成功)
|
|
289
|
+
# 期望值**写死**:体首语句行号不得臆测(上一版写成 lineno+1 即为反例)
|
|
290
|
+
_fnode = next(n for n in ast.parse(BODY_SRC).body
|
|
291
|
+
if isinstance(n, ast.FunctionDef) and n.name == "with_body")
|
|
292
|
+
_check("body 窗口产物逐字固定(期望值写死,非自证)",
|
|
293
|
+
codeindex._body_comments(BODY_SRC.splitlines(), _fnode.lineno,
|
|
294
|
+
_fnode.body[0].lineno) == [
|
|
295
|
+
"# 生效条件:入参 seq 为可迭代且元素可比较(body 窗口)",
|
|
296
|
+
"# 子功能:返回排序副本"],
|
|
297
|
+
repr(codeindex._body_comments(
|
|
298
|
+
BODY_SRC.splitlines(), _fnode.lineno,
|
|
299
|
+
_fnode.body[0].lineno))[:160])
|
|
300
|
+
it3 = _item_of(BODY_SRC, "demo2.py", "body_trap")
|
|
301
|
+
_check("体首语句【之后】的注释不入声明头窗口(边界不越界)",
|
|
302
|
+
it3["comments"] == [] and "实现注释" not in repr(it3["comments"]),
|
|
303
|
+
repr(it3["comments"])[:120])
|
|
304
|
+
# verifier 是否已收敛为「委托唯一真源」——等价断言不可静默跳过
|
|
305
|
+
try:
|
|
306
|
+
from .whitebox_kb.wisdom import verifier as Vmod
|
|
307
|
+
vobj = Vmod.Verifier.__new__(Vmod.Verifier)
|
|
308
|
+
tree = ast.parse(BODY_SRC)
|
|
309
|
+
fnode = next(n for n in tree.body if isinstance(n, ast.FunctionDef)
|
|
310
|
+
and n.name == "with_body")
|
|
311
|
+
src_lines = BODY_SRC.splitlines()
|
|
312
|
+
got, _doc = vobj._ccg_block(fnode, src_lines)
|
|
313
|
+
want = [s.lstrip("#").strip() for s in codeindex._body_comments(
|
|
314
|
+
src_lines, fnode.lineno, fnode.body[0].lineno)]
|
|
315
|
+
_check("verifier 已收敛为委托唯一真源(_ci_body_comments 可达)",
|
|
316
|
+
Vmod._ci_body_comments() is not None)
|
|
317
|
+
_check("verifier 窗口与 codeindex._body_comments 逐字等价",
|
|
318
|
+
got == want, repr([got, want])[:180])
|
|
319
|
+
_check("verifier 窗口只收 body 窗口(不误收 leading)",
|
|
320
|
+
got == want and all("外层声明" not in x for x in got),
|
|
321
|
+
repr(got)[:140])
|
|
322
|
+
except Exception as exc: # noqa: BLE001
|
|
323
|
+
global _skip
|
|
324
|
+
_skip += 1
|
|
325
|
+
print(" SKIP verifier 委托等价断言(模块不可导入):%r" % (exc,))
|
|
326
|
+
|
|
327
|
+
print()
|
|
328
|
+
print("PASS %d / FAIL %d / SKIP %d" % (_ok, len(_bad), _skip))
|
|
329
|
+
for b in _bad:
|
|
330
|
+
print(" - " + b)
|
|
331
|
+
return 1 if _bad else 0
|
|
332
|
+
except Exception: # noqa: BLE001
|
|
333
|
+
traceback.print_exc()
|
|
334
|
+
return 1
|
|
335
|
+
|
|
336
|
+
|
|
337
|
+
if __name__ == "__main__":
|
|
338
|
+
sys.exit(main())
|