@furongjun1999/dsh-memory 0.6.0 → 0.7.0
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 +618 -572
- package/codebuddy/CODEBUDDY.md +10 -10
- package/data/policy.json +27 -0
- package/docs/README.md +1 -0
- package/docs/discipline/harnesses.yaml +31 -0
- package/docs/discipline/templates/full.md.tmpl +1 -1
- package/docs/discipline/templates/rules.mdc.tmpl +1 -1
- package/docs/eval/N225_/347/264/242/345/274/225/346/227/245/345/277/227/351/235/236/345/257/271/350/261/241/350/243/205/350/275/275/351/235/242/347/261/273/345/236/213/351/227/270_v1.0.md +418 -0
- package/docs/eval/issue43_/351/273/230/350/256/244/347/255/226/347/225/245/344/270/216/351/224/256/347/261/273/345/236/213/351/227/270_v1.1.md +441 -0
- package/docs/eval/issue43_/351/273/230/350/256/244/347/255/226/347/225/245/345/212/240/350/275/275/344/270/216/345/207/255/346/215/256/346/230/216/346/226/207/351/230/237/345/210/227_v1.0.md +398 -0
- package/docs/eval//344/274/230/345/214/226/347/254/254/344/270/200/346/211/271_/346/216/245/347/272/277/344/270/216/347/255/211/344/273/267/345/217/230/346/215/242_v1.0.md +498 -0
- package/docs/eval//344/274/230/345/214/226/347/254/254/344/270/211/346/211/271_/351/227/250/347/246/201/350/275/254/346/255/243/344/270/216/350/260/203/345/272/246/346/255/242/350/241/200/344/270/216/351/227/250/346/216/247/346/224/266/345/217/243_v1.0.md +669 -0
- package/docs/eval//344/274/230/345/214/226/347/254/254/344/272/214/346/211/271_/344/276/235/350/265/226/351/200/217/344/274/240/344/270/216/345/257/271/346/213/215/345/217/243/345/276/204/344/270/216/350/264/237/347/274/223/345/255/230_v1.0.md +683 -0
- package/docs/eval//345/207/272/350/264/247/351/235/242/345/206/222/347/203/237_/350/277/233/350/264/247/351/227/250/347/246/201_v1.0.md +460 -0
- package/docs/eval//345/217/221/345/270/20307_/345/244/226/351/203/250/346/212/245/345/221/212/345/233/233/346/211/271/344/277/256/345/244/215/344/270/216/346/217/222/344/273/266/351/235/242/345/212/240/345/233/272_v1.0.md +205 -0
- package/docs/eval//345/217/221/345/270/20308_/350/207/252/350/277/255/344/273/243/344/270/216/347/235/241/347/234/240_/345/233/276/346/243/200/347/264/242/350/267/257/344/270/216/346/235/203/351/207/215_v1.0.md +97 -0
- package/docs/eval//345/275/222/344/270/200/345/261/202/347/274/272/347/234/201/347/277/273/345/205/263_/344/277/256/345/244/215/350/256/260/345/275/225_v1.0.md +458 -0
- package/docs/eval//346/200/247/350/203/275/344/270/223/351/241/271_/345/206/267/346/237/245/350/257/242/344/270/216/345/206/205/345/255/230_v1.0.md +218 -0
- package/docs/eval//346/225/205/351/232/234/346/263/250/345/205/245/345/256/236/346/265/213_v1.0.md +10 -0
- package/docs/eval//347/274/226/347/240/201/351/235/242/345/211/215/347/275/256_/345/205/245/345/217/243/350/207/252/344/277/235/350/257/201UTF8/344/270/216/346/226/207/346/234/254open/345/256/210/345/215/253_v1.0.md +646 -0
- package/docs/eval//347/274/272/351/231/267/346/214/226/346/216/230_/350/207/252/344/270/273/350/277/255/344/273/243_v20.md +283 -0
- package/docs/eval//347/274/272/351/231/267/346/214/226/346/216/230_/350/207/252/344/270/273/350/277/255/344/273/243_v21.md +224 -0
- package/docs/eval//347/274/272/351/231/267/346/214/226/346/216/230_/350/207/252/344/270/273/350/277/255/344/273/243_v22.md +223 -0
- package/docs/eval//347/274/272/351/231/267/346/214/226/346/216/230_/350/207/252/344/270/273/350/277/255/344/273/243_v23.md +293 -0
- package/docs/eval//347/274/272/351/231/267/346/214/226/346/216/230_/350/207/252/344/270/273/350/277/255/344/273/243_v24.md +303 -0
- package/docs/eval//347/274/272/351/231/267/346/214/226/346/216/230_/350/207/252/344/270/273/350/277/255/344/273/243_v25.md +230 -0
- package/docs/hive//346/243/200/347/264/242/347/256/227/346/263/225/345/217/243/345/276/204/345/257/271/347/205/247_v0.1.md +136 -11
- 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 +52 -2
- package/docs/hive//350/234/202/345/267/242M6_ingest/345/256/236/346/226/275/350/256/241/345/210/222_v0.1.md +1 -1
- package/docs/mdcg/README/350/257/246/347/273/206/347/211/210_v0.4.10.md +100 -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 +40 -40
- package/docs/mdcg//345/217/221/345/270/203/351/227/250/347/246/201/351/223/276_v0.1.md +57 -9
- 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 +7 -2
- package/docs/mdcg//347/235/241/347/234/240/345/221/250/346/234/237_/350/277/220/347/273/264/345/211/215/346/217/220/344/270/216/347/273/264/346/212/244/346/214/207/345/215/227_v1.0.md +183 -0
- package/docs/mdcg//350/256/244/347/237/245/345/233/276_/347/264/242/345/274/225/344/270/216/345/267/245/347/250/213/350/247/204/350/214/203/345/214/226_/350/256/241/345/210/222_v0.1.md +39 -3
- package/docs/plans//345/205/250/344/270/255/346/226/207/347/274/226/347/240/201/344/270/216/350/234/202/345/267/242/344/273/273/345/212/241/346/240/207/350/257/206/345/245/221/347/272/246_v2.0.md +220 -0
- package/docs/plans//347/234/237/346/272/220/347/264/242/345/274/225_/351/200/232/347/224/250/346/234/272/345/210/266_v0.3.md +227 -0
- package/docs/plans//347/235/241/347/234/240/344/270/216/350/207/252/350/277/255/344/273/243_/345/212/237/350/203/275/344/274/230/345/214/226/350/256/276/350/256/241_v0.4.md +547 -0
- package/docs/plans//350/234/202/345/267/242/346/250/241/345/236/213/345/257/206/351/222/245/351/205/215/347/275/256/351/235/242_v1.0.md +385 -0
- 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 +460 -460
- package/lib/bridge.js +24 -2
- package/lib/hooks.d.ts +31 -2
- package/lib/hooks.js +219 -16
- package/lib/init.js +16 -1
- package/lib/lib/prompt_safety.d.ts +52 -1
- package/lib/lib/prompt_safety.js +76 -1
- package/md_cg/audit.py +652 -379
- package/md_cg/bench6_arms.py +1 -1
- package/md_cg/bench_e2e_locomo_qa.py +11 -4
- package/md_cg/bench_p0.py +1 -1
- package/md_cg/branches.py +2 -2
- package/md_cg/ccgc.py +1071 -1006
- package/md_cg/chain.py +48 -1
- package/md_cg/consistency.py +97 -9
- package/md_cg/consolidate.py +34 -9
- package/md_cg/crypto.py +59 -26
- package/md_cg/docindex.py +16 -1
- package/md_cg/evolution.py +17 -1
- package/md_cg/export.py +1 -1
- package/md_cg/forgetting.py +309 -13
- package/md_cg/freshness.py +511 -0
- package/md_cg/fsutil.py +454 -8
- package/md_cg/generation.py +409 -0
- package/md_cg/hotcache.py +4 -1
- package/md_cg/identity.py +3 -3
- package/md_cg/insight.py +24 -3
- package/md_cg/linkref.py +1 -1
- package/md_cg/logref.py +327 -0
- package/md_cg/mcp_server.py +4261 -3818
- package/md_cg/mdcg.py +4695 -3547
- package/md_cg/mdcos.py +4672 -4159
- package/md_cg/mreview/pipeline.py +15 -1
- package/md_cg/nodefile.py +713 -576
- package/md_cg/protect.py +158 -11
- package/md_cg/protocol.py +41 -2
- package/md_cg/provenance.py +22 -4
- package/md_cg/reach.py +4 -4
- package/md_cg/readcache.py +76 -15
- package/md_cg/reconcile.py +1 -1
- package/md_cg/refindex.py +246 -37
- package/md_cg/refine.py +1 -1
- package/md_cg/routing.py +32 -5
- package/md_cg/run_tests.py +17 -0
- package/md_cg/scrub.py +30 -3
- package/md_cg/security.py +47 -1
- package/md_cg/self_state.py +5 -5
- package/md_cg/semantic/canonical.py +22 -0
- package/md_cg/semantic/unify.py +73 -22
- package/md_cg/semantic/unify_fixture.json +25 -0
- package/md_cg/sleep.py +1297 -0
- package/md_cg/sources.py +23 -6
- package/md_cg/srcindex.py +352 -0
- package/md_cg/stg.py +47 -3
- package/md_cg/subgraph.py +2 -2
- package/md_cg/sustain.py +1436 -1168
- package/md_cg/tasks.py +469 -470
- package/md_cg/test_auto_defaults.py +424 -0
- package/md_cg/test_b1_auto_id_multiproc.py +277 -0
- package/md_cg/test_b1b2_write_face.py +417 -0
- package/md_cg/test_b2_sensitivity_landing.py +223 -0
- package/md_cg/test_b3_merge_keeps_content.py +576 -0
- package/md_cg/test_b4_shard_dir_selfheal.py +843 -0
- package/md_cg/test_b4_shard_dir_selfheal_guard.py +238 -0
- package/md_cg/test_boundary_hit.py +410 -0
- package/md_cg/test_c3_transient_read_negative.py +793 -0
- package/md_cg/test_c8_search_rrf_gates.py +502 -0
- package/md_cg/test_ccg_form_parity.py +188 -0
- package/md_cg/test_ccgc.py +28 -3
- package/md_cg/test_en_pipeline.py +22 -13
- package/md_cg/test_generation_guard.py +352 -0
- package/md_cg/test_govern_directread.py +17 -4
- package/md_cg/test_h2_session_view_norm.py +233 -0
- package/md_cg/test_h4_sustain_snapshot.py +1387 -0
- package/md_cg/test_hive_ingest.py +285 -285
- package/md_cg/test_issue39_utf8_stdio.py +307 -16
- package/md_cg/test_issue43_default_policy.py +865 -0
- package/md_cg/test_legacy_p3_node_id_type.py +375 -0
- package/md_cg/test_linkref.py +10 -3
- package/md_cg/test_lock.py +2 -2
- package/md_cg/test_logref.py +1109 -0
- package/md_cg/test_m3_h9_bucket_health_protect_mark.py +301 -0
- package/md_cg/test_m3_h9_semantic_guard.py +525 -0
- package/md_cg/test_mr_m2.py +15 -3
- package/md_cg/test_n130_verify_falsified_protect.py +1 -1
- package/md_cg/test_n139_dek_provision_failclosed.py +185 -0
- package/md_cg/test_n176_link_trust.py +221 -0
- package/md_cg/test_n178_units_jobid_gate.py +212 -0
- package/md_cg/test_n184_keys_concurrent_provision.py +307 -0
- package/md_cg/test_n195_writepath_reload.py +283 -0
- package/md_cg/test_n196_stale_gate_skip.py +169 -0
- package/md_cg/test_n197_n208_write_face_gates.py +471 -0
- package/md_cg/test_n198_tokens_corrupt_failclosed.py +225 -0
- package/md_cg/test_n199_tokens_concurrent_write.py +378 -0
- package/md_cg/test_n201_proposal_visibility.py +382 -0
- package/md_cg/test_n202_session_notes_visibility.py +461 -0
- package/md_cg/test_n204_n205_n226_n227_n228_n229_exit_gates.py +542 -0
- package/md_cg/test_n206_stdio_jsonrpc_type.py +403 -0
- package/md_cg/test_n209_verify_write_face_gates.py +461 -0
- package/md_cg/test_n212_n213_n224_generation_gates.py +519 -0
- package/md_cg/test_n214_n215_n221_n222_write_face_gates.py +563 -0
- package/md_cg/test_n225_nonobject_load.py +1110 -0
- package/md_cg/test_n230_dirty_replay.py +375 -0
- package/md_cg/test_n62_tenant_bind_failclosed.py +200 -0
- package/md_cg/test_neg_condition_hits.py +333 -0
- package/md_cg/test_neg_tail_honesty.py +663 -0
- package/md_cg/test_none_id_write_guard.py +4 -3
- package/md_cg/test_opt_batch1_md_cg.py +451 -0
- package/md_cg/test_p1.py +5 -5
- package/md_cg/test_p11_consistency.py +161 -12
- package/md_cg/test_p26_refindex.py +2 -2
- package/md_cg/test_p27_docindex.py +777 -774
- package/md_cg/test_p28_refcheck.py +740 -13
- package/md_cg/test_p29_session_ingest_export.py +2 -2
- package/md_cg/test_p2_mcp.py +12 -1
- package/md_cg/test_p2_six_elements.py +463 -0
- package/md_cg/test_p30_maintain.py +33 -2
- package/md_cg/test_p31_insight.py +1 -0
- package/md_cg/test_p3_legacy_closure.py +433 -0
- package/md_cg/test_p4_freshness.py +680 -0
- package/md_cg/test_p8_subgraph_chain.py +14 -1
- package/md_cg/test_p9c_dedup_hints.py +276 -0
- package/md_cg/test_policy_required_ccg.py +426 -0
- package/md_cg/test_protocol.py +22 -0
- package/md_cg/test_rank_parity_score_mode.py +522 -0
- package/md_cg/test_read_face_input_gates.py +363 -0
- package/md_cg/test_read_face_semantics.py +489 -0
- package/md_cg/test_recall_face_guards.py +812 -0
- package/md_cg/test_rejected_credential_forms.py +188 -0
- package/md_cg/test_rejected_redact.py +146 -0
- package/md_cg/test_retr_s1b.py +2 -2
- package/md_cg/test_retr_s5.py +20 -7
- package/md_cg/test_review_conformance.py +21 -3
- package/md_cg/test_security_audit_v21.py +2 -2
- package/md_cg/test_semantic_canonical.py +5 -3
- package/md_cg/test_server_version.py +67 -0
- package/md_cg/test_sleep.py +611 -0
- package/md_cg/test_sleep_p1.py +784 -0
- package/md_cg/test_srcindex.py +171 -0
- package/md_cg/test_tenant_env_override_warn.py +63 -50
- package/md_cg/test_time_core_lint.py +968 -0
- package/md_cg/test_token_lowercase_form.py +315 -0
- package/md_cg/test_unify_default_off.py +701 -0
- package/md_cg/test_unify_scope.py +182 -0
- package/md_cg/test_v21r1_package_version.py +310 -0
- package/md_cg/test_writepipe.py +10 -4
- package/md_cg/tokens.py +225 -95
- package/md_cg/tool_face.py +4 -4
- package/md_cg/trust.py +49 -5
- package/md_cg/units.py +204 -6
- package/md_cg/weights.py +4 -4
- package/md_cg/whitebox_kb/aeis_core/time_core.py +8 -0
- package/md_cg/whitebox_kb/wisdom/knowledge_points.py +1 -1
- package/md_cg/writelimit.py +42 -8
- package/md_cg/writepipe.py +651 -554
- package/package.json +4 -2
- package/skills/plugin.json +1 -1
- package/src/bridge.ts +25 -2
- package/src/hooks.ts +229 -16
- package/src/init.ts +15 -1
- package/src/lib/prompt_safety.ts +88 -1
- package/utf8_boot.py +237 -0
- package/zcode/AGENTS.md +10 -10
|
@@ -0,0 +1,363 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""test_read_face_input_gates · 读面输入闸(M1 显式 k=0 / M2 node_id 类型 / M4 action 来源)
|
|
3
|
+
|
|
4
|
+
运行:python -m md_cg.test_read_face_input_gates
|
|
5
|
+
|
|
6
|
+
三个缺陷族(2026-09-30 修复,真源 md_cg/mcp_server.py):
|
|
7
|
+
|
|
8
|
+
M1 · `k` 的「显式 0 也是显式」
|
|
9
|
+
六处 `k=int(a.get("k") or 20)` 把显式 0(以及 -1)当「没传」,静默换成默认
|
|
10
|
+
20 —— 调用方以为「要 0 条」,拿到的是 20 条且返回形似正常。修法:单点判据
|
|
11
|
+
`_int_arg(a, "k", 20)`(`is not None` 口径,与 budget_tokens 先例一致),
|
|
12
|
+
六处全部**委托**该助手的单点实现。库层是「k<=0 意味什么」的唯一判据真源
|
|
13
|
+
(实测 cg.search/recall 对 0/-1 均返空且不抛),故本层只判透传 vs 回落。
|
|
14
|
+
|
|
15
|
+
**同日补强(k 族同族残余 8 处)**:self_check(5) / scrub.sample(k→n→
|
|
16
|
+
DEFAULT_SAMPLE) / scrub.associate(30) / scrub.sweep(k→n→DEFAULT_SAMPLE) /
|
|
17
|
+
scrub.history(100) / task.find(k→limit→5) / route(10) / mdcg_reflect(10)
|
|
18
|
+
原也是 `int(a.get("k") or N)`(含 k→n / k→limit 链式)。收口口径同上单点;
|
|
19
|
+
链式站点写成 `_int_arg(a, "k", _int_arg(a, "n", D))`——**逐级都不吞显式 0**。
|
|
20
|
+
其中 task.find(库层 `max(int(k or 5), 1)`)与 scrub.history(库层
|
|
21
|
+
`records[-int(limit):]`,0 即「取全部」)在库层另有 0 的语义、端到端不可观察,
|
|
22
|
+
故改断**本层透传给库层的原值**(打桩捕获,见 M1-D13/D14)。
|
|
23
|
+
|
|
24
|
+
M2 · read 的 node_id 类型闸
|
|
25
|
+
非 str 的 node_id:哈希可算者(int/float/bool)cg.get() 取不到 →
|
|
26
|
+
`_node_view(None)` → **裸 null**;不可哈希者(list/dict)TypeError 逃出。
|
|
27
|
+
修后**类型错**给结构化 error `node_id_not_str`(附 got_type;写面对应闸
|
|
28
|
+
见 mdcg.py:1776 的 `非法 node_id 类型` ValueError,读面取负路由形态)。
|
|
29
|
+
|
|
30
|
+
**「节点不存在」有意不动**:`md_cg/protocol.py` 的 read 形态表已登记
|
|
31
|
+
`node_missing.returns_null = True`(note:"不是错误对象——客户端须先判空再
|
|
32
|
+
解析"),反向证据 test_protocol A11/B8/B10 钉住它;改它须连带改协议真源与
|
|
33
|
+
那三条断言(不在本次指派文件内)。故两态的可区分性为:类型错 =
|
|
34
|
+
`{"ok": False, "error": "node_id_not_str", ...}`,不存在 = `null`。
|
|
35
|
+
|
|
36
|
+
M4 · action 来源恒定在场
|
|
37
|
+
旧行为只在「推导过」时透出 action_derived;显式传 action 时返回里没有
|
|
38
|
+
任何痕迹,调用方无法判断 action 是自己传的还是系统挑的。修后返回 dict 里
|
|
39
|
+
`action_source ∈ {explicit, sig, default, none}` **恒定在场**(`action_derived`
|
|
40
|
+
保持旧契约:只标记「推导发生过」,故显式态下**不出现**,test_action_derive
|
|
41
|
+
的 C5 断言依赖这一点)。`_cg_call.__doc__` 载默认动作表,本文件含
|
|
42
|
+
**表 ↔ 文档同源守卫**(防文档与 `_ACTION_DEFAULT` 漂移)。
|
|
43
|
+
"""
|
|
44
|
+
import os
|
|
45
|
+
import re
|
|
46
|
+
import sys
|
|
47
|
+
import tempfile
|
|
48
|
+
|
|
49
|
+
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
|
50
|
+
|
|
51
|
+
# Windows cmd 默认 GBK:中文打印即 UnicodeEncodeError —— 自带 UTF-8 兜底。
|
|
52
|
+
if hasattr(sys.stdout, "reconfigure"):
|
|
53
|
+
sys.stdout.reconfigure(encoding="utf-8", errors="replace")
|
|
54
|
+
|
|
55
|
+
from md_cg import mcp_server as M
|
|
56
|
+
from md_cg.mdcos import MdCGSecure
|
|
57
|
+
from md_cg.security import DEFAULT_SENSITIVITY, Principal
|
|
58
|
+
|
|
59
|
+
_ok = 0
|
|
60
|
+
_fail = []
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def check(name, cond, detail=""):
|
|
64
|
+
global _ok
|
|
65
|
+
if cond:
|
|
66
|
+
_ok += 1
|
|
67
|
+
print("[PASS] " + name)
|
|
68
|
+
else:
|
|
69
|
+
_fail.append(name)
|
|
70
|
+
print("[FAIL] %s · %s" % (name, str(detail)[:240]))
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def _raises(fn):
|
|
74
|
+
try:
|
|
75
|
+
fn()
|
|
76
|
+
return None
|
|
77
|
+
except Exception as e: # noqa: BLE001
|
|
78
|
+
return e
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
CCG = ("# 功能名:红按钮移动\n# 生效条件:问红按钮\n# 子功能:左移\n"
|
|
82
|
+
"# 执行:红按钮控制角色左移\n# 验证方式:test\n# 不适用条件:问蓝按钮\n\n"
|
|
83
|
+
"红按钮控制角色左移\n")
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
# ==================== M1-A. 单点判据(纯函数) ====================
|
|
87
|
+
IA = M._int_arg
|
|
88
|
+
|
|
89
|
+
check("M1-A1 显式 0 原样透传(旧写法回落 20)", IA({"k": 0}, "k", 20) == 0,
|
|
90
|
+
IA({"k": 0}, "k", 20))
|
|
91
|
+
check("M1-A2 显式负数原样透传", IA({"k": -3}, "k", 20) == -3, IA({"k": -3}, "k", 20))
|
|
92
|
+
check("M1-A3 缺键回落默认", IA({}, "k", 20) == 20, IA({}, "k", 20))
|
|
93
|
+
check("M1-A4 显式 None 视为未传,回落默认", IA({"k": None}, "k", 20) == 20,
|
|
94
|
+
IA({"k": None}, "k", 20))
|
|
95
|
+
check("M1-A5 普通值原样(不回归)", IA({"k": 5}, "k", 20) == 5, IA({"k": 5}, "k", 20))
|
|
96
|
+
check("M1-A6 字符串数字可 int 化(与旧写法同宽)", IA({"k": "7"}, "k", 20) == 7,
|
|
97
|
+
IA({"k": "7"}, "k", 20))
|
|
98
|
+
check("M1-A7 判据是 is not None 而非真值(False 也透传)",
|
|
99
|
+
IA({"k": False}, "k", 20) == 0, IA({"k": False}, "k", 20))
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
# ==================== M1-B. 源码级:六处全部委托单点 ====================
|
|
103
|
+
_SRC = open(M.__file__, encoding="utf-8", errors="replace").read()
|
|
104
|
+
_k_old = re.findall(r'k=int\(a\.get\("k"\)\s*or\s+20\)', _SRC)
|
|
105
|
+
_k_new = re.findall(r'k=_int_arg\(a, "k", 20\)', _SRC)
|
|
106
|
+
check("M1-B1 旧写法 k=int(a.get('k') or 20) 已清零(六处全改)",
|
|
107
|
+
len(_k_old) == 0, "残留 %d 处" % len(_k_old))
|
|
108
|
+
check("M1-B2 六处全部委托 _int_arg 单点(一处实现,六处调用)",
|
|
109
|
+
len(_k_new) == 6, "实测 %d 处:%s" % (len(_k_new), _k_new))
|
|
110
|
+
check("M1-B3 单点实现唯一(_int_arg 只定义一次)",
|
|
111
|
+
len(re.findall(r"^def _int_arg\(", _SRC, re.M)) == 1,
|
|
112
|
+
len(re.findall(r"^def _int_arg\(", _SRC, re.M)))
|
|
113
|
+
# ---- k 族**全量**收口(2026-09-30 补强):除 read 面六处外的其余 8 处站点 ----
|
|
114
|
+
# 旧写法在链式上还有 `int(a.get("k") or a.get("n") or N)` / `... or a.get("limit") or N`
|
|
115
|
+
# 两种形态,故用「`int(a.get(` 后紧跟 k 键 + or」的全族正则,避免只清直连形态。
|
|
116
|
+
_k_family_old = re.findall(r'int\(a\.get\("k"\)\s*or', _SRC)
|
|
117
|
+
check("M1-B4 k 族旧写法全量清零(含 k→n / k→limit 链式)",
|
|
118
|
+
len(_k_family_old) == 0, "残留 %d 处" % len(_k_family_old))
|
|
119
|
+
_K_SITES = (
|
|
120
|
+
("metacognition.self_check", r'k=_int_arg\(a, "k", 5\)\)'),
|
|
121
|
+
("scrub.sample",
|
|
122
|
+
r'cg, _int_arg\(a, "k", _int_arg\(a, "n", scrub\.DEFAULT_SAMPLE\)\),'),
|
|
123
|
+
("scrub.associate", r'limit=_int_arg\(a, "k", 30\),'),
|
|
124
|
+
("scrub.sweep",
|
|
125
|
+
r'cg, n=_int_arg\(a, "k", _int_arg\(a, "n", scrub\.DEFAULT_SAMPLE\)\),'),
|
|
126
|
+
("scrub.history", r'return scrub\.history\(cg, limit=_int_arg\(a, "k", 100\)\)'),
|
|
127
|
+
("task.find", r'cg, name, k=_int_arg\(a, "k", _int_arg\(a, "limit", 5\)\)\)'),
|
|
128
|
+
("op.route", r'res, meta = cg\.search\(intent, k=_int_arg\(a, "k", 10\),'),
|
|
129
|
+
("mdcg_reflect",
|
|
130
|
+
r'res, _ = cg\.search\(a\.get\("query", ""\), k=_int_arg\(a, "k", 10\),'),
|
|
131
|
+
)
|
|
132
|
+
_site_off = [(n, len(re.findall(p, _SRC)))
|
|
133
|
+
for n, p in _K_SITES if len(re.findall(p, _SRC)) != 1]
|
|
134
|
+
check("M1-B5 k 族 8 处站点逐一委托 _int_arg 单点(各恰 1 处)",
|
|
135
|
+
not _site_off, _site_off)
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
# ==================== M2-A / M4-B. 源码级:文档与常量同源 ====================
|
|
139
|
+
_doc = M._cg_call.__doc__ or ""
|
|
140
|
+
_doc_pairs = set(re.findall(r"([a-z_]+)→([a-z_]+)", _doc))
|
|
141
|
+
_table = dict(M._ACTION_DEFAULT)
|
|
142
|
+
check("M4-B1 默认动作表在 _cg_call 文档字符串中逐项在场(调用方不必猜)",
|
|
143
|
+
_doc_pairs == set(_table.items()),
|
|
144
|
+
"缺=%s 多=%s" % (sorted(set(_table.items()) - _doc_pairs),
|
|
145
|
+
sorted(_doc_pairs - set(_table.items()))))
|
|
146
|
+
check("M4-B2 文档表覆盖 _ACTION_DEFAULT 全部 %d 项" % len(_table),
|
|
147
|
+
len([p for p in _doc_pairs if p[0] in _table]) == len(_table),
|
|
148
|
+
len(_doc_pairs))
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
# ==================== 端到端(真 cg,临时库) ====================
|
|
152
|
+
root = tempfile.mkdtemp(prefix="mdcg_rfig_")
|
|
153
|
+
try:
|
|
154
|
+
p = Principal(actor="tester", clearance=DEFAULT_SENSITIVITY, can_write=True,
|
|
155
|
+
can_admin=True, role="designer", session="sess_a",
|
|
156
|
+
harness="test-harness")
|
|
157
|
+
cg = MdCGSecure(root, principal=p)
|
|
158
|
+
cg.add("n1", CCG, layer="knowledge", verification_basis="test")
|
|
159
|
+
cg.add("n2", CCG.replace("红按钮", "红按钮旁"), layer="knowledge")
|
|
160
|
+
|
|
161
|
+
# ---- M1-C. 端到端:k=0 不再是「没传」 ----
|
|
162
|
+
out0 = M._cg_call(cg, {"op": "read", "query": "红按钮", "k": 0})
|
|
163
|
+
check("M1-C1 read+query k=0 返回空结果(修前 20 条)",
|
|
164
|
+
out0.get("results") == [], str(out0.get("results"))[:80])
|
|
165
|
+
outd = M._cg_call(cg, {"op": "read", "query": "红按钮"})
|
|
166
|
+
check("M1-C2 read+query 不传 k 仍走默认 20(不回归)",
|
|
167
|
+
len(outd.get("results") or []) > 0, len(outd.get("results") or []))
|
|
168
|
+
outb = M._cg_call(cg, {"op": "read", "query": "红按钮",
|
|
169
|
+
"budget_tokens": 100000, "k": 0})
|
|
170
|
+
check("M1-C3 read+budget_tokens 路 k=0 同样透传(pack 为空)",
|
|
171
|
+
outb.get("pack") == [], str(outb.get("pack"))[:80])
|
|
172
|
+
outs = M._dispatch(cg, "mdcg_recall", {"query": "红按钮", "budget_tokens": 100000,
|
|
173
|
+
"k": 0})
|
|
174
|
+
check("M1-C4 mdcg_recall k=0 pack 为空(修前 20 条)",
|
|
175
|
+
outs.get("pack") == [], str(outs.get("pack"))[:80])
|
|
176
|
+
outss = M._dispatch(cg, "mdcg_search", {"query": "红按钮", "k": 0})
|
|
177
|
+
check("M1-C5 mdcg_search k=0 结果为空(修前 20 条)",
|
|
178
|
+
outss.get("results") == [], str(outss.get("results"))[:80])
|
|
179
|
+
# 两处 pooling 委托点:measure 把 k 原样回显在返回里,故可直接读读数
|
|
180
|
+
pm0 = M._sustain_call(cg, {"op": "sustain", "action": "pool_measure",
|
|
181
|
+
"queries": ["红按钮"], "k": 0})
|
|
182
|
+
check("M1-C6 pool_measure k=0 读数如实回显 0(修前回显 20)",
|
|
183
|
+
pm0.get("k") == 0, str(pm0.get("k")))
|
|
184
|
+
pmd = M._sustain_call(cg, {"op": "sustain", "action": "pool_measure",
|
|
185
|
+
"queries": ["红按钮"]})
|
|
186
|
+
check("M1-C7 pool_measure 不传 k 仍回落 20(不回归)",
|
|
187
|
+
pmd.get("k") == 20, str(pmd.get("k")))
|
|
188
|
+
pc0 = M._sustain_call(cg, {"op": "sustain", "action": "pool_compare",
|
|
189
|
+
"queries": ["红按钮"], "k": 0})
|
|
190
|
+
check("M1-C8 pool_compare k=0 读数如实回显 0(修前回显 20)",
|
|
191
|
+
pc0.get("k") == 0, str(pc0.get("k")))
|
|
192
|
+
# 库层是「k<=0」判据真源:不抛、返空(修法不自行报错的依据)
|
|
193
|
+
check("M1-C9 库层 cg.search(k=0) 不抛且返空(本层不自造报错的依据)",
|
|
194
|
+
cg.search("红按钮", k=0)[0] == [], cg.search("红按钮", k=0)[0])
|
|
195
|
+
|
|
196
|
+
# ---- M1-D. 端到端:k 族**其余 8 处**站点(同日补强,不再只覆盖 read 面) ----
|
|
197
|
+
# 判据面与 M1-C 同:本层只判「显式 0 透传 vs 缺省回落」,**0 在库层的含义由库层
|
|
198
|
+
# 定**——故逐站点断「显式 0 与缺省可区分」,不假定 0 一律等于「0 条」。
|
|
199
|
+
rf0 = M._dispatch(cg, "mdcg_reflect", {"query": "红按钮", "k": 0})
|
|
200
|
+
check("M1-D1 mdcg_reflect k=0 → n_results=0(修前回落 10 条)",
|
|
201
|
+
rf0.get("n_results") == 0, str(rf0.get("n_results")))
|
|
202
|
+
rfd = M._dispatch(cg, "mdcg_reflect", {"query": "红按钮"})
|
|
203
|
+
check("M1-D2 mdcg_reflect 缺省 k → 有结果(回落未被改坏)",
|
|
204
|
+
(rfd.get("n_results") or 0) > 0, str(rfd.get("n_results")))
|
|
205
|
+
rt0 = M._cg_dispatch(cg, {"op": "route", "intent": "红按钮", "k": 0})
|
|
206
|
+
check("M1-D3 route k=0 → knowledge 空(修前回落 10 条)",
|
|
207
|
+
rt0.get("knowledge") == [], len(rt0.get("knowledge") or []))
|
|
208
|
+
rtd = M._cg_dispatch(cg, {"op": "route", "intent": "红按钮"})
|
|
209
|
+
check("M1-D4 route 缺省 k → 有结果(回落未被改坏)",
|
|
210
|
+
len(rtd.get("knowledge") or []) > 0, len(rtd.get("knowledge") or []))
|
|
211
|
+
sm0 = M._scrub_call(cg, {"op": "scrub", "action": "sample", "k": 0})
|
|
212
|
+
check("M1-D5 scrub.sample k=0 → n=0(修前回落 DEFAULT_SAMPLE)",
|
|
213
|
+
sm0.get("n") == 0, str(sm0.get("n")))
|
|
214
|
+
smd = M._scrub_call(cg, {"op": "scrub", "action": "sample"})
|
|
215
|
+
check("M1-D6 scrub.sample 缺省 → 有样本(回落未被改坏)",
|
|
216
|
+
(smd.get("n") or 0) > 0, str(smd.get("n")))
|
|
217
|
+
as0 = M._scrub_call(cg, {"op": "scrub", "action": "associate",
|
|
218
|
+
"node_id": "n1", "k": 0})
|
|
219
|
+
check("M1-D7 scrub.associate k=0 → 联想 0 条(limit 修前回落 30)",
|
|
220
|
+
as0.get("n") == 0, str(as0.get("n")))
|
|
221
|
+
asd = M._scrub_call(cg, {"op": "scrub", "action": "associate",
|
|
222
|
+
"node_id": "n1"})
|
|
223
|
+
check("M1-D8 scrub.associate 缺省 → 有联想(回落未被改坏)",
|
|
224
|
+
(asd.get("n") or 0) > 0, str(asd.get("n")))
|
|
225
|
+
sw0 = M._scrub_call(cg, {"op": "scrub", "action": "sweep", "k": 0})
|
|
226
|
+
check("M1-D9 scrub.sweep k=0 → 抽样 0 条(修前回落 DEFAULT_SAMPLE)",
|
|
227
|
+
(sw0.get("sample") or {}).get("n") == 0,
|
|
228
|
+
str((sw0.get("sample") or {}).get("n")))
|
|
229
|
+
swd = M._scrub_call(cg, {"op": "scrub", "action": "sweep"})
|
|
230
|
+
check("M1-D10 scrub.sweep 缺省 → 有抽样(回落未被改坏)",
|
|
231
|
+
((swd.get("sample") or {}).get("n") or 0) > 0,
|
|
232
|
+
str((swd.get("sample") or {}).get("n")))
|
|
233
|
+
# self_check:k 决定「吃几条相似历史」——上面两次 mdcg_reflect 已写入反思记录
|
|
234
|
+
sc0 = M._metacognition_call(cg, {"op": "metacognition", "action": "self_check",
|
|
235
|
+
"query": "红按钮", "k": 0})
|
|
236
|
+
check("M1-D11 self_check k=0 → prior_attempts=0(修前回落 5 条历史)",
|
|
237
|
+
sc0.get("prior_attempts") == 0, str(sc0.get("prior_attempts")))
|
|
238
|
+
scd = M._metacognition_call(cg, {"op": "metacognition", "action": "self_check",
|
|
239
|
+
"query": "红按钮"})
|
|
240
|
+
check("M1-D12 self_check 缺省 → 吃到历史(回落未被改坏)",
|
|
241
|
+
(scd.get("prior_attempts") or 0) > 0, str(scd.get("prior_attempts")))
|
|
242
|
+
# task.find 与 scrub.history 两处:**库层对 k 另有语义**(`max(int(k or 5), 1)`
|
|
243
|
+
# 下限 1;`records[-int(limit):]` 下 limit=0 即「取全部」),端到端不可观察 ——
|
|
244
|
+
# 故改断**本层透传的原值**(打桩捕获库层入参,finally 恢复,零残留)。
|
|
245
|
+
from md_cg import scrub as _s_mod, tasks as _t_mod
|
|
246
|
+
_seen = {}
|
|
247
|
+
_orig_fs, _orig_sh = _t_mod.find_similar, _s_mod.history
|
|
248
|
+
try:
|
|
249
|
+
_t_mod.find_similar = lambda cg_, name, k=5: (
|
|
250
|
+
_seen.setdefault("task_find", []).append(k) or {"ok": True})
|
|
251
|
+
_s_mod.history = lambda cg_, limit=100: (
|
|
252
|
+
_seen.setdefault("scrub_history", []).append(limit)
|
|
253
|
+
or {"n": 0, "records": []})
|
|
254
|
+
M._task_call(cg, {"op": "task", "action": "find", "name": "红", "k": 0})
|
|
255
|
+
M._task_call(cg, {"op": "task", "action": "find", "name": "红"})
|
|
256
|
+
M._scrub_call(cg, {"op": "scrub", "action": "history", "k": 0})
|
|
257
|
+
M._scrub_call(cg, {"op": "scrub", "action": "history"})
|
|
258
|
+
finally:
|
|
259
|
+
_t_mod.find_similar, _s_mod.history = _orig_fs, _orig_sh
|
|
260
|
+
check("M1-D13 task.find 显式 k=0 原样透传库层(缺省才回落 5)",
|
|
261
|
+
_seen.get("task_find") == [0, 5], _seen.get("task_find"))
|
|
262
|
+
check("M1-D14 scrub.history 显式 k=0 原样透传库层(缺省才回落 100)",
|
|
263
|
+
_seen.get("scrub_history") == [0, 100], _seen.get("scrub_history"))
|
|
264
|
+
|
|
265
|
+
# ---- M2-B. 端到端:node_id 两态可区分、无裸 null、无 TypeError ----
|
|
266
|
+
ok1 = M._cg_call(cg, {"op": "read", "node_id": "n1"})
|
|
267
|
+
check("M2-B1 正路径:str node_id 仍返回节点视图(不回归)",
|
|
268
|
+
isinstance(ok1, dict) and ok1.get("id") == "n1", str(ok1)[:90])
|
|
269
|
+
ok2 = M._cg_call(cg, {"op": "read", "node_id": "n1", "offset": 1})
|
|
270
|
+
check("M2-B2 正路径带 offset 仍工作", isinstance(ok2, dict) and "content" in ok2,
|
|
271
|
+
str(ok2)[:90])
|
|
272
|
+
# 修前此处是**裸 null**(None);修后「不存在」**仍是 null**——protocol.py 的
|
|
273
|
+
# read 形态表登记 `node_missing.returns_null=True`,反向证据 test_protocol
|
|
274
|
+
# A11/B8/B10 钉住它(改它须连带改协议真源,不在本次指派文件内)。故此处断言的
|
|
275
|
+
# 是「协议形态零回归」,两态可区分性由 M2-B7 单独钉。
|
|
276
|
+
miss = M._cg_call(cg, {"op": "read", "node_id": "no_such_node"})
|
|
277
|
+
check("M2-B3 不存在的 str id 仍返回协议登记的 null(A11/B8/B10 零回归)",
|
|
278
|
+
miss is None, repr(miss)[:120])
|
|
279
|
+
check("M2-B4 两态可区分:类型错是结构化 error 对象,不存在是 null",
|
|
280
|
+
isinstance(M._cg_call(cg, {"op": "read", "node_id": 1}), dict)
|
|
281
|
+
and miss is None, "%r / %r" % (M._cg_call(cg, {"op": "read",
|
|
282
|
+
"node_id": 1}), miss))
|
|
283
|
+
for _bad, _tn in ((123, "int"), (3.5, "float"), (True, "bool"), (["x"], "list"),
|
|
284
|
+
({"a": 1}, "dict"), (("x",), "tuple")):
|
|
285
|
+
_r = _raises(lambda b=_bad: M._cg_call(cg, {"op": "read", "node_id": b}))
|
|
286
|
+
check("M2-B5 非 str node_id=%r 不抛异常(修前 list/dict 抛 TypeError)"
|
|
287
|
+
% (_bad,), _r is None, _r)
|
|
288
|
+
# 修前此处 list/dict 会 TypeError:捕成断言失败而不是拖崩整个文件,
|
|
289
|
+
# 否则后续断言连评估机会都没有(红项数会失真)。
|
|
290
|
+
try:
|
|
291
|
+
_o = M._cg_call(cg, {"op": "read", "node_id": _bad})
|
|
292
|
+
except Exception as _e: # noqa: BLE001
|
|
293
|
+
_o = {"_raised": "%s: %s" % (type(_e).__name__, _e)}
|
|
294
|
+
check("M2-B6 非 str node_id=%s → node_id_not_str(修前裸 null)" % _tn,
|
|
295
|
+
isinstance(_o, dict) and _o.get("ok") is False
|
|
296
|
+
and _o.get("error") == "node_id_not_str"
|
|
297
|
+
and _o.get("got_type") == _tn and _o.get("node_id") == _bad,
|
|
298
|
+
str(_o)[:140])
|
|
299
|
+
# 取值钳(先例:修前非 str 会返回裸 null/抛 TypeError,直接 .get 拖崩文件)
|
|
300
|
+
def _err(args):
|
|
301
|
+
try:
|
|
302
|
+
_r = M._cg_call(cg, args)
|
|
303
|
+
except Exception as _e: # noqa: BLE001
|
|
304
|
+
return "RAISED:%s" % type(_e).__name__
|
|
305
|
+
return _r.get("error") if isinstance(_r, dict) else _r
|
|
306
|
+
|
|
307
|
+
check("M2-B7 两态可区分:类型错 = 结构化 error,不存在 = 协议登记的 null",
|
|
308
|
+
_err({"op": "read", "node_id": 1}) == "node_id_not_str"
|
|
309
|
+
and _err({"op": "read", "node_id": "zzz"}) is None,
|
|
310
|
+
"type=%r miss=%r" % (_err({"op": "read", "node_id": 1}),
|
|
311
|
+
_err({"op": "read", "node_id": "zzz"})))
|
|
312
|
+
check("M2-B8 显式空串 node_id 不再静默降级为检索(判据 is not None)",
|
|
313
|
+
M._cg_call(cg, {"op": "read", "node_id": ""}) is None,
|
|
314
|
+
repr(M._cg_call(cg, {"op": "read", "node_id": ""}))[:140])
|
|
315
|
+
check("M2-B8b 空串走「节点不存在」路而非 query 检索路(无 results/meta)",
|
|
316
|
+
not isinstance(M._cg_call(cg, {"op": "read", "node_id": ""}), dict),
|
|
317
|
+
repr(M._cg_call(cg, {"op": "read", "node_id": ""}))[:140])
|
|
318
|
+
_znone = M._cg_call(cg, {"op": "read", "node_id": None, "query": "红按钮"})
|
|
319
|
+
check("M2-B9 node_id=None 仍是「未传」,走检索(不回归)",
|
|
320
|
+
"results" in _znone and len(_znone["results"]) > 0, str(_znone)[:100])
|
|
321
|
+
|
|
322
|
+
# ---- M4-C. 端到端:action_source 恒定在场 ----
|
|
323
|
+
_o, pid = M._dispatch(cg, "mdcg_propose", {"node_id": "p_act",
|
|
324
|
+
"content": CCG}), None
|
|
325
|
+
pid = _o.get("pid")
|
|
326
|
+
ex1 = M._cg_call(cg, {"op": "review", "action": "list"})
|
|
327
|
+
check("M4-C1 显式 action → action_source='explicit'",
|
|
328
|
+
ex1.get("action_source") == "explicit", str(ex1.get("action_source")))
|
|
329
|
+
check("M4-C2 显式态下 action_derived 不出现(保持旧契约,C5 依赖)",
|
|
330
|
+
"action_derived" not in ex1, sorted(ex1.keys())[:12])
|
|
331
|
+
sig = M._cg_call(cg, {"op": "review", "pid": pid, "decision": "accept",
|
|
332
|
+
"reason": "读面闸自测"})
|
|
333
|
+
check("M4-C3 签名推导 → action_source='sig' + action_derived + hint_action",
|
|
334
|
+
sig.get("action_source") == "sig" and sig.get("action_derived") is True
|
|
335
|
+
and bool(sig.get("hint_action")), str(sig)[:140])
|
|
336
|
+
dfl = M._cg_call(cg, {"op": "review"})
|
|
337
|
+
check("M4-C4 无签名走默认 → action_source='default' 且点名默认 action",
|
|
338
|
+
dfl.get("action_source") == "default" and dfl.get("action") == "list"
|
|
339
|
+
and "list" in (dfl.get("hint_action") or ""), str(dfl)[:140])
|
|
340
|
+
dfl2 = M._cg_call(cg, {"op": "protect"})
|
|
341
|
+
check("M4-C5 protect 缺省 stats 同样透出 action_source='default'",
|
|
342
|
+
dfl2.get("action_source") == "default" and dfl2.get("action") == "stats",
|
|
343
|
+
str({k: dfl2.get(k) for k in ("action", "action_source")}))
|
|
344
|
+
ex2 = M._cg_call(cg, {"op": "protect", "action": "stats"})
|
|
345
|
+
check("M4-C6 protect 显式 stats → 'explicit'",
|
|
346
|
+
ex2.get("action_source") == "explicit", str(ex2.get("action_source")))
|
|
347
|
+
none_ = M._cg_call(cg, {"op": "read", "query": "红按钮"})
|
|
348
|
+
check("M4-C7 该 op 无默认(read)→ action_source='none'(不编造默认)",
|
|
349
|
+
none_.get("action_source") == "none", str(none_.get("action_source")))
|
|
350
|
+
for _op in ("review", "task", "recent", "goal", "protect", "scrub", "link",
|
|
351
|
+
"theory", "ccg", "ref", "whitebox", "read", "status"):
|
|
352
|
+
_r = M._cg_call(cg, {"op": _op})
|
|
353
|
+
check("M4-C8 「推导发生过」恒定在场:op=%s" % _op,
|
|
354
|
+
isinstance(_r, dict) and "action_source" in _r, str(_r)[:90])
|
|
355
|
+
finally:
|
|
356
|
+
import shutil
|
|
357
|
+
shutil.rmtree(root, ignore_errors=True)
|
|
358
|
+
|
|
359
|
+
print("=" * 64)
|
|
360
|
+
print("read_face_input_gates:%d 通过 / %d 失败" % (_ok, len(_fail)))
|
|
361
|
+
if _fail:
|
|
362
|
+
print("失败项:" + "; ".join(_fail))
|
|
363
|
+
sys.exit(1 if _fail else 0)
|