@furongjun1999/dsh-memory 0.5.0 → 0.6.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 +572 -465
- package/docs/README.md +1 -0
- package/docs/eval/DSH/346/227/245/345/277/227/347/264/242/345/274/225v2_/345/217/202/350/200/203dsh-TUI_v1.0.md +248 -0
- package/docs/eval/DSH/346/227/245/345/277/227/347/264/242/345/274/225/346/225/210/346/236/234/351/252/214/350/257/201_v1.0.md +209 -0
- package/docs/eval/DSH/347/253/257/347/274/272/351/231/267/344/270/223/351/241/271_v1.0.md +254 -0
- package/docs/eval/P1b2_/350/257/273/351/235/242/344/273/243/351/231/205/344/277/256/345/244/215_v1.0.md +124 -0
- package/docs/eval/bench_lingshu_self/bench_self.py +140 -0
- package/docs/eval/bench_lingshu_self/self_bench_result.json +404 -0
- package/docs/eval/bench_lingshu_self//347/201/265/346/236/242/350/207/252/345/272/223/347/253/257/345/210/260/347/253/257/346/243/200/347/264/242/345/256/236/346/265/213_v1.0.md +38 -0
- package/docs/eval//344/270/215/345/217/257/351/235/240/346/200/247/350/220/275/345/234/260_P0_v1.0.md +185 -0
- package/docs/eval//345/217/221/345/270/20306_/344/270/200/351/224/256/351/205/215/347/275/256/344/270/216DSH0172_v1.0.md +171 -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 +422 -0
- package/docs/eval//347/253/257/345/210/260/347/253/257LoCoMoQA/345/220/214/345/217/243/345/276/204/345/257/271/347/205/247_v1.0.md +100 -0
- package/docs/eval//347/253/257/345/210/260/347/253/257/345/271/262/346/211/260/346/261/240/350/257/204/346/265/213_/347/241/256/345/256/232/346/200/247/350/243/201/345/206/263vsLLM_judge_v1.1.md +197 -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_v1.md +156 -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_v10.md +210 -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_v11.md +227 -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_v12.md +203 -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_v13.md +233 -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_v14.md +191 -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_v15.md +213 -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_v16.md +214 -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_v17.md +199 -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_v18.md +183 -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_v19.md +207 -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_v2.md +156 -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_v3.md +152 -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_v4.md +128 -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_v5.md +114 -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_v6.md +192 -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_v7.md +187 -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_v8.md +207 -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_v9.md +203 -0
- package/docs/hive//345/256/211/345/205/250/345/256/241/350/256/241/345/256/236/351/224/232_v0.1.md +202 -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 +19 -3
- package/docs/images/lingshu-moonlight-covenant-poster-preview.jpg +0 -0
- package/docs/images/lingshu-moonlight-covenant-poster.png +0 -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/theory//344/270/215/345/217/257/351/235/240/345/256/232/347/220/206/344/270/216/345/244/261/346/225/210/344/274/230/345/205/210/346/241/206/346/236/266_v0.3.md +365 -0
- package/docs//344/270/215/345/217/257/351/235/240/346/200/247/347/220/206/350/256/272_v0.1.md +343 -0
- package/dsh/cordis-patch-profile-web.example.yml +35 -0
- package/lib/bridge.d.ts +9 -0
- package/lib/bridge.js +35 -0
- package/lib/cli.d.ts +3 -0
- package/lib/cli.js +66 -0
- package/lib/index.js +7 -1
- package/lib/init.d.ts +88 -0
- package/lib/init.js +287 -0
- package/lib/lib/roleplay_web.js +530 -443
- package/lib/lib/token_store.d.ts +7 -1
- package/lib/lib/token_store.js +12 -3
- package/md_cg/audit.py +12 -1
- package/md_cg/backfill.py +16 -15
- package/md_cg/backfill_bucket_zh.py +35 -0
- package/md_cg/bench_e2e_judge.py +532 -0
- package/md_cg/bench_e2e_locomo_qa.py +368 -0
- package/md_cg/bench_e2e_qa.py +256 -0
- package/md_cg/branches.py +18 -2
- package/md_cg/ccgc.py +3 -2
- package/md_cg/chain.py +19 -4
- package/md_cg/consolidate.py +7 -6
- package/md_cg/crosscheck.py +4 -3
- package/md_cg/crypto.py +439 -437
- package/md_cg/datapath.py +395 -335
- package/md_cg/evidence.py +8 -3
- package/md_cg/export.py +3 -1
- package/md_cg/forgetting.py +2 -2
- package/md_cg/fsutil.py +48 -0
- package/md_cg/hotcache.py +255 -238
- package/md_cg/interop.py +161 -22
- package/md_cg/judgment_manifest.py +177 -0
- package/md_cg/links.py +655 -622
- package/md_cg/mcp_server.py +282 -31
- package/md_cg/mdcg.py +702 -126
- package/md_cg/mdcos.py +454 -68
- package/md_cg/mreview/govern.py +5 -4
- package/md_cg/postings.py +4 -2
- package/md_cg/readcache.py +76 -18
- package/md_cg/reconcile.py +228 -0
- package/md_cg/review_cli.py +215 -0
- package/md_cg/routing.py +28 -0
- package/md_cg/run_tests.py +211 -0
- package/md_cg/scrub.py +862 -852
- package/md_cg/security.py +385 -275
- package/md_cg/selfreport.py +3 -2
- package/md_cg/signer.py +565 -562
- package/md_cg/sources.py +3 -2
- package/md_cg/stg.py +6 -0
- package/md_cg/sustain.py +1168 -1138
- package/md_cg/test_access_hints.py +147 -0
- package/md_cg/test_branch_discard_tombstone.py +136 -0
- package/md_cg/test_branches.py +259 -249
- package/md_cg/test_chain_read_isolate.py +168 -0
- package/md_cg/test_datapath_device_name.py +203 -0
- package/md_cg/test_emit_negtail_cache.py +156 -0
- package/md_cg/test_en_pipeline.py +186 -166
- package/md_cg/test_govern_directread.py +421 -0
- package/md_cg/test_i32_hotcache_env_key.py +218 -0
- package/md_cg/test_identity_attribution.py +228 -147
- package/md_cg/test_index_crossprocess_reload.py +301 -0
- package/md_cg/test_index_durability.py +238 -224
- package/md_cg/test_interop.py +4 -2
- package/md_cg/test_interop_judgment.py +228 -0
- package/md_cg/test_issue39_utf8_stdio.py +273 -0
- package/md_cg/test_links_concurrent_write.py +188 -0
- package/md_cg/test_merge_upsert.py +168 -0
- package/md_cg/test_n123_derive_expiry_chain.py +205 -0
- package/md_cg/test_n130_verify_falsified_protect.py +185 -0
- package/md_cg/test_n131_merge_gate.py +205 -0
- package/md_cg/test_none_id_write_guard.py +165 -0
- package/md_cg/test_p1x_ref_root.py +160 -0
- package/md_cg/test_p27_docindex.py +774 -765
- package/md_cg/test_p2_mcp.py +3 -0
- package/md_cg/test_p32_backfill.py +304 -298
- package/md_cg/test_p39_verify_flow.py +90 -50
- package/md_cg/test_p47_session_view.py +60 -25
- package/md_cg/test_propose_tail_index.py +157 -0
- package/md_cg/test_read_scope_b27.py +277 -0
- package/md_cg/test_readcache_default_on.py +168 -0
- package/md_cg/test_readcache_precise_inval.py +270 -0
- package/md_cg/test_readcache_prodpath.py +55 -7
- package/md_cg/test_reconcile_v0.py +294 -0
- package/md_cg/test_retr_s1.py +6 -2
- package/md_cg/test_retr_s1b.py +67 -0
- package/md_cg/test_retr_s7.py +8 -0
- package/md_cg/test_retr_s9_entity_ctx.py +181 -175
- package/md_cg/test_retr_score_once.py +208 -0
- package/md_cg/test_review_cli_attribution.py +177 -0
- package/md_cg/test_review_cli_visibility.py +235 -0
- package/md_cg/test_review_onepass.py +170 -0
- package/md_cg/test_rrf_graph_seed_cache.py +154 -0
- package/md_cg/test_security_audit.py +155 -0
- package/md_cg/test_security_audit_b26.py +161 -0
- package/md_cg/test_security_audit_v21.py +250 -0
- package/md_cg/test_semantic_canonical.py +255 -241
- package/md_cg/test_session_isolation.py +168 -0
- package/md_cg/test_snapshot_autoclose.py +187 -0
- package/md_cg/test_tail_watermark_race.py +208 -0
- package/md_cg/test_tenant_env_override_warn.py +139 -0
- package/md_cg/test_tenant_registry_corrupt_warn.py +151 -0
- package/md_cg/test_v14_fixes.py +415 -397
- package/md_cg/test_verify_dirty_reconcile.py +157 -0
- package/md_cg/theory.py +276 -273
- package/md_cg/tokens.py +734 -677
- package/md_cg/units.py +3 -2
- package/md_cg/vision_evidence.py +4 -3
- package/md_cg/whitebox_kb/wisdom/code_solidified.json +6195 -6195
- package/md_cg/whitebox_kb/wisdom/multilang_ir.py +128 -124
- package/md_cg/writepipe.py +554 -550
- package/package.json +11 -2
- package/src/bridge.ts +434 -401
- package/src/cli.ts +65 -0
- package/src/index.ts +526 -518
- package/src/init.ts +347 -0
- package/src/lib/roleplay_web.ts +1019 -932
- package/src/lib/token_store.ts +202 -192
|
@@ -1,147 +1,228 @@
|
|
|
1
|
-
# -*- coding: utf-8 -*-
|
|
2
|
-
"""test_identity_attribution · 写入归属(writer/session/harness
|
|
3
|
-
|
|
4
|
-
背景:多会话共用一个 root 时,读取记忆无法区分「本会话写的」与「其他会话写的」。
|
|
5
|
-
修复:写入自动归属(MdCGSecure 写路径注入 writer/session/harness)+
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
import
|
|
13
|
-
import
|
|
14
|
-
import
|
|
15
|
-
import
|
|
16
|
-
import
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
#
|
|
22
|
-
#
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
from md_cg.
|
|
28
|
-
from md_cg.
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
cg2
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
#
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
check("
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
#
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
#
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""test_identity_attribution · 写入归属(writer/session/harness)与会话可见性
|
|
3
|
+
|
|
4
|
+
背景:多会话共用一个 root 时,读取记忆无法区分「本会话写的」与「其他会话写的」。
|
|
5
|
+
修复:写入自动归属(MdCGSecure 写路径注入 writer/session/harness)+ 会话可见性
|
|
6
|
+
**分档判定**(issue #35 定稿:public/internal = 跨会话共享档;private/secret =
|
|
7
|
+
绑定档,仅归属会话可见,设计者豁免;查询侧 session 参数只作归因过滤、不构成
|
|
8
|
+
授权豁免)+ 审计/提案入队补 session + MDCG_SESSION env(部署侧固定会话归属)。
|
|
9
|
+
边界:归属是归因维度不参与授权;MCP 面不透传写入归属入参(客户端不得伪造),
|
|
10
|
+
库层调用方可显式覆盖(setdefault);writer 语义=最后写入者。
|
|
11
|
+
"""
|
|
12
|
+
import glob
|
|
13
|
+
import json
|
|
14
|
+
import shutil
|
|
15
|
+
import sys
|
|
16
|
+
import tempfile
|
|
17
|
+
import os
|
|
18
|
+
|
|
19
|
+
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
|
20
|
+
|
|
21
|
+
# Windows cmd 默认 GBK 代码页:带圈数字 ⑪⑫⑬ 等不在 GBK 内,打印即
|
|
22
|
+
# UnicodeEncodeError,且崩在断言之后、报告之前 —— 同一测试「因环境而异」。
|
|
23
|
+
# 测试自带 UTF-8 兜底,不依赖调用方记得加 -X utf8(可复现性纪律)。
|
|
24
|
+
if hasattr(sys.stdout, "reconfigure"):
|
|
25
|
+
sys.stdout.reconfigure(encoding="utf-8", errors="replace")
|
|
26
|
+
|
|
27
|
+
from md_cg.mdcg import MdCG
|
|
28
|
+
from md_cg.mdcos import MdCGSecure
|
|
29
|
+
from md_cg.security import DEFAULT_SENSITIVITY, Principal
|
|
30
|
+
|
|
31
|
+
_ok = 0
|
|
32
|
+
_fail = []
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def check(name, cond, detail=""):
|
|
36
|
+
global _ok
|
|
37
|
+
if cond:
|
|
38
|
+
_ok += 1
|
|
39
|
+
print("[PASS] " + name)
|
|
40
|
+
else:
|
|
41
|
+
_fail.append(name)
|
|
42
|
+
print("[FAIL] %s · %s" % (name, str(detail)[:240]))
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def _p(actor, session):
|
|
46
|
+
return Principal(actor=actor, clearance=DEFAULT_SENSITIVITY,
|
|
47
|
+
can_write=True, can_admin=True, role="designer",
|
|
48
|
+
session=session, harness="test-harness")
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def _last_jsonl(pattern):
|
|
52
|
+
rec = None
|
|
53
|
+
for path in glob.glob(pattern, recursive=True):
|
|
54
|
+
with open(path, encoding="utf-8", errors="replace") as f:
|
|
55
|
+
for line in f:
|
|
56
|
+
if line.strip():
|
|
57
|
+
rec = json.loads(line)
|
|
58
|
+
return rec
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def _row(r):
|
|
62
|
+
"""search 原生返回 (node, score, qual, prov) tuple;dict 则原样。"""
|
|
63
|
+
return r if isinstance(r, dict) else (r[0] if r else {})
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
root = tempfile.mkdtemp(prefix="mdcg_attr_")
|
|
67
|
+
try:
|
|
68
|
+
cg = MdCGSecure(root, principal=_p("alice", "sess_A"))
|
|
69
|
+
|
|
70
|
+
# ① 写入自动归属
|
|
71
|
+
nid1 = cg.add("n1", "甲会话写的知识条目", layer="knowledge")
|
|
72
|
+
fm1 = cg.get(nid1)["frontmatter"]
|
|
73
|
+
check("①写入自动归属 writer/session",
|
|
74
|
+
fm1.get("writer") == "alice" and fm1.get("session") == "sess_A", fm1)
|
|
75
|
+
check("①b harness 归属落盘", fm1.get("harness") == "test-harness", fm1)
|
|
76
|
+
|
|
77
|
+
# ② 索引同步(_stage 透传)
|
|
78
|
+
e1 = cg.index["nodes"].get(nid1) or {}
|
|
79
|
+
check("②索引带归属", e1.get("session") == "sess_A", e1)
|
|
80
|
+
|
|
81
|
+
# ③ 显式覆盖(库层调用方可传;MCP 面不透传)
|
|
82
|
+
nid2 = cg.add("n2", "显式会话标签条目", layer="knowledge", session="sess_custom")
|
|
83
|
+
fm2 = cg.get(nid2)["frontmatter"]
|
|
84
|
+
check("③显式覆盖尊重调用方", fm2.get("session") == "sess_custom", fm2)
|
|
85
|
+
|
|
86
|
+
# ④ 更新刷新为最后写入者(另一身份写同 id)
|
|
87
|
+
cg2 = MdCGSecure(root, principal=_p("bob", "sess_B"))
|
|
88
|
+
cg2.add("n1", "乙会话更新了这条", layer="knowledge")
|
|
89
|
+
fm1b = cg.get("n1")["frontmatter"]
|
|
90
|
+
check("④更新刷新 writer/session",
|
|
91
|
+
fm1b.get("writer") == "bob" and fm1b.get("session") == "sess_B", fm1b)
|
|
92
|
+
|
|
93
|
+
# ⑤ 检索透出归属(node.frontmatter 零改动自动生效)
|
|
94
|
+
res, _meta = cg2.search("乙会话更新")
|
|
95
|
+
hit = next((_row(r) for r in res if _row(r).get("id") == "n1"), None)
|
|
96
|
+
check("⑤检索透出归属",
|
|
97
|
+
bool(hit) and (hit.get("frontmatter") or {}).get("session") == "sess_B",
|
|
98
|
+
[_row(r).get("id") for r in res])
|
|
99
|
+
|
|
100
|
+
# ⑥⑦ 会话语义(issue #35 定稿:**分档**判定,查询参数不构成豁免)
|
|
101
|
+
# · 共享档(public/internal)跨会话可见——n1/n2 都是 internal,故传任何
|
|
102
|
+
# session(他人会话 / "*" / 缺省)都应看得见;旧断言「按 session 过滤掉」
|
|
103
|
+
# 编码的是 #35 之前的契约(同口径见 test_p47_session_view 的 D1a/D2/D3)。
|
|
104
|
+
# · 绑定档(private/secret)仅归属会话——由 principal.session 判定,查询侧
|
|
105
|
+
# 自报的 session 不越权;can_admin(设计者)整体判断豁免。
|
|
106
|
+
res_b, _ = cg2.search("知识", session="sess_B")
|
|
107
|
+
ids_b = {_row(r).get("id") for r in res_b}
|
|
108
|
+
check("⑥a 共享档跨会话可见:显式 session 不缩小可见性",
|
|
109
|
+
"n1" in ids_b and "n2" in ids_b, ids_b)
|
|
110
|
+
|
|
111
|
+
# ⑥b~⑦ 绑定档对照(private 节点归 sess_B):
|
|
112
|
+
# · 跨身份(另一 actor/会话)不可见;
|
|
113
|
+
# · **同身份换会话**:非设计者不可见(绑定档生效);
|
|
114
|
+
# · **同身份换会话 + can_admin**:豁免可见(设计者整体判断)。
|
|
115
|
+
# 注:豁免的是「会话绑定判定」,不越过加密身份——private 内容按
|
|
116
|
+
# (tenant, actor) 派生 DEK,跨 actor 即便判定可见也解不开(get 无正文),
|
|
117
|
+
# 故可见性对照必须在同一 actor 内做。
|
|
118
|
+
w_priv = MdCGSecure(root, principal=Principal(
|
|
119
|
+
actor="bob", clearance="private", can_write=True, can_admin=False,
|
|
120
|
+
role="recorder", session="sess_B"))
|
|
121
|
+
try:
|
|
122
|
+
w_priv.add("n_priv_b", "乙会话私有知识条目", layer="knowledge",
|
|
123
|
+
sensitivity="private")
|
|
124
|
+
w_priv.flush()
|
|
125
|
+
finally:
|
|
126
|
+
w_priv.close()
|
|
127
|
+
carol = MdCGSecure(root, principal=Principal(
|
|
128
|
+
actor="carol", clearance="private", can_write=False, can_admin=False,
|
|
129
|
+
role="recorder", session="sess_C"))
|
|
130
|
+
try:
|
|
131
|
+
res_c, _ = carol.search("私有知识", session="sess_B") # 自报他人会话
|
|
132
|
+
ids_c = {_row(r).get("id") for r in res_c}
|
|
133
|
+
check("⑥b 绑定档按身份判定:自报他人会话不越权",
|
|
134
|
+
"n_priv_b" not in ids_c, ids_c)
|
|
135
|
+
finally:
|
|
136
|
+
carol.close()
|
|
137
|
+
bob_other = MdCGSecure(root, principal=Principal(
|
|
138
|
+
actor="bob", clearance="private", can_write=False, can_admin=False,
|
|
139
|
+
role="recorder", session="sess_OTHER"))
|
|
140
|
+
try:
|
|
141
|
+
res_o, _ = bob_other.search("私有知识")
|
|
142
|
+
check("⑥c 同一身份换会话:非设计者仍受绑定档约束",
|
|
143
|
+
"n_priv_b" not in {_row(r).get("id") for r in res_o},
|
|
144
|
+
[_row(r).get("id") for r in res_o])
|
|
145
|
+
finally:
|
|
146
|
+
bob_other.close()
|
|
147
|
+
bob_admin = MdCGSecure(root, principal=Principal(
|
|
148
|
+
actor="bob", clearance="private", can_write=False, can_admin=True,
|
|
149
|
+
role="designer", session="sess_OTHER"))
|
|
150
|
+
try:
|
|
151
|
+
res_d, _ = bob_admin.search("私有知识", session="sess_C")
|
|
152
|
+
check("⑥d 同一身份 + 设计者(can_admin):豁免绑定档",
|
|
153
|
+
"n_priv_b" in {_row(r).get("id") for r in res_d},
|
|
154
|
+
[_row(r).get("id") for r in res_d])
|
|
155
|
+
|
|
156
|
+
# ⑦ recall(RRF 主链)同口径:绑定档判定不因查询 session 变化
|
|
157
|
+
pack = bob_admin.recall("私有知识", session="sess_custom")
|
|
158
|
+
hits = pack.get("pack") if isinstance(pack, dict) else None
|
|
159
|
+
hid = {_row(h).get("id") for h in (hits or [])}
|
|
160
|
+
check("⑦recall 同口径:设计者可见绑定档(查询 session 不改判定)",
|
|
161
|
+
"n_priv_b" in hid,
|
|
162
|
+
list(hid) or (list(pack.keys()) if isinstance(pack, dict)
|
|
163
|
+
else type(pack)))
|
|
164
|
+
finally:
|
|
165
|
+
bob_admin.close()
|
|
166
|
+
|
|
167
|
+
# ⑧ 审计带 session
|
|
168
|
+
last_audit = _last_jsonl(os.path.join(root, "**", "*audit*.jsonl"))
|
|
169
|
+
check("⑧审计带 session",
|
|
170
|
+
bool(last_audit) and last_audit.get("session") == "sess_B", last_audit)
|
|
171
|
+
|
|
172
|
+
# ⑨ propose 入队带 session
|
|
173
|
+
cg2.propose("n_prop", "提案内容", layer="knowledge")
|
|
174
|
+
rec_inbox = _last_jsonl(os.path.join(root, "**", "*inbox*.jsonl"))
|
|
175
|
+
check("⑨提案入队带 session",
|
|
176
|
+
bool(rec_inbox) and rec_inbox.get("session") == "sess_B", rec_inbox)
|
|
177
|
+
|
|
178
|
+
# ⑩ 旧节点兼容(基类写入、无归属字段)
|
|
179
|
+
# · 共享档:跨会话可见(#35 定稿);
|
|
180
|
+
# · 绑定档但无归属:无法证明归属 → fail-closed,仅设计者(can_admin)可见。
|
|
181
|
+
cg0 = MdCG(root)
|
|
182
|
+
cg0.add("legacy", "旧库无归属节点的知识", layer="knowledge")
|
|
183
|
+
cg0.add("legacy_priv", "旧库无归属的私有知识条目", layer="knowledge",
|
|
184
|
+
sensitivity="private")
|
|
185
|
+
cgs = MdCGSecure(root, principal=_p("carol", "sess_C"))
|
|
186
|
+
res_all, _m = cgs.search("旧库无归属")
|
|
187
|
+
check("⑩旧节点默认检索不受影响",
|
|
188
|
+
any(_row(r).get("id") == "legacy" for r in res_all),
|
|
189
|
+
[_row(r).get("id") for r in res_all])
|
|
190
|
+
res_f, _ = cgs.search("旧库无归属", session="sess_C")
|
|
191
|
+
check("⑩b 旧节点(无归属·共享档)跨会话可见(不炸)",
|
|
192
|
+
any(_row(r).get("id") == "legacy" for r in res_f),
|
|
193
|
+
[_row(r).get("id") for r in res_f])
|
|
194
|
+
# ⑩c/⑩d 绑定档无归属的 fail-closed(同 clearance 的非设计者不可读 / 设计者豁免)
|
|
195
|
+
peak = MdCGSecure(root, principal=Principal(
|
|
196
|
+
actor="erin", clearance="private", can_write=False, can_admin=False,
|
|
197
|
+
role="recorder", session="sess_E"))
|
|
198
|
+
try:
|
|
199
|
+
res_p, _ = peak.search("旧库无归属")
|
|
200
|
+
ids_p = [_row(r).get("id") for r in res_p]
|
|
201
|
+
check("⑩c 绑定档无归属 fail-closed(同 clearance 非设计者不可读)",
|
|
202
|
+
"legacy_priv" not in ids_p and "legacy" in ids_p, ids_p)
|
|
203
|
+
finally:
|
|
204
|
+
peak.close()
|
|
205
|
+
lim = MdCGSecure(root, principal=Principal(
|
|
206
|
+
actor="dave2", clearance="private", can_write=True, can_admin=True,
|
|
207
|
+
role="designer", session="sess_D"))
|
|
208
|
+
try:
|
|
209
|
+
res_l, _ = lim.search("旧库无归属")
|
|
210
|
+
check("⑩d 设计者可见无归属绑定档(整体判断豁免)",
|
|
211
|
+
"legacy_priv" in [_row(r).get("id") for r in res_l],
|
|
212
|
+
[_row(r).get("id") for r in res_l])
|
|
213
|
+
finally:
|
|
214
|
+
lim.close()
|
|
215
|
+
|
|
216
|
+
# ⑪⑫ 负记忆归属
|
|
217
|
+
rn = cg2.add_rejected("假设X", "已被证伪")
|
|
218
|
+
fm_r = cg2.get(rn)["frontmatter"]
|
|
219
|
+
check("⑪负记忆归属", fm_r.get("writer") == "bob"
|
|
220
|
+
and fm_r.get("session") == "sess_B", fm_r)
|
|
221
|
+
ru = cg2.add_unresolved("问题Y", "已有线索若干")
|
|
222
|
+
fm_u = cg2.get(ru)["frontmatter"]
|
|
223
|
+
check("⑫未解清单归属", fm_u.get("session") == "sess_B", fm_u)
|
|
224
|
+
|
|
225
|
+
print("\n通过 %d / 失败 %d" % (_ok, len(_fail)))
|
|
226
|
+
sys.exit(1 if _fail else 0)
|
|
227
|
+
finally:
|
|
228
|
+
shutil.rmtree(root, ignore_errors=True)
|