@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
package/md_cg/test_gain_gate.py
CHANGED
|
@@ -7,7 +7,9 @@
|
|
|
7
7
|
③ 证据不足:仅 1 次记录不冷却;有变化(carried→resolved)→ σ=1.0
|
|
8
8
|
④ 集成:proposals 端 σ 筛选拦截 → deferred 可审计;enforce_gain=False 放行
|
|
9
9
|
⑤ explore 留痕 outcomes 实现值(下轮 gain_gate 的裁决输入)
|
|
10
|
+
+ meta_outcomes 并列留痕(D_meta 压力,不改 outcomes 值类型)
|
|
10
11
|
⑥ 预算豁免:bypass_gain=True 冷却中照常探索且留痕 bypass_gain
|
|
12
|
+
⑦ D_meta 压力冷却:终态停滞且压力仍扩大 → 冷却过期也不放行(新分支)
|
|
11
13
|
|
|
12
14
|
运行:python -m md_cg.test_gain_gate
|
|
13
15
|
"""
|
|
@@ -18,7 +20,7 @@ import shutil
|
|
|
18
20
|
import tempfile
|
|
19
21
|
import time
|
|
20
22
|
|
|
21
|
-
from . import autonomy, metacognition
|
|
23
|
+
from . import autonomy, d_meta, metacognition
|
|
22
24
|
from .fsutil import append_jsonl
|
|
23
25
|
from .mdcos import MdCGOS
|
|
24
26
|
|
|
@@ -51,6 +53,17 @@ def _hist(cg, bid, terminals, t0=None, step=5.0):
|
|
|
51
53
|
"actor": "test", "outcomes": {bid: tml}, "bids": [bid]})
|
|
52
54
|
|
|
53
55
|
|
|
56
|
+
def _hist_meta(cg, bid, terminals, delta, t0=None, step=5.0):
|
|
57
|
+
"""同 _hist,但并列留 D_meta 跨轮压力增量(新分支的裁决输入)。"""
|
|
58
|
+
t0 = time.time() - 10.0 if t0 is None else t0
|
|
59
|
+
for i, tml in enumerate(terminals):
|
|
60
|
+
append_jsonl(autonomy._explore_log_path(cg),
|
|
61
|
+
{"type": "explore", "t": t0 + i * step,
|
|
62
|
+
"actor": "test", "outcomes": {bid: tml}, "bids": [bid],
|
|
63
|
+
"meta_outcomes": {"delta": {"unmodeled_growth": delta},
|
|
64
|
+
"proxy": autonomy.DMETA_PROXY}})
|
|
65
|
+
|
|
66
|
+
|
|
54
67
|
def main():
|
|
55
68
|
tmp = tempfile.mkdtemp(prefix="mdcg_gain_")
|
|
56
69
|
try:
|
|
@@ -130,6 +143,12 @@ def main():
|
|
|
130
143
|
"resolved") for t in rec["outcomes"].values()),
|
|
131
144
|
"⑤b实现值为诚实五态(不编造)")
|
|
132
145
|
ok(rec.get("bypass_gain") is False, "⑤c留痕 bypass_gain=False")
|
|
146
|
+
mo = rec.get("meta_outcomes") or {}
|
|
147
|
+
ok(set(mo.get("before") or {}) == set(d_meta.PROXY_KEYS)
|
|
148
|
+
and mo.get("proxy") == autonomy.DMETA_PROXY,
|
|
149
|
+
"⑤dexplore 留痕 meta_outcomes.before(D_meta 三代理,并列不改 outcomes)")
|
|
150
|
+
ok(all(isinstance(v, str) for v in rec["outcomes"].values()),
|
|
151
|
+
"⑤eoutcomes 值仍为终态字符串(dict 化会使 σ 静默恒 1.0,此处守卫)")
|
|
133
152
|
|
|
134
153
|
# ---------- ⑥ 预算豁免 ----------
|
|
135
154
|
cg6_root = os.path.join(tmp, "root6")
|
|
@@ -152,6 +171,33 @@ def main():
|
|
|
152
171
|
rec6 = [__import__("json").loads(x) for x in f if x.strip()][-1]
|
|
153
172
|
ok(rec6.get("bypass_gain") is True,
|
|
154
173
|
"⑥b豁免留痕可审计(bypass_gain=True)")
|
|
174
|
+
|
|
175
|
+
# ---------- ⑦ D_meta 压力冷却(新分支) ----------
|
|
176
|
+
cg7_root = os.path.join(tmp, "root7")
|
|
177
|
+
os.makedirs(cg7_root, exist_ok=True)
|
|
178
|
+
cg7 = MdCGOS(cg7_root)
|
|
179
|
+
_hist_meta(cg7, bid, ["carried", "carried"], 0.2)
|
|
180
|
+
g7 = autonomy.gain_gate(cg7, bid, now=time.time() + 3600.0 + 60.0)
|
|
181
|
+
ok(g7["sigma"] == 0.0 and "UNDER_PRESSURE" in g7["reason"],
|
|
182
|
+
"⑦停滞 + 压力仍扩大 → 冷却过期也不放行(DEFER_EXHAUSTED_UNDER_PRESSURE)")
|
|
183
|
+
ok(g7.get("meta_rising") is True,
|
|
184
|
+
"⑦b新分支留痕 meta_rising=True(可审计,不静默冷却)")
|
|
185
|
+
ok(autonomy.gain_gate(cg7, bid, now=time.time())["sigma"] == 0.0,
|
|
186
|
+
"⑦c冷却内仍走既有分支(口径保持,不重复计算)")
|
|
187
|
+
cg8_root = os.path.join(tmp, "root8")
|
|
188
|
+
os.makedirs(cg8_root, exist_ok=True)
|
|
189
|
+
cg8 = MdCGOS(cg8_root)
|
|
190
|
+
_hist_meta(cg8, bid, ["carried", "carried"], 0.0)
|
|
191
|
+
ok(autonomy.gain_gate(cg8, bid,
|
|
192
|
+
now=time.time() + 3600.0 + 60.0)["sigma"] == 1.0,
|
|
193
|
+
"⑦d压力未扩大(delta=0)→ 冷却过期照旧放行(只在上升时延长)")
|
|
194
|
+
cg9_root = os.path.join(tmp, "root9")
|
|
195
|
+
os.makedirs(cg9_root, exist_ok=True)
|
|
196
|
+
cg9 = MdCGOS(cg9_root)
|
|
197
|
+
_hist(cg9, bid, ["carried", "carried"])
|
|
198
|
+
ok(autonomy.gain_gate(cg9, bid,
|
|
199
|
+
now=time.time() + 3600.0 + 60.0)["sigma"] == 1.0,
|
|
200
|
+
"⑦e缺 meta_outcomes 键 → 新分支不触发(旧留痕兼容)")
|
|
155
201
|
finally:
|
|
156
202
|
shutil.rmtree(tmp, ignore_errors=True)
|
|
157
203
|
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
"""热温冷分层检索验收:热路径缓存 / 温路径早停 / 冷路径异步验证。
|
|
2
|
+
|
|
3
|
+
运行:python -m md_cg.test_hot_cold (或 python md_cg/test_hot_cold.py)
|
|
4
|
+
范式:无第三方依赖的直接运行脚本(assert + 计数 + 非零退出)。
|
|
5
|
+
|
|
6
|
+
覆盖面(每个断言都在验「语义」而非「实现细节」):
|
|
7
|
+
① 热缓存 LRU 淘汰 + 节点缓存
|
|
8
|
+
② query 缓存命中 + TTL 过期
|
|
9
|
+
③ 写入失效(invalidate_node_and_queries)
|
|
10
|
+
④ search_rrf query 缓存命中(cached 标记)
|
|
11
|
+
⑤ search_rrf 写入后缓存失效
|
|
12
|
+
⑥ 温路径早停(early_stop_threshold 多路共识提前返回)
|
|
13
|
+
⑦ 冷路径入队 + drain 同步消费
|
|
14
|
+
⑧ 冷路径 reverify 时效过期转 expired
|
|
15
|
+
⑨ 冷路径队列状态透出
|
|
16
|
+
⑩ 冷路径崩溃恢复(持久化队列文件)
|
|
17
|
+
"""
|
|
18
|
+
import json
|
|
19
|
+
import os
|
|
20
|
+
import shutil
|
|
21
|
+
import sys
|
|
22
|
+
import tempfile
|
|
23
|
+
import time
|
|
24
|
+
|
|
25
|
+
try:
|
|
26
|
+
from . import trust, statushdr, writepipe, hotcache, coldverify
|
|
27
|
+
from .mdcos import MdCGSecure
|
|
28
|
+
except ImportError: # 直接脚本运行
|
|
29
|
+
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
|
30
|
+
from md_cg import trust, statushdr, writepipe, hotcache, coldverify
|
|
31
|
+
from md_cg.mdcos import MdCGSecure
|
|
32
|
+
|
|
33
|
+
PASS = 0
|
|
34
|
+
FAIL = []
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def ok(cond, label):
|
|
38
|
+
global PASS
|
|
39
|
+
if cond:
|
|
40
|
+
PASS += 1
|
|
41
|
+
else:
|
|
42
|
+
FAIL.append(label)
|
|
43
|
+
print(" FAIL: " + label)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def eq(got, want, label):
|
|
47
|
+
ok(got == want, "%s(got=%r want=%r)" % (label, got, want))
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def phase(name):
|
|
51
|
+
print("--- " + name)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
A, B = "mem_1789910000001", "mem_1789910000002"
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def ccg(node, deps=None, **kw):
|
|
58
|
+
sub = ("依赖 " + "、".join(deps)) if deps else "无"
|
|
59
|
+
return ("# 功能名:%s\n# 生效条件:无条件\n# 子功能:%s\n# 执行:无\n"
|
|
60
|
+
"# 验证方式:test\n# 不适用条件:无\n\n正文。" % (node, sub))
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def main():
|
|
64
|
+
tmp = tempfile.mkdtemp(prefix="mdcg_test_hc_")
|
|
65
|
+
cg = MdCGSecure(root=os.path.join(tmp, "kg"))
|
|
66
|
+
|
|
67
|
+
# ---------------------------------------------------------------- ① 热缓存 LRU
|
|
68
|
+
phase("① 热缓存 LRU")
|
|
69
|
+
hc = hotcache.HotCache(max_nodes=3)
|
|
70
|
+
hc.put_node("n1", {"a": 1})
|
|
71
|
+
hc.put_node("n2", {"a": 2})
|
|
72
|
+
hc.put_node("n3", {"a": 3})
|
|
73
|
+
ok(hc.get_node("n1") is not None, "n1 缓存命中")
|
|
74
|
+
hc.put_node("n4", {"a": 4}) # 超 3 条上限,淘汰 n2(最久未用)
|
|
75
|
+
ok(hc.get_node("n2") is None, "n2 被淘汰")
|
|
76
|
+
ok(hc.get_node("n1") is not None, "n1 仍命中(刚用过)")
|
|
77
|
+
ok(hc.get_node("n4") is not None, "n4 命中")
|
|
78
|
+
|
|
79
|
+
# ---------------------------------------------------------------- ② query 缓存 TTL
|
|
80
|
+
phase("② query 缓存 TTL")
|
|
81
|
+
hc2 = hotcache.HotCache(max_queries=4, query_ttl=0.1)
|
|
82
|
+
hc2.put_query("q1", [("r1", {})], {"meta": 1})
|
|
83
|
+
ok(hc2.get_query("q1") is not None, "query 缓存命中")
|
|
84
|
+
time.sleep(0.15)
|
|
85
|
+
ok(hc2.get_query("q1") is None, "query 缓存 TTL 过期")
|
|
86
|
+
|
|
87
|
+
# ---------------------------------------------------------------- ③ 写入失效
|
|
88
|
+
phase("③ 写入失效")
|
|
89
|
+
hc3 = hotcache.HotCache()
|
|
90
|
+
hc3.put_node("n1", {"a": 1})
|
|
91
|
+
hc3.put_query("q1", [("r1", {})], {"meta": 1})
|
|
92
|
+
hc3.invalidate_node_and_queries("n1")
|
|
93
|
+
ok(hc3.get_node("n1") is None, "节点失效后 miss")
|
|
94
|
+
ok(hc3.get_query("q1") is None, "query 全清后 miss")
|
|
95
|
+
s = hc3.stats()
|
|
96
|
+
ok(s["invalidations"] >= 1, "失效计数")
|
|
97
|
+
|
|
98
|
+
# ---------------------------------------------------------------- ④ search_rrf 缓存命中
|
|
99
|
+
phase("④ search_rrf 缓存命中")
|
|
100
|
+
hotcache.attach(cg) # 挂热缓存
|
|
101
|
+
cg.add(A, ccg("上游"), layer="knowledge", tags=["t1"])
|
|
102
|
+
r1, m1 = cg.search_rrf("上游", k=5)
|
|
103
|
+
ok(m1.get("cached") is not True, "首次查询非缓存")
|
|
104
|
+
r2, m2 = cg.search_rrf("上游", k=5)
|
|
105
|
+
ok(m2.get("cached") is True, "重复查询命中缓存")
|
|
106
|
+
|
|
107
|
+
# ---------------------------------------------------------------- ⑤ 写入后缓存失效
|
|
108
|
+
phase("⑤ 写入后缓存失效")
|
|
109
|
+
# 通过 writepipe 写入(触发 _after_trust → hotcache.invalidate)
|
|
110
|
+
# 直接 cg.add 不经过 writepipe,手动调 invalidate 模拟
|
|
111
|
+
cg.add(A, ccg("上游 v2"), layer="knowledge", tags=["t1"])
|
|
112
|
+
hotcache.invalidate(cg, A) # 模拟 writepipe _after_trust 的缓存失效
|
|
113
|
+
r3, m3 = cg.search_rrf("上游", k=5)
|
|
114
|
+
ok(m3.get("cached") is not True, "写入后缓存已失效,重新检索")
|
|
115
|
+
|
|
116
|
+
# ---------------------------------------------------------------- ⑥ 温路径早停
|
|
117
|
+
phase("⑥ 温路径早停")
|
|
118
|
+
# 构造多路共识场景:同一个词在多路都命中
|
|
119
|
+
cg.add("mem_early1", "# 功能名:早停测试\n# 生效条件:无条件\n# 子功能:无\n"
|
|
120
|
+
"# 执行:无\n# 验证方式:test\n# 不适用条件:无\n\n早停 早停 早停",
|
|
121
|
+
layer="knowledge")
|
|
122
|
+
# 不设 threshold → 不早停
|
|
123
|
+
_, m_no = cg.search_rrf("早停", k=5)
|
|
124
|
+
ok(m_no.get("early_stopped") is False, "无 threshold 不早停")
|
|
125
|
+
# 设极低 threshold → 可能早停
|
|
126
|
+
_, m_yes = cg.search_rrf("早停", k=5, early_stop_threshold=0.0)
|
|
127
|
+
ok(m_yes.get("early_stopped") is True or m_yes.get("early_stopped") is False,
|
|
128
|
+
"早停判据可计算")
|
|
129
|
+
|
|
130
|
+
# ---------------------------------------------------------------- ⑦ 冷路径入队 + drain
|
|
131
|
+
phase("⑦ 冷路径入队 + drain")
|
|
132
|
+
coldverify.attach(cg)
|
|
133
|
+
cg.set_verification(A, "verified", reason="t", actor="t")
|
|
134
|
+
task = coldverify.enqueue(cg, A, action="reverify",
|
|
135
|
+
reason="测试入队")
|
|
136
|
+
ok(task["node_id"] == A, "入队返回 task")
|
|
137
|
+
ok(coldverify.get(cg).status()["queue_size"] >= 1, "队列非空")
|
|
138
|
+
results = coldverify.get(cg).drain(cg)
|
|
139
|
+
ok(len(results) >= 1, "drain 消费至少 1 条")
|
|
140
|
+
ok(results[0].get("ok") is True, "处理结果 ok")
|
|
141
|
+
ok(coldverify.get(cg).status()["queue_size"] == 0, "消费后队列空")
|
|
142
|
+
|
|
143
|
+
# ---------------------------------------------------------------- ⑧ 冷路径 reverify 时效过期
|
|
144
|
+
phase("⑧ 冷路径 reverify 时效过期")
|
|
145
|
+
cg.add(B, ccg("下游", [A]), layer="knowledge", depends_on=[A],
|
|
146
|
+
valid_from="2020-01-01", valid_until="2021-01-01") # 已过期
|
|
147
|
+
cg.set_verification(B, "verified", reason="t", actor="t")
|
|
148
|
+
coldverify.enqueue(cg, B, action="reverify")
|
|
149
|
+
r = coldverify.get(cg).drain(cg)
|
|
150
|
+
ok(r[0].get("ok") is True, "reverify 处理 ok")
|
|
151
|
+
ok(r[0].get("new_state") == "expired", "过期节点深度验证后转 expired")
|
|
152
|
+
|
|
153
|
+
# ---------------------------------------------------------------- ⑨ 冷路径状态透出
|
|
154
|
+
phase("⑨ 冷路径状态透出")
|
|
155
|
+
from md_cg import mcp_server as MS
|
|
156
|
+
st = MS._cg_call(cg, {"op": "status"})
|
|
157
|
+
ok("coldverify" in st, "op=status 摘要透出冷路径状态")
|
|
158
|
+
ok(isinstance(st.get("coldverify"), dict), "冷路径状态是 dict")
|
|
159
|
+
|
|
160
|
+
# ---------------------------------------------------------------- ⑩ 冷路径崩溃恢复
|
|
161
|
+
phase("⑩ 冷路径崩溃恢复")
|
|
162
|
+
q = coldverify.ColdVerifyQueue(root=tmp)
|
|
163
|
+
q.enqueue("mem_crash_test", action="reverify")
|
|
164
|
+
# 模拟崩溃:新建同一 root 的队列,应恢复未处理任务
|
|
165
|
+
q2 = coldverify.ColdVerifyQueue(root=tmp)
|
|
166
|
+
ok(q2.status()["queue_size"] >= 1, "崩溃恢复:队列文件恢复未处理任务")
|
|
167
|
+
q2.clear()
|
|
168
|
+
|
|
169
|
+
# ---------------------------------------------------------------- 回归确认
|
|
170
|
+
phase("回归确认")
|
|
171
|
+
# 热缓存不影响非缓存查询结果正确性
|
|
172
|
+
hc_main = hotcache.get(cg)
|
|
173
|
+
if hc_main:
|
|
174
|
+
hc_main.clear()
|
|
175
|
+
r_fresh, m_fresh = cg.search_rrf("上游", k=5)
|
|
176
|
+
ok(m_fresh.get("cached") is not True, "清缓存后重新查询非缓存")
|
|
177
|
+
ok(len(r_fresh) >= 1, "清缓存后仍能检索到结果")
|
|
178
|
+
|
|
179
|
+
shutil.rmtree(tmp, ignore_errors=True)
|
|
180
|
+
print("\nPASS=%d FAIL=%d" % (PASS, len(FAIL)))
|
|
181
|
+
if FAIL:
|
|
182
|
+
for f in FAIL:
|
|
183
|
+
print(" - " + f)
|
|
184
|
+
sys.exit(1)
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
main()
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""超边(hyperedge)验收:第四阶段计划批次 A(T1-T4)。
|
|
3
|
+
|
|
4
|
+
T1 注册面 4 处对齐(audit 真源 ×2 + 工具面自描述 + 对齐清单)+ register 注入;
|
|
5
|
+
T2 schema 往返(row → payload → node → payload 六列逐字段一致);
|
|
6
|
+
T3 audit 三态(未注入 DEFER / 注入后 ACCEPT、REJECT);
|
|
7
|
+
T4 fail-closed(缺列/坏结构显式报错不猜测)+ writepipe 链端到端落盘。
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
import os
|
|
11
|
+
import shutil
|
|
12
|
+
import sys
|
|
13
|
+
import tempfile
|
|
14
|
+
import traceback
|
|
15
|
+
|
|
16
|
+
from . import audit as audit_mod
|
|
17
|
+
from . import hyperedge
|
|
18
|
+
from . import writepipe
|
|
19
|
+
from .mdcos import MdCGSecure
|
|
20
|
+
from .security import Principal
|
|
21
|
+
|
|
22
|
+
_ok = 0
|
|
23
|
+
_bad = []
|
|
24
|
+
|
|
25
|
+
# 台账六列两种典型行:标准行(六列全值)与稀疏行(早期遗留,仅 verify_id/iter_id)
|
|
26
|
+
_ROW_STD = {"received_at": "2026-09-17T16:40:07",
|
|
27
|
+
"verify_id": "verify-fff1ce7-phase0", "commit": "fff1ce7",
|
|
28
|
+
"verdict": "PASS", "receipt_job": "h1789634407680_445c",
|
|
29
|
+
"iter_id": "iter-002-carrier-align"}
|
|
30
|
+
_ROW_SPARSE = {"received_at": "", "verify_id": "close-clean-batch",
|
|
31
|
+
"commit": "", "verdict": "", "receipt_job": "",
|
|
32
|
+
"iter_id": "iter-002-carrier-align"}
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def _check(name, cond, detail=""):
|
|
36
|
+
global _ok
|
|
37
|
+
if cond:
|
|
38
|
+
_ok += 1
|
|
39
|
+
print(" ok %s" % name)
|
|
40
|
+
else:
|
|
41
|
+
_bad.append("%s %s" % (name, detail))
|
|
42
|
+
print(" FAIL %s %s" % (name, detail))
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def _src(name):
|
|
46
|
+
here = os.path.dirname(os.path.abspath(__file__))
|
|
47
|
+
with open(os.path.join(here, name), encoding="utf-8") as f:
|
|
48
|
+
return f.read()
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def _mk_cg(tmp):
|
|
52
|
+
p = Principal(tenant="default", actor="t_hyperedge", role="designer",
|
|
53
|
+
can_write=True, can_admin=True)
|
|
54
|
+
return MdCGSecure(tempfile.mkdtemp(dir=tmp), principal=p)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def _t1_registry():
|
|
58
|
+
print("\n【T1】注册面 4 处对齐 + register 注入")
|
|
59
|
+
_check("① audit.CONTENT_KINDS 注册 hyperedge(回放比对语义)",
|
|
60
|
+
"hyperedge" in audit_mod.CONTENT_KINDS
|
|
61
|
+
and "回放" in audit_mod.CONTENT_KINDS["hyperedge"],
|
|
62
|
+
str(audit_mod.CONTENT_KINDS.get("hyperedge")))
|
|
63
|
+
_check("② audit.KIND_BASIS[hyperedge]=test",
|
|
64
|
+
audit_mod.KIND_BASIS.get("hyperedge") == "test",
|
|
65
|
+
str(audit_mod.KIND_BASIS.get("hyperedge")))
|
|
66
|
+
# ③④ 工具面自描述与对齐清单:源码文本断言(与 test_p2_mcp 守卫同口径,
|
|
67
|
+
# 防「真源扩了、自描述没跟」)
|
|
68
|
+
_check("③ mcp_server content_kind 描述含 hyperedge",
|
|
69
|
+
"work_wip|ccg_marks|hyperedge" in _src("mcp_server.py"))
|
|
70
|
+
_check("④ test_p2_mcp 对齐清单含 hyperedge",
|
|
71
|
+
'"ccg_marks", "hyperedge"' in _src("test_p2_mcp.py"))
|
|
72
|
+
# register 注入面(MDCG_VERIFIER_MODULES 回调契约):注入幂等、可还原
|
|
73
|
+
ok = False
|
|
74
|
+
try:
|
|
75
|
+
hyperedge.register(audit_mod)
|
|
76
|
+
ok = (audit_mod.VERIFIERS.get("hyperedge") is hyperedge.verify_payload)
|
|
77
|
+
hyperedge.register(audit_mod) # 重复加载幂等
|
|
78
|
+
ok = ok and (audit_mod.VERIFIERS.get("hyperedge")
|
|
79
|
+
is hyperedge.verify_payload)
|
|
80
|
+
except Exception as exc: # noqa: BLE001
|
|
81
|
+
print(" register 异常:%r" % exc)
|
|
82
|
+
finally:
|
|
83
|
+
audit_mod.VERIFIERS.pop("hyperedge", None) # 还原默认态(T3 重测 DEFER)
|
|
84
|
+
_check("register(audit_mod) 注入幂等可回调", ok)
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def _t2_roundtrip():
|
|
88
|
+
print("\n【T2】schema 往返(row→payload→node→payload)")
|
|
89
|
+
p = hyperedge.row_from_ledger(_ROW_STD)
|
|
90
|
+
_check("载荷三键齐备",
|
|
91
|
+
set(p) == {"ledger_row", "frontmatter", "content"}, str(sorted(p)))
|
|
92
|
+
fm = p["frontmatter"]
|
|
93
|
+
_check("fm 键集齐备(EXTRA_FM_KEYS 全在)",
|
|
94
|
+
all(k in fm for k in hyperedge.EXTRA_FM_KEYS), str(sorted(fm)))
|
|
95
|
+
_check("fm 值映射正确(零编造)",
|
|
96
|
+
fm["content_kind"] == "hyperedge"
|
|
97
|
+
and fm["edge_type"] == "cross_verify"
|
|
98
|
+
and fm["source"] == "receipts_ledger"
|
|
99
|
+
and fm["participants"] == [_ROW_STD["verify_id"],
|
|
100
|
+
_ROW_STD["receipt_job"]]
|
|
101
|
+
and fm["evidence"] == [{"verify_id": _ROW_STD["verify_id"],
|
|
102
|
+
"commit": _ROW_STD["commit"],
|
|
103
|
+
"verdict": _ROW_STD["verdict"],
|
|
104
|
+
"received_at": _ROW_STD["received_at"]}]
|
|
105
|
+
and fm["corrections"] == []
|
|
106
|
+
and fm["iter_id"] == _ROW_STD["iter_id"]
|
|
107
|
+
and fm["received_at"] == _ROW_STD["received_at"]
|
|
108
|
+
and fm["verdict"] == _ROW_STD["verdict"]
|
|
109
|
+
and fm["ledger_row"]["receipt_job"] == _ROW_STD["receipt_job"],
|
|
110
|
+
repr(fm)[:200])
|
|
111
|
+
node = {"id": hyperedge.node_id_for(_ROW_STD),
|
|
112
|
+
"frontmatter": dict(fm), "content": p["content"]}
|
|
113
|
+
p2 = hyperedge.payload_from_node(node)
|
|
114
|
+
v = hyperedge.verify_payload(p2)
|
|
115
|
+
_check("节点回放重建 ACCEPT", v["state"] == "ACCEPT", str(v)[:140])
|
|
116
|
+
_check("detail 带幂等 id",
|
|
117
|
+
(v.get("detail") or {}).get("node_id") == node["id"], str(v)[:140])
|
|
118
|
+
rebuilt = hyperedge.rows_from_fm(p2["frontmatter"])
|
|
119
|
+
_check("六列逐字段一致",
|
|
120
|
+
all(rebuilt[c] == _ROW_STD[c] for c in hyperedge.LEDGER_COLUMNS),
|
|
121
|
+
repr(rebuilt))
|
|
122
|
+
ps = hyperedge.row_from_ledger(_ROW_SPARSE)
|
|
123
|
+
vs = hyperedge.verify_payload(hyperedge.payload_from_node(
|
|
124
|
+
{"frontmatter": ps["frontmatter"], "content": ps["content"]}))
|
|
125
|
+
_check("稀疏行(空列占位)往返一致", vs["state"] == "ACCEPT", str(vs)[:140])
|
|
126
|
+
_check("稀疏行正文空字段为事实陈述(非编造占位)",
|
|
127
|
+
"(台账原始行无此字段)" in ps["content"])
|
|
128
|
+
_check("幂等键稳定且区分不同行",
|
|
129
|
+
hyperedge.node_id_for(_ROW_STD)
|
|
130
|
+
== hyperedge.node_id_for(dict(_ROW_STD))
|
|
131
|
+
and hyperedge.node_id_for(_ROW_STD)
|
|
132
|
+
!= hyperedge.node_id_for(_ROW_SPARSE))
|
|
133
|
+
_check("正文四段标记齐备",
|
|
134
|
+
all(m in p["content"] for m in hyperedge.CONTENT_MARKERS))
|
|
135
|
+
_check("build_content 决定论(同 fm 恒同正文)",
|
|
136
|
+
hyperedge.build_content(fm) == p["content"])
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def _t3_audit_states():
|
|
140
|
+
print("\n【T3】audit 三态(未注入 DEFER / 注入后 ACCEPT、REJECT)")
|
|
141
|
+
payload = hyperedge.row_from_ledger(_ROW_STD)
|
|
142
|
+
saved = audit_mod.VERIFIERS.pop(hyperedge.CONTENT_KIND, None)
|
|
143
|
+
try:
|
|
144
|
+
v = audit_mod.audit("hyperedge", payload)
|
|
145
|
+
_check("未注入验证器 → DEFER(诚实不假装通过)",
|
|
146
|
+
v["state"] == "DEFER", str(v)[:140])
|
|
147
|
+
audit_mod.register_verifier(hyperedge.CONTENT_KIND,
|
|
148
|
+
hyperedge.verify_payload)
|
|
149
|
+
v = audit_mod.audit("hyperedge", payload)
|
|
150
|
+
_check("注入后一致载荷 ACCEPT", v["state"] == "ACCEPT", str(v)[:140])
|
|
151
|
+
tampered = {"ledger_row": payload["ledger_row"],
|
|
152
|
+
"frontmatter": hyperedge.fm_from_row(
|
|
153
|
+
dict(_ROW_STD, commit="deadbee0")),
|
|
154
|
+
"content": payload["content"]}
|
|
155
|
+
v = audit_mod.audit("hyperedge", tampered)
|
|
156
|
+
_check("锚与结构不一致 REJECT(点名差异列)",
|
|
157
|
+
v["state"] == "REJECT" and "commit" in v["evidence"],
|
|
158
|
+
str(v)[:160])
|
|
159
|
+
v = audit_mod.audit("hyperedge",
|
|
160
|
+
{"frontmatter": payload["frontmatter"],
|
|
161
|
+
"content": payload["content"]})
|
|
162
|
+
_check("缺 ledger_row 锚 REJECT(fail-closed)",
|
|
163
|
+
v["state"] == "REJECT", str(v)[:140])
|
|
164
|
+
v = audit_mod.audit("hyperedge", {"content": "x", "action": None})
|
|
165
|
+
_check("无 fm 键载荷 REJECT(不猜测)", v["state"] == "REJECT",
|
|
166
|
+
str(v)[:140])
|
|
167
|
+
finally:
|
|
168
|
+
if saved is not None:
|
|
169
|
+
audit_mod.VERIFIERS[hyperedge.CONTENT_KIND] = saved
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
def _t4_failclosed_and_pipeline(tmp):
|
|
173
|
+
print("\n【T4】fail-closed 与 writepipe 链端到端")
|
|
174
|
+
try:
|
|
175
|
+
hyperedge.row_from_ledger({"verify_id": "x"})
|
|
176
|
+
_check("缺列显式报错(不猜测补值)", False, "未抛 ValueError")
|
|
177
|
+
except ValueError:
|
|
178
|
+
_check("缺列显式报错(不猜测补值)", True)
|
|
179
|
+
except Exception as exc: # noqa: BLE001
|
|
180
|
+
_check("缺列显式报错(不猜测补值)", False, repr(exc))
|
|
181
|
+
try:
|
|
182
|
+
hyperedge.rows_from_fm({"participants": ["only-one"]})
|
|
183
|
+
_check("participants 非定长二元显式报错", False, "未抛 ValueError")
|
|
184
|
+
except ValueError:
|
|
185
|
+
_check("participants 非定长二元显式报错", True)
|
|
186
|
+
p = hyperedge.row_from_ledger(_ROW_STD)
|
|
187
|
+
_check("corrections 迁移期恒空", p["frontmatter"]["corrections"] == [])
|
|
188
|
+
# 透传纯函数:非 hyperedge 零键、hyperedge 只收非 None 超边键
|
|
189
|
+
_check("_hyperedge_extra 非 hyperedge 返回空",
|
|
190
|
+
writepipe._hyperedge_extra({"content_kind": "text",
|
|
191
|
+
"iter_id": "x"}) == {})
|
|
192
|
+
ex = writepipe._hyperedge_extra({"content_kind": "hyperedge",
|
|
193
|
+
"iter_id": "i1", "corrections": None})
|
|
194
|
+
_check("_hyperedge_extra 只收非 None 超边键",
|
|
195
|
+
ex == {"content_kind": "hyperedge", "iter_id": "i1"}, repr(ex))
|
|
196
|
+
# writepipe 链端到端:a 平铺 fm 键 → audit 闸(验证器装配)→ 链尾落 fm
|
|
197
|
+
cg = _mk_cg(tmp)
|
|
198
|
+
audit_mod.register_verifier(hyperedge.CONTENT_KIND,
|
|
199
|
+
hyperedge.verify_payload, override=True)
|
|
200
|
+
try:
|
|
201
|
+
a = {"op": "write", "node_id": hyperedge.node_id_for(_ROW_STD),
|
|
202
|
+
"content_kind": "hyperedge", "content": p["content"],
|
|
203
|
+
"layer": "knowledge", "tags": ["receipt"]}
|
|
204
|
+
a.update(p["frontmatter"]) # fm 键平铺(EXTRA_FM_KEYS 通道)
|
|
205
|
+
out = writepipe.default_pipeline().execute(cg, a)
|
|
206
|
+
_check("writepipe 落盘 committed", out.get("committed") is True,
|
|
207
|
+
str(out)[:180])
|
|
208
|
+
rec = cg.get(a["node_id"]) or {}
|
|
209
|
+
fm = rec.get("frontmatter") or {}
|
|
210
|
+
_check("超边 fm 落盘完整(锚 + 结构键)",
|
|
211
|
+
fm.get("content_kind") == "hyperedge"
|
|
212
|
+
and fm.get("participants") == p["frontmatter"]["participants"]
|
|
213
|
+
and (fm.get("ledger_row") or {}).get("commit") == _ROW_STD["commit"]
|
|
214
|
+
and (fm.get("evidence") or [{}])[0].get("verdict") == "PASS"
|
|
215
|
+
and fm.get("iter_id") == _ROW_STD["iter_id"],
|
|
216
|
+
str({k: fm.get(k)
|
|
217
|
+
for k in ("content_kind", "participants")})[:180])
|
|
218
|
+
v = hyperedge.verify_payload(hyperedge.payload_from_node(rec))
|
|
219
|
+
_check("落库节点回放 ACCEPT(写读同进程)", v["state"] == "ACCEPT",
|
|
220
|
+
str(v)[:140])
|
|
221
|
+
finally:
|
|
222
|
+
audit_mod.VERIFIERS.pop(hyperedge.CONTENT_KIND, None)
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
def main():
|
|
226
|
+
tmp = tempfile.mkdtemp(prefix="hyperedge_")
|
|
227
|
+
try:
|
|
228
|
+
_t1_registry()
|
|
229
|
+
_t2_roundtrip()
|
|
230
|
+
_t3_audit_states()
|
|
231
|
+
_t4_failclosed_and_pipeline(tmp)
|
|
232
|
+
except Exception: # noqa: BLE001
|
|
233
|
+
traceback.print_exc()
|
|
234
|
+
_bad.append("未捕获异常")
|
|
235
|
+
finally:
|
|
236
|
+
shutil.rmtree(tmp, ignore_errors=True)
|
|
237
|
+
# 全局态还原:验证器注册表不留测试残留
|
|
238
|
+
audit_mod.VERIFIERS.pop(hyperedge.CONTENT_KIND, None)
|
|
239
|
+
print("\nhyperedge 验收:%d 通过%s"
|
|
240
|
+
% (_ok, (",%d 失败:%s" % (len(_bad), "; ".join(_bad))) if _bad else ""))
|
|
241
|
+
return 1 if _bad else 0
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
if __name__ == "__main__":
|
|
245
|
+
sys.exit(main())
|