@furongjun1999/dsh-memory 0.6.0 → 0.6.1
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 +588 -572
- package/codebuddy/CODEBUDDY.md +10 -10
- package/data/policy.json +27 -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/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//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/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 +20 -0
- 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 +12 -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 +21 -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//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//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_p0.py +1 -1
- package/md_cg/branches.py +2 -2
- package/md_cg/ccgc.py +1071 -1006
- package/md_cg/chain.py +1 -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/fsutil.py +454 -8
- 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 +4152 -3818
- package/md_cg/mdcg.py +4208 -3547
- package/md_cg/mdcos.py +4491 -4159
- package/md_cg/mreview/pipeline.py +15 -1
- package/md_cg/nodefile.py +639 -575
- 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/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 +1299 -1168
- package/md_cg/tasks.py +469 -470
- 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_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_govern_directread.py +17 -4
- package/md_cg/test_h2_session_view_norm.py +233 -0
- package/md_cg/test_h4_sustain_snapshot.py +1377 -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 +513 -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_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_p30_maintain.py +33 -2
- package/md_cg/test_p31_insight.py +1 -0
- 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 +516 -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_server_version.py +67 -0
- package/md_cg/test_srcindex.py +171 -0
- package/md_cg/test_tenant_env_override_warn.py +63 -50
- package/md_cg/test_token_lowercase_form.py +315 -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/wisdom/knowledge_points.py +1 -1
- package/md_cg/writelimit.py +42 -8
- package/md_cg/writepipe.py +651 -554
- package/package.json +3 -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/zcode/AGENTS.md +10 -10
|
@@ -0,0 +1,563 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""md_cg · 写面绕闸族续深(N214 + N215 + N221 + N222,同批四写点)
|
|
3
|
+
|
|
4
|
+
四个写点同族同错型:**直调 `cg._write_node`(或改写既有节点的层位/正文),
|
|
5
|
+
全程既无 `principal.require_layer_write` 亦无 `protect.guard_write`**——同一身份
|
|
6
|
+
对**同层**的 `add` 被正常 `AccessDenied`,但这些出口照样落盘;且不抛错、不落
|
|
7
|
+
`_protected_history` 快照、不写 `_protected_audit.jsonl`(「同一库多条出口口径
|
|
8
|
+
不一致」,与 N131 / N197+N208 / N209 同一族)。
|
|
9
|
+
|
|
10
|
+
① **N214 `forgetting.reinforce`(MERGE 落库动作,`md_cg/forgetting.py:247/274`)**
|
|
11
|
+
两个入口都裸调 `cg._write_node` 覆写既有节点:
|
|
12
|
+
· `md_cg/mdcos.py:3380` = `remember_gated` 的 MERGE 分支;
|
|
13
|
+
· `md_cg/mdcos.py:2783` = `cg(op=maintain, action=prefeed, write=true)` 的
|
|
14
|
+
reinforce 分支(`md_cg/mcp_server.py:2748-2750` 只在**无写权**时才
|
|
15
|
+
`require_admin`,故有写权的非管理身份一路直通)。
|
|
16
|
+
持 write 身份(`tokens.py:150` reflect `layers_allow=('contextual',)`)对
|
|
17
|
+
**self 层既有节点**以 `layer='self'` 发 `mdcg_remember(gated=true)`:
|
|
18
|
+
`writelimit.check` 因形参层≠contextual 直接放行(`writelimit.py:141`)、
|
|
19
|
+
`forgetting.assess` 按形参层过滤得重复度 1.0 → 判 MERGE → reinforce 改写
|
|
20
|
+
`importance`/`merge_count`/`last_merge_at` 并自动打 `protected=True`。
|
|
21
|
+
实测(修复前,本守卫红态):`{'verdict':'MERGE','merged_into':'self214',
|
|
22
|
+
'reinforced':{'importance':0.95,'merge_count':1,'protected':True}}`,
|
|
23
|
+
同身份对同层 `add(self214, layer='self')` 却是 `AccessDenied`。
|
|
24
|
+
|
|
25
|
+
② **N215 `writelimit.converge_into`(CONVERGE 落库动作,`md_cg/writelimit.py:211/225`)**
|
|
26
|
+
同构聚合兜底:`remember_gated` 先以 `(node_id=<anchor 节点>, layer='contextual')`
|
|
27
|
+
预占位签名(该次 `add` 被保护闸拒,但 `check` 已落盘 `sig→anchor id` 映射,
|
|
28
|
+
`writelimit.py:187`),再换正文重发 → `gate.verdict=CONVERGE/target=anchor`
|
|
29
|
+
→ 追加聚合行、`merge_count+1`(每次换正文追加一行),**anchor 层不可篡改
|
|
30
|
+
节点被无痕追加**;`_forgetting.jsonl` 末行不含 anchor id(留痕不记目标)。
|
|
31
|
+
|
|
32
|
+
③ **N221 新增:`add` 覆写既有节点时层位由形参 `layer` 全权决定**
|
|
33
|
+
(`md_cg/mdcg.py:1560/1571` 落层、`:1794-1801` 旧层同 id 文件 `os.remove`、
|
|
34
|
+
`md_cg/mdcos.py:3889` 层闸校验**同一形参**)。传「自己可写层」即可覆写并
|
|
35
|
+
顶替他层既有节点:新文件落自己层、旧层同 id 文件被删、索引改指新路径——
|
|
36
|
+
语义等同 `_move_layer` 搬迁,却绕过 N209 的源/目标双 `protect.require_layer`
|
|
37
|
+
与 `guard_move`,无降级审计、原内容被删(等于对他人层节点的**删除+顶替**)。
|
|
38
|
+
|
|
39
|
+
④ **N222 新增:`scrub._apply_offset`(calibrate 落库动作,`md_cg/scrub.py:718/747`)**
|
|
40
|
+
全库遍历式改写 `layer∉(self,anchor)` 且 `evidence_count≥1` 的节点
|
|
41
|
+
`confidence`/`calibration`:不过 principal 层白名单、不过 `guard_overwrite`、
|
|
42
|
+
`override` 形参直通(含 `importance≥0.7` 的不可遗忘节点),无快照无审计。
|
|
43
|
+
`md_cg/mcp_server.py:1671-1674`(`_scrub_call` 对 calibrate 不设
|
|
44
|
+
`require_admin`)。持 sustain 身份(`layers_allow=('self',)`)即可改
|
|
45
|
+
knowledge 层节点的置信度。
|
|
46
|
+
|
|
47
|
+
修复(最小改动 · 同族单点,**复用** N197/N208 新建的 `protect.guard_overwrite`
|
|
48
|
+
与 N209 新建的 `protect.require_layer`,不另造第二份口径):
|
|
49
|
+
① `forgetting.reinforce` / `writelimit.converge_into` 写盘前统一过
|
|
50
|
+
`protect.guard_overwrite(cg, target, layer=fm.layer, sensitivity=fm.sensitivity,
|
|
51
|
+
override=…, actor=…)`——principal 层写闸在前(对照 `MdCGSecure.add`),引擎级
|
|
52
|
+
`guard_write` 在后(对照 `MdCG.add`),`override=True` 时自动快照 + 审计留痕。
|
|
53
|
+
② `MdCG.add`:既有节点**跨层覆写**(`prev_entry.layer != layer`)= 搬迁语义,
|
|
54
|
+
与 `_move_layer` 同口径补上**源层** `protect.require_layer` + `guard_move`
|
|
55
|
+
(目标层闸由 `MdCGSecure.add` 的形参层闸承担,且此形参层即真目标层)。
|
|
56
|
+
③ `scrub._apply_offset` 写盘前过 `protect.guard_overwrite`(层/敏感度取节点
|
|
57
|
+
fm 真值),`actor` 由 `calibrate` 透传。
|
|
58
|
+
|
|
59
|
+
守卫(六组,全部临时库 + 哑主密钥 + 临时令牌库,**绝不触真实 ~/.mdcg**):
|
|
60
|
+
⓿ 隔离自证:aux 根 / 令牌库都须落在守卫临时目录,否则中止;
|
|
61
|
+
① N214:reflect 越层 MERGE 被层闸拒 + 盘面零改写 + 零留痕;designer 对 self
|
|
62
|
+
层节点被**保护闸**拒(层闸对其放行,故该腿单独钉住 guard_write);
|
|
63
|
+
`override=True` 正路落快照+审计;反向腿(同层 contextual MERGE 仍成功);
|
|
64
|
+
工具面 `op=maintain action=prefeed write=true` 同样被拒;
|
|
65
|
+
② N215:预占位签名 + 换正文重发 → 越层聚合被拒 + anchor 正文逐字未变;
|
|
66
|
+
designer 直调 `converge_into` 被保护闸拒、`override=True` 落快照+审计;
|
|
67
|
+
反向腿(同层 contextual 聚合仍成功);
|
|
68
|
+
③ N221:reflect 跨层覆写 knowledge/goals 节点被拒(库面 + `mdcg_remember`
|
|
69
|
+
工具面)+ 旧层文件与索引零改写;反向腿(新建节点 / 同层覆写 /
|
|
70
|
+
designer 同层覆写仍成功);
|
|
71
|
+
④ N222:sustain 工具面 `op=scrub action=calibrate apply=true`(含
|
|
72
|
+
`override=true` 腿)被层闸拒 + 置信度零改写;反向腿 designer 校准仍生效;
|
|
73
|
+
⑤ 源断言 + floor:四处写点前都出现 `guard_overwrite`/跨层闸,实弹断言计数下限。
|
|
74
|
+
|
|
75
|
+
运行:python -X utf8 -m md_cg.test_n214_n215_n221_n222_write_face_gates
|
|
76
|
+
"""
|
|
77
|
+
from __future__ import annotations
|
|
78
|
+
|
|
79
|
+
import atexit
|
|
80
|
+
import json
|
|
81
|
+
import os
|
|
82
|
+
import shutil
|
|
83
|
+
import sys
|
|
84
|
+
import tempfile
|
|
85
|
+
import time
|
|
86
|
+
|
|
87
|
+
# ── 隔离前置:必须在 import md_cg 子模块**之前**(tokens/theory 等在模块级把
|
|
88
|
+
# aux_root() 冻成常量)——否则会指到真实 ~/.mdcg 并在那里建密钥/令牌库。 ──
|
|
89
|
+
_TMP = tempfile.mkdtemp(prefix="n214_")
|
|
90
|
+
os.environ["MDCG_AUX_ROOT"] = os.path.join(_TMP, "auxroot")
|
|
91
|
+
os.environ["MDCG_MASTER_KEY"] = "cd" * 32 # 哑主密钥(绝不触真实密钥面)
|
|
92
|
+
os.environ["MDCG_TOKEN_FILE"] = os.path.join(_TMP, "tokens.json")
|
|
93
|
+
os.environ["MDCG_ROOT"] = os.path.join(_TMP, "cgroot")
|
|
94
|
+
for _k in ("MDCG_TOKEN", "MDCG_TENANT", "MDCG_CLEARANCE", "MDCG_SESSION",
|
|
95
|
+
"DSH_SESSION_ID", "MDCG_LEGACY_ENV_AUTH", "MDCG_LEGACY_ENV_ADMIN",
|
|
96
|
+
"MDCG_CAN_ADMIN", "MDCG_CAN_WRITE", "MDCG_STATE_ROOT",
|
|
97
|
+
"MDCG_DATA_ROOT", "MDCG_TENANT_REGISTRY", "MDCG_VERIFIER_MODULES",
|
|
98
|
+
"PYTHONIOENCODING", "PYTHONUTF8"):
|
|
99
|
+
os.environ.pop(_k, None)
|
|
100
|
+
|
|
101
|
+
from . import forgetting, mcp_server, protect, scrub, tokens, writelimit # noqa: E402
|
|
102
|
+
from .datapath import aux_root # noqa: E402
|
|
103
|
+
from .mdcos import MdCGSecure # noqa: E402
|
|
104
|
+
from .security import Principal # noqa: E402
|
|
105
|
+
|
|
106
|
+
atexit.register(lambda: _cleanup(_TMP)) # LIFO:晚于库层 _atexit_flush_all
|
|
107
|
+
|
|
108
|
+
try:
|
|
109
|
+
sys.stdout.reconfigure(encoding="utf-8", errors="replace")
|
|
110
|
+
except AttributeError:
|
|
111
|
+
pass
|
|
112
|
+
|
|
113
|
+
ROOT = os.path.join(_TMP, "cgroot")
|
|
114
|
+
PASS = FAIL = LIVE = 0
|
|
115
|
+
LIVE_FLOOR = 18
|
|
116
|
+
FAILS = []
|
|
117
|
+
|
|
118
|
+
SELF_ID, ANCHOR_ID = "self214", "anchor215"
|
|
119
|
+
KN221_ID, CTX_ID = "kn221", "ctx214"
|
|
120
|
+
KN222P = "kn222p"
|
|
121
|
+
EVID = ["kn222_%d" % i for i in range(5)]
|
|
122
|
+
SELF_BODY = "# 自我认知卡(甲):我是灵枢,服务于设计者。\n"
|
|
123
|
+
ANCHOR_BODY = "# 身份锚点(丙):灵枢的锚点声明。\n"
|
|
124
|
+
KN221_BODY = "# 知识条目(丁):离线优先的网络探测策略。\n"
|
|
125
|
+
CTX_BODY = "# 情境记录(乙):今日试用新的检索路径。\n"
|
|
126
|
+
# N215 攻击腿:同模板签名、正文不同(模板骨架去数字后一致)
|
|
127
|
+
T1 = "# 功能名:批次流水核对报告\n本批次已收官并归档。\n"
|
|
128
|
+
T2 = "# 功能名:批次流水核对报告\n本批次已复核并放行。\n"
|
|
129
|
+
# 反向腿用**另一条**模板签名(避免与攻击腿共用 sig→target 映射)
|
|
130
|
+
R1 = "# 功能名:情境记录归档复核\n本日情境已归档。\n"
|
|
131
|
+
R2 = "# 功能名:情境记录归档复核\n本日情境已复核。\n"
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def check(name, cond, detail="", live=False):
|
|
135
|
+
global PASS, FAIL, LIVE
|
|
136
|
+
if live:
|
|
137
|
+
LIVE += 1
|
|
138
|
+
if cond:
|
|
139
|
+
PASS += 1
|
|
140
|
+
print(" [PASS] %s" % name)
|
|
141
|
+
else:
|
|
142
|
+
FAIL += 1
|
|
143
|
+
FAILS.append(name)
|
|
144
|
+
print(" [FAIL] %s · %s" % (name, detail))
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def _princ(role, clearance="internal", can_admin=False):
|
|
148
|
+
spec = tokens.role_spec(role)
|
|
149
|
+
return Principal(tenant="default", actor="n214-" + role,
|
|
150
|
+
clearance=clearance, can_write=bool(spec["can_write"]),
|
|
151
|
+
can_admin=can_admin, role=role,
|
|
152
|
+
layers_allow=spec["layers_allow"],
|
|
153
|
+
ops_allow=spec["ops_allow"], auth_mode="test")
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
_OPEN = []
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def _secured(principal):
|
|
160
|
+
cg = MdCGSecure(ROOT, principal=principal)
|
|
161
|
+
_OPEN.append(cg)
|
|
162
|
+
return cg
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
def _close_all():
|
|
166
|
+
for cg in _OPEN:
|
|
167
|
+
try:
|
|
168
|
+
cg.close()
|
|
169
|
+
except Exception: # noqa: BLE001 —— 清理面不得再抛
|
|
170
|
+
pass
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
def _cleanup(path) -> bool:
|
|
174
|
+
import gc
|
|
175
|
+
for _ in range(6):
|
|
176
|
+
shutil.rmtree(path, ignore_errors=True)
|
|
177
|
+
if not os.path.exists(path):
|
|
178
|
+
return True
|
|
179
|
+
gc.collect()
|
|
180
|
+
time.sleep(0.25)
|
|
181
|
+
return not os.path.exists(path)
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
def _raises(fn, *a, **kw):
|
|
185
|
+
"""执行并返回 (ok, 异常类名/空, 结果/异常文本)。"""
|
|
186
|
+
try:
|
|
187
|
+
return True, "", fn(*a, **kw)
|
|
188
|
+
except Exception as exc: # noqa: BLE001 —— 守卫要的是拒绝形态
|
|
189
|
+
return False, type(exc).__name__, str(exc)
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
def _fresh():
|
|
193
|
+
"""盘面真值读取器:同 root 新建实例(自带全量重扫,不依赖他实例内存索引)。"""
|
|
194
|
+
return _secured(_princ("designer", clearance="secret", can_admin=True))
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
def _disk(nid):
|
|
198
|
+
"""(frontmatter, content, index 条目) —— 从盘面重扫所得(非调用方内存索引)。"""
|
|
199
|
+
cg = _fresh()
|
|
200
|
+
node = cg.get(nid) or {}
|
|
201
|
+
entry = (cg.index.get("nodes") or {}).get(nid) or {}
|
|
202
|
+
return (node.get("frontmatter") or {}, node.get("content") or "", entry)
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
def _audit_rows():
|
|
206
|
+
p = os.path.join(ROOT, protect.AUDIT_FILE)
|
|
207
|
+
if not os.path.exists(p):
|
|
208
|
+
return []
|
|
209
|
+
out = []
|
|
210
|
+
try:
|
|
211
|
+
with open(p, "r", encoding="utf-8") as f:
|
|
212
|
+
for line in f:
|
|
213
|
+
line = line.strip()
|
|
214
|
+
if line:
|
|
215
|
+
try:
|
|
216
|
+
out.append(json.loads(line))
|
|
217
|
+
except ValueError:
|
|
218
|
+
continue
|
|
219
|
+
except OSError:
|
|
220
|
+
return []
|
|
221
|
+
return out
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
def _src_window(src, start, end):
|
|
225
|
+
i = src.find(start)
|
|
226
|
+
if i < 0:
|
|
227
|
+
return ""
|
|
228
|
+
j = src.find(end, i)
|
|
229
|
+
return src[i:] if j < 0 else src[i:j]
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
def main():
|
|
233
|
+
global LIVE
|
|
234
|
+
# ---------- ⓿ 隔离自证 ----------
|
|
235
|
+
print("[0] 隔离自证")
|
|
236
|
+
_tok = tokens.token_file()
|
|
237
|
+
check("0a 默认令牌库与 aux 根都落在守卫临时目录(绝不触真实凭据)",
|
|
238
|
+
os.path.abspath(_tok).lower().startswith(os.path.abspath(_TMP).lower())
|
|
239
|
+
and os.path.abspath(aux_root()).lower().startswith(
|
|
240
|
+
os.path.abspath(_TMP).lower()),
|
|
241
|
+
"tok=%s aux=%s" % (_tok, aux_root()))
|
|
242
|
+
if not os.path.abspath(ROOT).lower().startswith(os.path.abspath(_TMP).lower()):
|
|
243
|
+
print("FATAL: 隔离失败,中止(绝不带病跑真实库)")
|
|
244
|
+
return 2
|
|
245
|
+
|
|
246
|
+
# ---------- 夹具 ----------
|
|
247
|
+
designer = _secured(_princ("designer", clearance="secret", can_admin=True))
|
|
248
|
+
designer.add(SELF_ID, SELF_BODY, layer="self", importance=0.9)
|
|
249
|
+
designer.add(ANCHOR_ID, ANCHOR_BODY, layer="anchor", importance=0.9)
|
|
250
|
+
designer.add(KN221_ID, KN221_BODY, layer="knowledge", importance=0.4)
|
|
251
|
+
designer.add(CTX_ID, CTX_BODY, layer="contextual", importance=0.4)
|
|
252
|
+
goal_id = designer.add_goal("把检索延迟降到 100ms 以内")
|
|
253
|
+
for i, nid in enumerate(EVID):
|
|
254
|
+
designer.add(nid, "# 知识条目(己%d):校准样本。\n" % i,
|
|
255
|
+
layer="knowledge", confidence=0.8,
|
|
256
|
+
evidence_count=1, positive_evidence=1)
|
|
257
|
+
designer.add(KN222P, "# 知识条目(庚):高重要度校准样本。\n",
|
|
258
|
+
layer="knowledge", importance=0.9, confidence=0.8,
|
|
259
|
+
evidence_count=1, positive_evidence=1)
|
|
260
|
+
# 校准面(N222)读的是**索引**中的 evidence_count(add 的 _stage 不带该键)
|
|
261
|
+
# ——按盘面重扫一次,与攻击材料的起手式一致。
|
|
262
|
+
designer.rebuild_index()
|
|
263
|
+
designer.flush()
|
|
264
|
+
P_KN221 = (designer.index["nodes"].get(KN221_ID) or {}).get("path")
|
|
265
|
+
check("0b 夹具就位(self / anchor / knowledge / contextual / goals 五层各一,"
|
|
266
|
+
"校准样本 5+1 个;索引层位可查)",
|
|
267
|
+
(designer.index["nodes"].get(SELF_ID) or {}).get("layer") == "self"
|
|
268
|
+
and (designer.index["nodes"].get(ANCHOR_ID) or {}).get("layer") == "anchor"
|
|
269
|
+
and (designer.index["nodes"].get(KN221_ID) or {}).get("layer") == "knowledge"
|
|
270
|
+
and bool(goal_id)
|
|
271
|
+
and (designer.index["nodes"].get(goal_id) or {}).get("layer") == "goals"
|
|
272
|
+
and all((designer.index["nodes"].get(n) or {}).get("evidence_count") == 1
|
|
273
|
+
for n in EVID + [KN222P]),
|
|
274
|
+
"kn221_path=%s goal=%s" % (P_KN221, goal_id))
|
|
275
|
+
|
|
276
|
+
# ---------- ① N214 MERGE→forgetting.reinforce ----------
|
|
277
|
+
print("[1] N214 forgetting.reinforce:越层 MERGE / 保护闸 / 快照审计 / 反向腿")
|
|
278
|
+
rec = _secured(_princ("reflect"))
|
|
279
|
+
ok, exc, out = _raises(rec.remember_gated, "n214a", SELF_BODY,
|
|
280
|
+
layer="self", role="user")
|
|
281
|
+
check("1a reflect(layers=('contextual',))对 self 层既有节点 MERGE 被拒"
|
|
282
|
+
"(修前 = 覆写 importance/merge_count 并自动打保护位)",
|
|
283
|
+
(not ok) and exc == "AccessDenied",
|
|
284
|
+
"exc=%s out=%s" % (exc, str(out)[:120]), live=True)
|
|
285
|
+
fm_self, cont_self, _e = _disk(SELF_ID)
|
|
286
|
+
check("1b 被拒后盘面零改写(importance 仍 0.9、无 merge_count、正文未变)",
|
|
287
|
+
float(fm_self.get("importance") or 0) == 0.9
|
|
288
|
+
and not fm_self.get("merge_count") and cont_self == SELF_BODY,
|
|
289
|
+
"imp=%s mc=%s" % (fm_self.get("importance"), fm_self.get("merge_count")),
|
|
290
|
+
live=True)
|
|
291
|
+
check("1c 被拒路径零留痕(无 _protected_history/self214 快照、审计无该条)",
|
|
292
|
+
protect.history(rec, SELF_ID) == []
|
|
293
|
+
and not [r for r in _audit_rows() if r.get("node_id") == SELF_ID],
|
|
294
|
+
"history=%s audit=%d" % (protect.history(rec, SELF_ID),
|
|
295
|
+
len(_audit_rows())))
|
|
296
|
+
ok_d, exc_d, out_d = _raises(forgetting.reinforce, _fresh(), SELF_ID)
|
|
297
|
+
check("1d 接线自证:designer(层闸放行)直调 reinforce 覆写 self 层节点仍被"
|
|
298
|
+
"**保护闸**拒(修前 = 无痕覆写成功;该腿单独钉住 guard_write)",
|
|
299
|
+
(not ok_d) and exc_d in ("ProtectionError", "PermissionError"),
|
|
300
|
+
"exc=%s out=%s" % (exc_d, str(out_d)[:120]), live=True)
|
|
301
|
+
ok_o, exc_o, out_o = _raises(forgetting.reinforce, _fresh(), SELF_ID,
|
|
302
|
+
override=True, actor="designer")
|
|
303
|
+
check("1e override=True 正路:落 _protected_history 快照 + _protected_audit.jsonl"
|
|
304
|
+
"(保护不等于黑箱;修前该路径无任何留痕)",
|
|
305
|
+
ok_o and out_o and out_o.get("merge_count") == 1
|
|
306
|
+
and len(protect.history(_fresh(), SELF_ID)) == 1
|
|
307
|
+
and [r for r in _audit_rows()
|
|
308
|
+
if r.get("node_id") == SELF_ID and r.get("action") == "override_write"],
|
|
309
|
+
"ok=%s exc=%s out=%s hist=%s" % (ok_o, exc_o, str(out_o)[:80],
|
|
310
|
+
protect.history(_fresh(), SELF_ID)),
|
|
311
|
+
live=True)
|
|
312
|
+
rec2 = _secured(_princ("reflect"))
|
|
313
|
+
ok_r, exc_r, out_r = _raises(rec2.remember_gated, "ctxrev0", CTX_BODY,
|
|
314
|
+
layer="contextual", role="user")
|
|
315
|
+
check("1f 反向腿:reflect 对**自己层**既有节点 MERGE 仍成功(未一刀切收紧)",
|
|
316
|
+
ok_r and out_r.get("verdict") == "MERGE"
|
|
317
|
+
and out_r.get("merged_into") == CTX_ID,
|
|
318
|
+
"exc=%s out=%s" % (exc_r, str(out_r)[:120]), live=True)
|
|
319
|
+
rec3 = _secured(_princ("reflect"))
|
|
320
|
+
_mc_before = _disk(SELF_ID)[0].get("merge_count")
|
|
321
|
+
ok_t, exc_t, out_t = _raises(mcp_server.call_tool, rec3, "cg",
|
|
322
|
+
{"op": "maintain", "action": "prefeed",
|
|
323
|
+
"write": True, "content": SELF_BODY,
|
|
324
|
+
"layer": "self", "role": "user",
|
|
325
|
+
"node_id": "n214b"})
|
|
326
|
+
fm_self2, _c2, _e2 = _disk(SELF_ID)
|
|
327
|
+
check("1g 第二入口(工具面 op=maintain action=prefeed write=true)同拒"
|
|
328
|
+
"(mcp_server 只在无写权时才 require_admin)",
|
|
329
|
+
(not ok_t) and exc_t in ("AccessDenied", "ProtectionError")
|
|
330
|
+
and fm_self2.get("merge_count") == _mc_before,
|
|
331
|
+
"exc=%s out=%s mc=%s→%s" % (exc_t, str(out_t)[:120], _mc_before,
|
|
332
|
+
fm_self2.get("merge_count")), live=True)
|
|
333
|
+
|
|
334
|
+
# ---------- ② N215 CONVERGE→writelimit.converge_into ----------
|
|
335
|
+
print("[2] N215 writelimit.converge_into:越层聚合 / 保护闸 / 快照审计 / 反向腿")
|
|
336
|
+
rec4 = _secured(_princ("reflect"))
|
|
337
|
+
ok_p, exc_p, out_p = _raises(rec4.remember_gated, ANCHOR_ID, T1,
|
|
338
|
+
layer="contextual", role="user")
|
|
339
|
+
sig_state = {}
|
|
340
|
+
try:
|
|
341
|
+
with open(os.path.join(ROOT, writelimit.STATE_FILE), encoding="utf-8") as fh:
|
|
342
|
+
sig_state = json.load(fh).get("sigs") or {}
|
|
343
|
+
except OSError:
|
|
344
|
+
sig_state = {}
|
|
345
|
+
check("2a 攻击前置成立:首次调用虽被保护闸拒,签名已预占位为 anchor id"
|
|
346
|
+
"(修前修后同态,是攻击链的起点)",
|
|
347
|
+
(not ok_p) and exc_p in ("ProtectionError", "AccessDenied")
|
|
348
|
+
and any(v.get("nid") == ANCHOR_ID for v in sig_state.values()),
|
|
349
|
+
"exc=%s sigs=%s" % (exc_p, json.dumps(sig_state, ensure_ascii=False)[:120]))
|
|
350
|
+
rec5 = _secured(_princ("reflect"))
|
|
351
|
+
ok_c, exc_c, out_c = _raises(rec5.remember_gated, "n215b", T2,
|
|
352
|
+
layer="contextual", role="user")
|
|
353
|
+
check("2b 换正文重发 → 越层同构聚合被拒(修前 = CONVERGE/target=anchor 并追加"
|
|
354
|
+
"聚合行)",
|
|
355
|
+
(not ok_c) and exc_c == "AccessDenied",
|
|
356
|
+
"exc=%s out=%s" % (exc_c, str(out_c)[:160]), live=True)
|
|
357
|
+
_fm_a, cont_a, _ea = _disk(ANCHOR_ID)
|
|
358
|
+
check("2c anchor 层节点正文逐字未变(无【聚合】行、无 merge_count)",
|
|
359
|
+
cont_a == ANCHOR_BODY and "【聚合" not in cont_a
|
|
360
|
+
and not _fm_a.get("merge_count"),
|
|
361
|
+
"content=%r mc=%s" % (cont_a[:80], _fm_a.get("merge_count")), live=True)
|
|
362
|
+
ok_g, exc_g, out_g = _raises(writelimit.converge_into, _fresh(), ANCHOR_ID,
|
|
363
|
+
"# 探针正文(凑)\n")
|
|
364
|
+
check("2d 接线自证:designer 直调 converge_into 追加 anchor 层节点仍被保护闸拒"
|
|
365
|
+
"(修前 = 追加成功)",
|
|
366
|
+
(not ok_g) and exc_g in ("ProtectionError", "PermissionError"),
|
|
367
|
+
"exc=%s out=%s" % (exc_g, str(out_g)[:120]), live=True)
|
|
368
|
+
ok_go, exc_go, out_go = _raises(writelimit.converge_into, _fresh(), ANCHOR_ID,
|
|
369
|
+
"# 探针正文(凑)\n", override=True,
|
|
370
|
+
actor="designer")
|
|
371
|
+
check("2e override=True 正路:落快照 + 审计后才追加(修前无任何留痕)",
|
|
372
|
+
ok_go and out_go and out_go.get("ok") is True
|
|
373
|
+
and len(protect.history(_fresh(), ANCHOR_ID)) == 1
|
|
374
|
+
and [r for r in _audit_rows()
|
|
375
|
+
if r.get("node_id") == ANCHOR_ID and r.get("action") == "override_write"],
|
|
376
|
+
"ok=%s exc=%s out=%s hist=%s" % (ok_go, exc_go, str(out_go)[:80],
|
|
377
|
+
protect.history(_fresh(), ANCHOR_ID)),
|
|
378
|
+
live=True)
|
|
379
|
+
rec6 = _secured(_princ("reflect"))
|
|
380
|
+
ok_1, exc_1, out_1 = _raises(rec6.remember_gated, "ctxrev1", R1,
|
|
381
|
+
layer="contextual", role="user")
|
|
382
|
+
rec7 = _secured(_princ("reflect"))
|
|
383
|
+
ok_2, exc_2, out_2 = _raises(rec7.remember_gated, "ctxrev2", R2,
|
|
384
|
+
layer="contextual", role="user")
|
|
385
|
+
_fm_cr, cont_cr, _ecr = _disk("ctxrev1")
|
|
386
|
+
check("2f 反向腿:reflect 对**自己层**上下文节点同构聚合仍成功(聚合行落盘)",
|
|
387
|
+
ok_1 and ok_2 and out_2.get("gate", {}).get("verdict") == "CONVERGE"
|
|
388
|
+
and out_2.get("merged_into") == "ctxrev1"
|
|
389
|
+
and "【聚合" in cont_cr,
|
|
390
|
+
"ok1=%s ok2=%s out2=%s" % (ok_1, ok_2, str(out_2)[:140]), live=True)
|
|
391
|
+
|
|
392
|
+
# ---------- ③ N221 add 跨层覆写(搬迁语义) ----------
|
|
393
|
+
print("[3] N221 add 跨层覆写:源层闸 + 搬迁闸 / 工具面 / 反向腿")
|
|
394
|
+
rec8 = _secured(_princ("reflect"))
|
|
395
|
+
ok_k, exc_k, out_k = _raises(rec8.add, KN221_ID, "# 我改写的内容(丁·攻击)\n",
|
|
396
|
+
layer="contextual")
|
|
397
|
+
check("3a reflect 以自己可写层覆写 knowledge 层既有节点被拒(修前 = {'ok':真},"
|
|
398
|
+
"旧层文件被删、节点被顶替到 contextual)",
|
|
399
|
+
(not ok_k) and exc_k == "AccessDenied",
|
|
400
|
+
"exc=%s out=%s" % (exc_k, str(out_k)[:120]), live=True)
|
|
401
|
+
_fm_k, cont_k, entry_k = _disk(KN221_ID)
|
|
402
|
+
check("3b 盘面零搬迁:旧层文件仍在、正文未变、索引仍指旧路径、无新层文件",
|
|
403
|
+
os.path.exists(os.path.join(ROOT, P_KN221))
|
|
404
|
+
and cont_k == KN221_BODY
|
|
405
|
+
and entry_k.get("path") == P_KN221
|
|
406
|
+
and entry_k.get("layer") == "knowledge"
|
|
407
|
+
and not os.path.exists(os.path.join(ROOT, "contextual",
|
|
408
|
+
KN221_ID + ".md")),
|
|
409
|
+
"path=%s/%s cont=%r" % (entry_k.get("path"), P_KN221, cont_k[:40]),
|
|
410
|
+
live=True)
|
|
411
|
+
rec9 = _secured(_princ("reflect"))
|
|
412
|
+
ok_k2, exc_k2, out_k2 = _raises(mcp_server.call_tool, rec9, "mdcg_remember",
|
|
413
|
+
{"node_id": KN221_ID,
|
|
414
|
+
"content": "# 工具面改写(丁)\n",
|
|
415
|
+
"layer": "contextual", "gated": False})
|
|
416
|
+
check("3c 工具面(mdcg_remember gated=false)同拒且盘面零改写",
|
|
417
|
+
(not ok_k2) and exc_k2 == "AccessDenied"
|
|
418
|
+
and _disk(KN221_ID)[1] == KN221_BODY,
|
|
419
|
+
"exc=%s out=%s" % (exc_k2, str(out_k2)[:120]), live=True)
|
|
420
|
+
rec10 = _secured(_princ("reflect"))
|
|
421
|
+
ok_g2, exc_g2, out_g2 = _raises(rec10.add, goal_id, "# 目标被顶替(戊)\n",
|
|
422
|
+
layer="contextual")
|
|
423
|
+
_fm_go, cont_go, entry_go = _disk(goal_id)
|
|
424
|
+
check("3d 同形 goals 腿同拒(目标节点未被顶替出层)",
|
|
425
|
+
(not ok_g2) and exc_g2 == "AccessDenied"
|
|
426
|
+
and entry_go.get("layer") == "goals",
|
|
427
|
+
"exc=%s layer=%s" % (exc_g2, entry_go.get("layer")), live=True)
|
|
428
|
+
rec11 = _secured(_princ("reflect"))
|
|
429
|
+
ok_n, exc_n, out_n = _raises(rec11.add, "ctxnew1", "# 全新情境记录(庚)\n",
|
|
430
|
+
layer="contextual")
|
|
431
|
+
check("3e 反向腿:reflect 新建节点(无既有条目,非搬迁)仍成功",
|
|
432
|
+
ok_n and out_n == "ctxnew1" and _disk("ctxnew1")[2].get("layer")
|
|
433
|
+
== "contextual",
|
|
434
|
+
"exc=%s out=%s" % (exc_n, out_n), live=True)
|
|
435
|
+
rec12 = _secured(_princ("reflect"))
|
|
436
|
+
ok_s, exc_s, out_s = _raises(rec12.add, "ctxnew1", "# 情境记录更新(庚·同层)\n",
|
|
437
|
+
layer="contextual")
|
|
438
|
+
check("3f 反向腿:reflect **同层**覆写自己的节点仍成功(层闸只拦跨层搬迁)",
|
|
439
|
+
ok_s and out_s == "ctxnew1"
|
|
440
|
+
and "同层" in _disk("ctxnew1")[1],
|
|
441
|
+
"exc=%s out=%s" % (exc_s, out_s), live=True)
|
|
442
|
+
_fresh().add(KN221_ID, KN221_BODY + "# 追加一行(丁·同层覆写)\n",
|
|
443
|
+
layer="knowledge", importance=0.4)
|
|
444
|
+
check("3g 反向腿:designer 对 knowledge 层**同层**覆写仍成功",
|
|
445
|
+
"追加一行" in _disk(KN221_ID)[1],
|
|
446
|
+
"content=%r" % _disk(KN221_ID)[1][:60], live=True)
|
|
447
|
+
|
|
448
|
+
# ---------- ④ N222 scrub.calibrate apply ----------
|
|
449
|
+
print("[4] N222 scrub._apply_offset:层闸(含 override 直通腿)/ 反向腿")
|
|
450
|
+
sus = _secured(_princ("sustain"))
|
|
451
|
+
sus.rebuild_index()
|
|
452
|
+
ok_cal, exc_cal, out_cal = _raises(
|
|
453
|
+
mcp_server.call_tool, sus, "cg",
|
|
454
|
+
{"op": "scrub", "action": "calibrate", "apply": True})
|
|
455
|
+
check("4a sustain(layers=('self',))工具面 calibrate apply=true 被层闸拒"
|
|
456
|
+
"(修前 = n_adjusted=5,knowledge 层 confidence 0.8→0.95)",
|
|
457
|
+
(not ok_cal) and exc_cal == "AccessDenied",
|
|
458
|
+
"exc=%s out=%s" % (exc_cal, str(out_cal)[:160]), live=True)
|
|
459
|
+
conf_a = [_disk(n)[0].get("confidence") for n in EVID]
|
|
460
|
+
check("4b 被拒后全库零校准改写(5 个 knowledge 样本 confidence 仍 0.8、"
|
|
461
|
+
"无 calibration 字段)",
|
|
462
|
+
all(float(c or 0) == 0.8 for c in conf_a)
|
|
463
|
+
and not _disk(EVID[0])[0].get("calibration"),
|
|
464
|
+
"conf=%s" % conf_a, live=True)
|
|
465
|
+
sus2 = _secured(_princ("sustain"))
|
|
466
|
+
sus2.rebuild_index()
|
|
467
|
+
ok_cal2, exc_cal2, out_cal2 = _raises(
|
|
468
|
+
mcp_server.call_tool, sus2, "cg",
|
|
469
|
+
{"op": "scrub", "action": "calibrate", "apply": True, "override": True})
|
|
470
|
+
fm_p, _cp, _ep = _disk(KN222P)
|
|
471
|
+
check("4c override=true 腿同样被层闸拒(override 不直通层白名单;"
|
|
472
|
+
"importance≥0.7 的不可遗忘节点未被改写)",
|
|
473
|
+
(not ok_cal2) and exc_cal2 == "AccessDenied"
|
|
474
|
+
and float(fm_p.get("confidence") or 0) == 0.8,
|
|
475
|
+
"exc=%s out=%s conf=%s" % (exc_cal2, str(out_cal2)[:120],
|
|
476
|
+
fm_p.get("confidence")), live=True)
|
|
477
|
+
dcg = _fresh()
|
|
478
|
+
dcg.rebuild_index() # 校准面读索引 evidence_count(与攻击腿同起手式)
|
|
479
|
+
dcal, exc_dcal, out_dcal = _raises(
|
|
480
|
+
mcp_server.call_tool, dcg, "cg",
|
|
481
|
+
{"op": "scrub", "action": "calibrate", "apply": True})
|
|
482
|
+
check("4d 反向腿:designer 同 op 校准仍生效(层闸只拦无层写权的身份)",
|
|
483
|
+
dcal and out_dcal.get("ok") and int(out_dcal.get("n_adjusted") or 0) >= 1
|
|
484
|
+
and float(_disk(EVID[0])[0].get("confidence") or 0) > 0.8,
|
|
485
|
+
"ok=%s exc=%s out=%s" % (dcal, exc_dcal,
|
|
486
|
+
json.dumps({k: out_dcal.get(k) for k in
|
|
487
|
+
("n_adjusted", "skipped_protected")}
|
|
488
|
+
if isinstance(out_dcal, dict) else {},
|
|
489
|
+
ensure_ascii=False)), live=True)
|
|
490
|
+
|
|
491
|
+
# ---------- ⑤ 源断言 + floor ----------
|
|
492
|
+
print("[5] 源断言 + 守卫自检")
|
|
493
|
+
_dir = os.path.dirname(os.path.abspath(__file__))
|
|
494
|
+
|
|
495
|
+
def _src(name):
|
|
496
|
+
with open(os.path.join(_dir, name), encoding="utf-8",
|
|
497
|
+
errors="replace") as fh:
|
|
498
|
+
return fh.read().replace("\r\n", "\n")
|
|
499
|
+
|
|
500
|
+
src_f, src_w, src_s, src_m = (_src("forgetting.py"), _src("writelimit.py"),
|
|
501
|
+
_src("scrub.py"), _src("mdcg.py"))
|
|
502
|
+
win_f = _src_window(src_f, "def reinforce(", "\n\n# ")
|
|
503
|
+
check("5a forgetting.reinforce 写盘前紧邻 guard_overwrite(层/敏感度取节点 "
|
|
504
|
+
"fm 真值),且在 cg._write_node 之前",
|
|
505
|
+
"protect.guard_overwrite(cg, node_id, layer=fm.get(\"layer\"),\n"
|
|
506
|
+
" sensitivity=fm.get(\"sensitivity\"),\n"
|
|
507
|
+
" override=override, actor=actor)"
|
|
508
|
+
in src_f
|
|
509
|
+
and 0 < win_f.find("guard_overwrite")
|
|
510
|
+
< win_f.find("cg._write_node(node_id"),
|
|
511
|
+
"win_len=%d" % len(win_f))
|
|
512
|
+
win_w = _src_window(src_w, "def converge_into(", "\n\n# ")
|
|
513
|
+
check("5b writelimit.converge_into 写盘前紧邻 guard_overwrite 且在 "
|
|
514
|
+
"cg._write_node 之前",
|
|
515
|
+
"protect.guard_overwrite(cg, target, layer=fm.get(\"layer\"),\n"
|
|
516
|
+
" sensitivity=fm.get(\"sensitivity\"),\n"
|
|
517
|
+
" override=override, actor=actor)"
|
|
518
|
+
in src_w
|
|
519
|
+
and 0 < win_w.find("guard_overwrite")
|
|
520
|
+
< win_w.find("cg._write_node(target,"),
|
|
521
|
+
"win_len=%d" % len(win_w))
|
|
522
|
+
win_s = _src_window(src_s, "def _apply_offset(", "def calibrate(")
|
|
523
|
+
check("5c scrub._apply_offset 写盘前紧邻 guard_overwrite(actor 由 calibrate "
|
|
524
|
+
"透传)且在 cg._write_node 之前",
|
|
525
|
+
"protect.guard_overwrite(cg, nid, layer=fm.get(\"layer\"),\n"
|
|
526
|
+
" sensitivity=fm.get(\"sensitivity\"),\n"
|
|
527
|
+
" override=override, actor=actor)"
|
|
528
|
+
in src_s
|
|
529
|
+
and 0 < win_s.find("guard_overwrite")
|
|
530
|
+
< win_s.find("cg._write_node(nid,")
|
|
531
|
+
and "actor=actor" in _src_window(src_s, "def calibrate(",
|
|
532
|
+
"\ndef sweep("),
|
|
533
|
+
"win_len=%d" % len(win_s))
|
|
534
|
+
win_m = _src_window(src_m, "prev_entry = (self.index.get(\"nodes\") or {})",
|
|
535
|
+
"os.remove(_old_real)")
|
|
536
|
+
check("5d MdCG.add 既有节点跨层覆写 = 搬迁语义:源层 require_layer + "
|
|
537
|
+
"guard_move(对齐 _move_layer 的源/目标双闸口径)",
|
|
538
|
+
"prev_entry.get(\"layer\")" in win_m
|
|
539
|
+
and "protect.require_layer(self, node_id, layer=_prev_layer,"
|
|
540
|
+
in src_m
|
|
541
|
+
and "protect.guard_move(self, node_id, layer, override=override,"
|
|
542
|
+
in src_m
|
|
543
|
+
and "require_layer" in win_m and "guard_move" in win_m,
|
|
544
|
+
"win_len=%d" % len(win_m))
|
|
545
|
+
check("5e 实弹断言数 ≥ %d(防 spawn/扫描面失效假绿)" % LIVE_FLOOR,
|
|
546
|
+
LIVE >= LIVE_FLOOR, "LIVE=%d" % LIVE)
|
|
547
|
+
|
|
548
|
+
print("\n=== N214/N215/N221/N222 write-face gates: %d passed, %d failed "
|
|
549
|
+
"(live=%d) ===" % (PASS, FAIL, LIVE))
|
|
550
|
+
if FAILS:
|
|
551
|
+
print("失败项:%s" % ";".join(FAILS))
|
|
552
|
+
return 1 if FAIL else 0
|
|
553
|
+
|
|
554
|
+
|
|
555
|
+
if __name__ == "__main__":
|
|
556
|
+
_code = 0
|
|
557
|
+
try:
|
|
558
|
+
_code = main()
|
|
559
|
+
finally:
|
|
560
|
+
_close_all()
|
|
561
|
+
if not _cleanup(_TMP):
|
|
562
|
+
print("WARN: 临时根残留未清(%s)——请人工清理" % _TMP)
|
|
563
|
+
sys.exit(_code)
|