@furongjun1999/dsh-memory 0.7.1 → 0.7.2
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 +3 -2
- package/codebuddy/CODEBUDDY.md +5 -5
- package/docs/eval//345/217/221/345/270/20309_/346/243/200/347/264/242/351/235/242/344/270/211/346/211/271/346/224/266/345/217/243_v1.0.md +1 -1
- package/docs/eval//345/217/221/345/270/20310_/350/257/273/351/235/242/346/215/237/345/235/217UTF8/345/256/266/346/227/217/346/224/266/345/217/243_v1.0.md +71 -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 +4 -4
- package/md_cg/bench6_arms.py +10 -6
- package/md_cg/census.py +2 -1
- package/md_cg/mdcg.py +83 -25
- package/md_cg/mdcos.py +9 -3
- package/md_cg/mreview/locate.py +1 -1
- package/md_cg/rollback.py +12 -3
- package/md_cg/routing.py +10 -1
- package/md_cg/test_corrupt_utf8_read_surfaces.py +451 -0
- package/md_cg/test_generation_guard.py +15 -9
- package/md_cg/test_health_corrupt_utf8.py +37 -0
- package/md_cg/test_index_crossprocess_reload.py +39 -24
- package/md_cg/test_issue53_cipher_health_hash.py +385 -0
- package/package.json +1 -1
- package/skills/plugin.json +1 -1
- package/zcode/AGENTS.md +5 -5
|
@@ -0,0 +1,385 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
# 功能名:issue #53 三缺陷守卫——密文域标签(A) / 桶计数单点(B) / 写盘口内容指纹同步(C)
|
|
3
|
+
# 生效条件:md_cg/routing.py 的 `classify_text` 密文拦截与「单桶」文案、md_cg/mdcg.py 的
|
|
4
|
+
# `backfill_big_domain` 密文计数分支 / `_set_index_entry`+`_bucket_shift` 桶计数
|
|
5
|
+
# 单点 / `_write_node` 内容指纹同步 / `health` 的 bucket_scope 自述在位时成立;
|
|
6
|
+
# 沙箱条件:所有库根一律 tempfile.mkdtemp,绝不触在役库与在役服务;
|
|
7
|
+
# 本文件只读源码,变异时只写目标文件且在 finally 里按原始字节复原。
|
|
8
|
+
# 子功能:钉消费方看得见的目标语义(报告 issue #53 三条逐条对应):
|
|
9
|
+
# A 密文不得被当可分类正文:`classify_text(密文)` 恒 None(含 GDP 撞词形态);
|
|
10
|
+
# backfill 不写密文节点标签、ciphertext 计数单列且各计数为互斥分区;明文对照不误伤
|
|
11
|
+
# B 桶计数是单点事务:同桶覆写零动作、换桶撤旧加新、删除归零删键、读数自洽
|
|
12
|
+
# (nodes==带桶条目数)且带 bucket_scope 覆盖面自述
|
|
13
|
+
# C 重封(密文重写)必须同步索引内容指纹:update_tags/append_edge 后 entry.content_hash
|
|
14
|
+
# 仍落在盘面双形态内、reconcile 零假漂移、跨 close/重开仍零假漂移
|
|
15
|
+
# 执行:python -X utf8 -m md_cg.test_issue53_cipher_health_hash
|
|
16
|
+
# python -X utf8 -m md_cg.test_issue53_cipher_health_hash --mutation=all
|
|
17
|
+
# 验证方式:本文件自跑(自带断言计数、live 计数与退出码 rc=0/1);定点变异自证见
|
|
18
|
+
# --mutation(把每处修复逐点抽回改动前语义 → 本守卫必红且红项名可贴出;
|
|
19
|
+
# 变异在 finally 中按原始字节复原并回读核对逐字节相等 → 无残余)。
|
|
20
|
+
# 不适用条件:情感交互|闲聊|纯查询无改动;routing 阈值面与既有区块守卫(test_m3_h9 /
|
|
21
|
+
# test_retr_s1)的判据面不在本席改动面内。
|
|
22
|
+
from __future__ import annotations
|
|
23
|
+
|
|
24
|
+
import os
|
|
25
|
+
import shutil
|
|
26
|
+
import subprocess
|
|
27
|
+
import sys
|
|
28
|
+
import tempfile
|
|
29
|
+
|
|
30
|
+
# ── 隔离前置:在任何 md_cg 子模块 import 之前把根指到临时目录 ──
|
|
31
|
+
_TMP = tempfile.mkdtemp(prefix="i53_")
|
|
32
|
+
os.environ["MDCG_AUX_ROOT"] = os.path.join(_TMP, "auxroot")
|
|
33
|
+
os.environ["MDCG_ROOT"] = os.path.join(_TMP, "cgroot")
|
|
34
|
+
for _k in ("MDCG_TOKEN", "MDCG_TENANT", "MDCG_CLEARANCE", "MDCG_SESSION",
|
|
35
|
+
"DSH_SESSION_ID", "MDCG_LEGACY_ENV_AUTH", "MDCG_LEGACY_ENV_ADMIN",
|
|
36
|
+
"MDCG_CAN_ADMIN", "MDCG_CAN_WRITE", "MDCG_STATE_ROOT",
|
|
37
|
+
"MDCG_DATA_ROOT", "MDCG_TENANT_REGISTRY", "MDCG_VERIFIER_MODULES"):
|
|
38
|
+
os.environ.pop(_k, None)
|
|
39
|
+
os.environ["MDCG_RETRIEVAL_PIPELINE"] = "1" # A/B 组前置(回填/域标签面)
|
|
40
|
+
|
|
41
|
+
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
|
42
|
+
|
|
43
|
+
from md_cg import nodefile, routing # noqa: E402
|
|
44
|
+
from md_cg.mdcg import MdCG # noqa: E402
|
|
45
|
+
from md_cg.mdcos import MdCGOS, MdCGSecure # noqa: E402
|
|
46
|
+
from md_cg.security import Principal # noqa: E402
|
|
47
|
+
|
|
48
|
+
try:
|
|
49
|
+
sys.stdout.reconfigure(encoding="utf-8", errors="replace")
|
|
50
|
+
except Exception: # noqa: BLE001
|
|
51
|
+
pass
|
|
52
|
+
|
|
53
|
+
_REPO = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
|
54
|
+
|
|
55
|
+
PASS = FAIL = LIVE = 0
|
|
56
|
+
LIVE_FLOOR = 10
|
|
57
|
+
FAILS = []
|
|
58
|
+
_OPEN = []
|
|
59
|
+
|
|
60
|
+
TEST_KEY = bytes(range(32)) # 合成测试钥;绝不触碰真实主密钥面
|
|
61
|
+
|
|
62
|
+
#: GDP 三连是唯一实测命中形态(BIG_DOMAINS 里唯一的拉丁词是「经济」的 GDP)
|
|
63
|
+
CIPHER_GDP = "<!-- mdcg-enc:v1:QUJDREVGGDPQUJDREVG -->"
|
|
64
|
+
CIPHER_PLAIN = "<!-- mdcg-enc:v1:QUJDREVGQUJDREVG -->"
|
|
65
|
+
BODY = ("# 功能名:issue53 探针\n"
|
|
66
|
+
"# 生效条件:守卫临时库\n"
|
|
67
|
+
"# 子功能:素材节点\n"
|
|
68
|
+
"# 执行:cg.add\n"
|
|
69
|
+
"# 验证方式:本守卫断言\n"
|
|
70
|
+
"# 不适用条件:无\n"
|
|
71
|
+
"守卫探针正文。\n")
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def check(name, cond, detail="", live=False):
|
|
75
|
+
global PASS, FAIL, LIVE
|
|
76
|
+
if live:
|
|
77
|
+
LIVE += 1
|
|
78
|
+
if cond:
|
|
79
|
+
PASS += 1
|
|
80
|
+
print(" [PASS] %s" % name)
|
|
81
|
+
else:
|
|
82
|
+
FAIL += 1
|
|
83
|
+
FAILS.append(name)
|
|
84
|
+
print(" [FAIL] %s · %s" % (name, detail))
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def _mk(rel, cls=MdCGOS, **kw):
|
|
88
|
+
cg = cls(os.path.join(_TMP, rel), autoflush=0, **kw)
|
|
89
|
+
_OPEN.append(cg)
|
|
90
|
+
return cg
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def _put_raw(lib, nid, layer, content, sens=None):
|
|
94
|
+
"""手工落一个节点文件(可造密文形态正文——基类写入不做封装)。"""
|
|
95
|
+
fm = {"id": nid, "layer": layer, "modality": "text", "importance": 0.5,
|
|
96
|
+
"confidence": 0.6, "condition_space": {"time_window": [1.0, 9e9]},
|
|
97
|
+
"tags": [], "created_at": 1.0, "access_count": 0, "last_access": 0,
|
|
98
|
+
"edges": [], "verification_basis": None,
|
|
99
|
+
"non_applicable_conditions": [], "evidence_count": 0,
|
|
100
|
+
"positive_evidence": 0, "negative_evidence": 0}
|
|
101
|
+
if sens:
|
|
102
|
+
fm["sensitivity"] = sens
|
|
103
|
+
p = os.path.join(lib, layer, nid + ".md")
|
|
104
|
+
os.makedirs(os.path.dirname(p), exist_ok=True)
|
|
105
|
+
with open(p, "w", encoding="utf-8") as f:
|
|
106
|
+
f.write(nodefile.dumps(fm, content))
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def _disk_match(cg, nid):
|
|
110
|
+
"""entry.content_hash 是否落在盘面双形态接受集(reconcile 同口径)。"""
|
|
111
|
+
e = cg.index["nodes"][nid]
|
|
112
|
+
p = os.path.join(cg.root, e["path"])
|
|
113
|
+
with open(p, encoding="utf-8") as f:
|
|
114
|
+
_fm, content = nodefile.loads(f.read())
|
|
115
|
+
raw = nodefile.content_hash(content or "")
|
|
116
|
+
norm = nodefile.content_hash((content or "").rstrip("\n"))
|
|
117
|
+
return e.get("content_hash") in (raw, norm), e.get("content_hash"), raw, norm
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def _close_all():
|
|
121
|
+
for cg in _OPEN:
|
|
122
|
+
try:
|
|
123
|
+
cg.close()
|
|
124
|
+
except Exception: # noqa: BLE001
|
|
125
|
+
pass
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def _cleanup():
|
|
129
|
+
import gc
|
|
130
|
+
import time
|
|
131
|
+
for _ in range(6):
|
|
132
|
+
shutil.rmtree(_TMP, ignore_errors=True)
|
|
133
|
+
if not os.path.exists(_TMP):
|
|
134
|
+
return
|
|
135
|
+
gc.collect()
|
|
136
|
+
time.sleep(0.25)
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
# ───────────────────────── A:密文域标签 ─────────────────────────
|
|
140
|
+
|
|
141
|
+
def group_a():
|
|
142
|
+
print("\n【A】issue #53-A:密文不得被当可分类正文")
|
|
143
|
+
check("A1 classify_text 对密文(含 GDP 撞词形态)恒 None",
|
|
144
|
+
routing.classify_text(CIPHER_GDP) is None
|
|
145
|
+
and routing.classify_text(CIPHER_PLAIN) is None,
|
|
146
|
+
"%r / %r" % (routing.classify_text(CIPHER_GDP),
|
|
147
|
+
routing.classify_text(CIPHER_PLAIN)))
|
|
148
|
+
check("A2 明文对照不误伤:GDP 明文字面仍归「经济」",
|
|
149
|
+
routing.classify_text("GDP 与市场") == "经济",
|
|
150
|
+
repr(routing.classify_text("GDP 与市场")))
|
|
151
|
+
|
|
152
|
+
lib = os.path.join(_TMP, "lib_a")
|
|
153
|
+
os.makedirs(lib, exist_ok=True)
|
|
154
|
+
_put_raw(lib, "a_cip_gdp", "contextual", CIPHER_GDP, sens="private")
|
|
155
|
+
_put_raw(lib, "a_cip_ok", "contextual", CIPHER_PLAIN, sens="private")
|
|
156
|
+
_put_raw(lib, "a_plain", "contextual", "# 功能名:x\n计算机与算法内容\n")
|
|
157
|
+
cg = MdCG(lib) # 基类=backfill CLI 的同款打开方式
|
|
158
|
+
st = cg.backfill_big_domain()
|
|
159
|
+
check("A3 回填不给密文写假标签(修前:GDP 形态被判「经济」并落盘)",
|
|
160
|
+
st["written"] == 1
|
|
161
|
+
and (cg.get("a_cip_gdp")["frontmatter"] or {}).get("big_domain") is None
|
|
162
|
+
and (cg.get("a_cip_ok")["frontmatter"] or {}).get("big_domain") is None
|
|
163
|
+
and cg.get("a_plain")["frontmatter"].get("big_domain") == "计算机",
|
|
164
|
+
str(st), live=True)
|
|
165
|
+
check("A4 ciphertext 独立计数且各计数为互斥分区(和 == seen)",
|
|
166
|
+
st.get("ciphertext") == 2
|
|
167
|
+
and (st["written"] + st["already"] + st["no_signal"]
|
|
168
|
+
+ st["unreadable"] + st["ciphertext"] + st["index_synced"]
|
|
169
|
+
== st["seen"]),
|
|
170
|
+
str(st), live=True)
|
|
171
|
+
st2 = cg.backfill_big_domain()
|
|
172
|
+
check("A5 二次回填:密文仍走 ciphertext(不被 already 假确认、不重写)",
|
|
173
|
+
st2.get("ciphertext") == 2 and st2["already"] == 1
|
|
174
|
+
and st2["written"] == 0, str(st2))
|
|
175
|
+
cg.close()
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
# ───────────────────────── B:桶计数单点 ─────────────────────────
|
|
179
|
+
|
|
180
|
+
def group_b():
|
|
181
|
+
print("\n【B】issue #53-B:桶计数单点(覆写零虚高 / 换桶撤旧加新 / 归零删键)")
|
|
182
|
+
cg = _mk("lib_b")
|
|
183
|
+
cg.add("b1", BODY, layer="knowledge")
|
|
184
|
+
cg.flush()
|
|
185
|
+
check("B1a 首次写入计数 1(前置)",
|
|
186
|
+
dict(cg.index["buckets"]) == {"orphan": 1}, str(cg.index["buckets"]))
|
|
187
|
+
cg.add("b1", BODY + "覆写。\n", layer="knowledge")
|
|
188
|
+
check("B1b 同桶覆写不虚高(修前:{'orphan': 2})",
|
|
189
|
+
dict(cg.index["buckets"]) == {"orphan": 1}, str(cg.index["buckets"]),
|
|
190
|
+
live=True)
|
|
191
|
+
cg.flush()
|
|
192
|
+
cg.update_tags("b1", add=["t"])
|
|
193
|
+
check("B1c update_tags 不动桶计数(物理未动)",
|
|
194
|
+
dict(cg.index["buckets"]) == {"orphan": 1}, str(cg.index["buckets"]))
|
|
195
|
+
|
|
196
|
+
cg.add("b2", BODY, layer="knowledge", tags=["domain:甲"])
|
|
197
|
+
cg.flush()
|
|
198
|
+
b_old = routing.bucket_dir(routing.normalize_domain("甲"))
|
|
199
|
+
b_new = routing.bucket_dir(routing.normalize_domain("乙"))
|
|
200
|
+
has_old = b_old in cg.index["buckets"]
|
|
201
|
+
cg.add("b2", BODY + "换桶覆写。\n", layer="knowledge", tags=["domain:乙"])
|
|
202
|
+
check("B2 换桶覆写:旧桶撤、新桶加(净变化守恒)",
|
|
203
|
+
has_old and b_old not in cg.index["buckets"]
|
|
204
|
+
and cg.index["buckets"].get(b_new) == 1
|
|
205
|
+
and sum(cg.index["buckets"].values()) == 2,
|
|
206
|
+
"%s | old_in=%s" % (cg.index["buckets"], has_old), live=True)
|
|
207
|
+
cg.flush()
|
|
208
|
+
|
|
209
|
+
cg.forget("b2", reason="守卫归零删键探针")
|
|
210
|
+
check("B3 删除路径归零删键(counts 无 0 值残留、总数守恒)",
|
|
211
|
+
b_new not in cg.index["buckets"]
|
|
212
|
+
and sum(cg.index["buckets"].values()) == 1,
|
|
213
|
+
str(cg.index["buckets"]), live=True)
|
|
214
|
+
|
|
215
|
+
h = cg.health()
|
|
216
|
+
n_bucketed = sum(1 for e in list(cg.index["nodes"].values())
|
|
217
|
+
if e.get("bucket"))
|
|
218
|
+
check("B4 health 读数自洽且带覆盖面自述(nodes==带桶条目数 / bucket_scope)",
|
|
219
|
+
h.get("nodes") == n_bucketed
|
|
220
|
+
and h.get("bucket_scope") == ["knowledge"],
|
|
221
|
+
"nodes=%s n=%s scope=%s" % (h.get("nodes"), n_bucketed,
|
|
222
|
+
h.get("bucket_scope")), live=True)
|
|
223
|
+
check("B5 修复后单桶文案不再称「全库」(统计面已如实)",
|
|
224
|
+
any("单桶" in p for p in (h.get("problems") or []))
|
|
225
|
+
and not any("全库" in p for p in (h.get("problems") or [])),
|
|
226
|
+
str(h.get("problems")))
|
|
227
|
+
cg.close()
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
# ───────────────────────── C:写盘口内容指纹 ─────────────────────────
|
|
231
|
+
|
|
232
|
+
def group_c():
|
|
233
|
+
print("\n【C】issue #53-C:重封必须同步索引内容指纹(零假漂移)")
|
|
234
|
+
pri = Principal(clearance="secret", actor="i53")
|
|
235
|
+
lib = os.path.join(_TMP, "lib_c")
|
|
236
|
+
cg = _mk("lib_c", cls=MdCGSecure, principal=pri, master_key=TEST_KEY)
|
|
237
|
+
nid = cg.add("c_priv", BODY + "私密。\n", layer="contextual",
|
|
238
|
+
sensitivity="private")
|
|
239
|
+
cg.add("c_plain", BODY + "明文。\n", layer="contextual")
|
|
240
|
+
cg.flush()
|
|
241
|
+
m0 = _disk_match(cg, nid)
|
|
242
|
+
check("C1 写入后指纹即落入盘面双形态(前置)", m0[0], str(m0), live=True)
|
|
243
|
+
cg.update_tags(nid, add=["t1"])
|
|
244
|
+
m1 = _disk_match(cg, nid)
|
|
245
|
+
check("C2 update_tags 重封后指纹仍同步(修前:停在旧密文)",
|
|
246
|
+
m1[0] and m1[1] != m0[1], str(m1), live=True)
|
|
247
|
+
cg.append_edge(nid, {"target": "x", "relation_type": "derived_from"})
|
|
248
|
+
m2 = _disk_match(cg, nid)
|
|
249
|
+
check("C3 append_edge 重封后指纹仍同步", m2[0], str(m2))
|
|
250
|
+
cg.flush()
|
|
251
|
+
rep = cg.reconcile_state(apply=False)
|
|
252
|
+
check("C4 对账零假漂移(修前:hash_drift=['c_priv'])",
|
|
253
|
+
rep["hash_drift"] == [], str(rep["hash_drift"]), live=True)
|
|
254
|
+
cg.close()
|
|
255
|
+
|
|
256
|
+
# 跨 close / 重开:毒若未根治会随 compact 快照持久(issue #53-C 实测路径)
|
|
257
|
+
cg2 = _mk("lib_c", cls=MdCGSecure, principal=pri, master_key=TEST_KEY)
|
|
258
|
+
rep2 = cg2.reconcile_state(apply=False)
|
|
259
|
+
m3 = _disk_match(cg2, nid)
|
|
260
|
+
check("C5 重开后仍零假漂移且指纹同步(跨进程持久面)",
|
|
261
|
+
rep2["hash_drift"] == [] and m3[0], str(rep2["hash_drift"]),
|
|
262
|
+
live=True)
|
|
263
|
+
check("C6 明文节点对照:重写后指纹不漂(内容未变,零误伤)",
|
|
264
|
+
_disk_match(cg2, "c_plain")[0], str(_disk_match(cg2, "c_plain")))
|
|
265
|
+
cg2.close()
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
# ───────────────────────── 主流程 ─────────────────────────
|
|
269
|
+
|
|
270
|
+
def main():
|
|
271
|
+
print("【⓪】隔离自证")
|
|
272
|
+
check("0a 临时根内(不触在役库)",
|
|
273
|
+
os.path.abspath(_TMP).startswith(os.path.abspath(tempfile.gettempdir())),
|
|
274
|
+
_TMP, live=True)
|
|
275
|
+
group_a()
|
|
276
|
+
group_b()
|
|
277
|
+
group_c()
|
|
278
|
+
print("\n" + "=" * 66)
|
|
279
|
+
print("PASS=%d FAIL=%d LIVE=%d(floor=%d)" % (PASS, FAIL, LIVE, LIVE_FLOOR))
|
|
280
|
+
check("live 断言数不低于 floor", LIVE >= LIVE_FLOOR, "live=%d" % LIVE)
|
|
281
|
+
if FAILS:
|
|
282
|
+
print("失败项:" + ";".join(FAILS))
|
|
283
|
+
return 0 if FAIL == 0 else 1
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
# =========================================================================
|
|
287
|
+
# 定点变异(把每处修复逐点抽回改动前语义)——字节保真改写 → 跑 → finally 复原
|
|
288
|
+
# =========================================================================
|
|
289
|
+
|
|
290
|
+
def _read_bytes(rel):
|
|
291
|
+
with open(os.path.join(_REPO, rel), "rb") as f:
|
|
292
|
+
return f.read()
|
|
293
|
+
|
|
294
|
+
|
|
295
|
+
def _j(*lines):
|
|
296
|
+
return "\n".join(lines) + "\n"
|
|
297
|
+
|
|
298
|
+
|
|
299
|
+
MUTATIONS = {
|
|
300
|
+
# A:classify_text 单点去掉密文拦截 ⇒ A1 红
|
|
301
|
+
"a_classify": ("md_cg/routing.py",
|
|
302
|
+
" if crypto.is_encrypted(text):\n return None\n",
|
|
303
|
+
" if False:\n return None\n"),
|
|
304
|
+
# A:backfill 显式计数分支抽掉(单点仍在,故 A3 仍绿、A4 计数红)
|
|
305
|
+
"a_backfill": ("md_cg/mdcg.py",
|
|
306
|
+
" if crypto.is_encrypted(content):\n",
|
|
307
|
+
" if False:\n"),
|
|
308
|
+
# B:桶计数退回「同 id 无条件 +1」⇒ B1b 红
|
|
309
|
+
"b_bucket": ("md_cg/mdcg.py",
|
|
310
|
+
_j(" if _ob != _nb:",
|
|
311
|
+
" self._bucket_shift(_ob, -1)",
|
|
312
|
+
" self._bucket_shift(_nb, +1)"),
|
|
313
|
+
_j(" if _nb:",
|
|
314
|
+
" self._bucket_shift(_nb, +1)")),
|
|
315
|
+
# C:写盘口指纹同步段抽掉 ⇒ C2/C4 红
|
|
316
|
+
"c_hash": ("md_cg/mdcg.py",
|
|
317
|
+
_j(" _entry = self.index[\"nodes\"].get(node_id)",
|
|
318
|
+
" if _entry is not None:",
|
|
319
|
+
" _entry[\"content_hash\"] = nodefile.content_hash(sealed)"),
|
|
320
|
+
_j(" _entry = None",
|
|
321
|
+
" if _entry is not None:",
|
|
322
|
+
" _entry[\"content_hash\"] = nodefile.content_hash(sealed)")),
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
|
|
326
|
+
def _run_child():
|
|
327
|
+
env = dict(os.environ)
|
|
328
|
+
env["PYTHONUTF8"] = "1"
|
|
329
|
+
p = subprocess.run([sys.executable, "-X", "utf8", "-m",
|
|
330
|
+
"md_cg.test_issue53_cipher_health_hash"],
|
|
331
|
+
cwd=_REPO, env=env, capture_output=True, text=True,
|
|
332
|
+
encoding="utf-8", errors="replace")
|
|
333
|
+
return p.returncode, (p.stdout or "") + (p.stderr or "")
|
|
334
|
+
|
|
335
|
+
|
|
336
|
+
def _mutate(name):
|
|
337
|
+
rel, old, new = MUTATIONS[name]
|
|
338
|
+
path = os.path.join(_REPO, rel)
|
|
339
|
+
raw = _read_bytes(rel)
|
|
340
|
+
text = raw.decode("utf-8")
|
|
341
|
+
if text.count(old) != 1:
|
|
342
|
+
raise SystemExit("[MUT] %s:锚点命中 %d 次(应为 1)——树已变,请复核"
|
|
343
|
+
% (name, text.count(old)))
|
|
344
|
+
try:
|
|
345
|
+
with open(path, "wb") as f:
|
|
346
|
+
f.write(text.replace(old, new, 1).encode("utf-8"))
|
|
347
|
+
rc, out = _run_child()
|
|
348
|
+
finally:
|
|
349
|
+
with open(path, "wb") as f:
|
|
350
|
+
f.write(raw)
|
|
351
|
+
restored = _read_bytes(rel) == raw
|
|
352
|
+
reds = [ln.strip() for ln in out.splitlines() if "[FAIL]" in ln]
|
|
353
|
+
print("\n" + "=" * 66)
|
|
354
|
+
print("[MUT] %s(%s)→ 子进程 rc=%d" % (name, rel, rc))
|
|
355
|
+
for ln in reds:
|
|
356
|
+
print(" RED " + ln)
|
|
357
|
+
print("[MUT] 红项数 = %d" % len(reds))
|
|
358
|
+
print("[MUT] 恢复核对:%s 逐字节复原=%s" % (rel, restored))
|
|
359
|
+
return rc, len(reds), restored
|
|
360
|
+
|
|
361
|
+
|
|
362
|
+
if __name__ == "__main__":
|
|
363
|
+
args = list(sys.argv[1:])
|
|
364
|
+
_mut = [a for a in args if a.startswith("--mutation")]
|
|
365
|
+
if _mut:
|
|
366
|
+
want = _mut[-1].split("=", 1)[1] if "=" in _mut[-1] else "all"
|
|
367
|
+
names = list(MUTATIONS) if want == "all" else [want]
|
|
368
|
+
bad = []
|
|
369
|
+
for nm in names:
|
|
370
|
+
if nm not in MUTATIONS:
|
|
371
|
+
raise SystemExit("未知变异:%s" % nm)
|
|
372
|
+
rc, nred, ok = _mutate(nm)
|
|
373
|
+
if rc == 0 or nred == 0 or not ok:
|
|
374
|
+
bad.append(nm)
|
|
375
|
+
print("\n[SUMMARY] 变异 %d 项;未变红或未复原的:%s"
|
|
376
|
+
% (len(names), bad or "无"))
|
|
377
|
+
_close_all()
|
|
378
|
+
_cleanup()
|
|
379
|
+
sys.exit(1 if bad else 0)
|
|
380
|
+
try:
|
|
381
|
+
rc = main()
|
|
382
|
+
finally:
|
|
383
|
+
_close_all()
|
|
384
|
+
_cleanup()
|
|
385
|
+
sys.exit(rc)
|
package/package.json
CHANGED
package/skills/plugin.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json",
|
|
3
3
|
"name": "lingshu-skills",
|
|
4
|
-
"version": "0.7.
|
|
4
|
+
"version": "0.7.2",
|
|
5
5
|
"description": "灵枢(AEIS)自我认知技能包——白箱条件化知识(Agent Skills 兼容导出)。本质是灵枢了解自身的工具:每个技能描述灵枢在什么条件下能做什么、怎么执行、克制什么(KCCS 四要素),由条件路由图精确路由,由灵枢 MCP 工具执行验证(物理基底)。",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "灵枢(AEIS)· CommonTrustProtocol"
|
package/zcode/AGENTS.md
CHANGED
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
> 本文件由 `scripts/render_discipline.py` 从真源
|
|
4
4
|
> `docs/工作纪律_认知图条目_v1.1.json` 渲染生成,**请勿手改**;改真源后重跑渲染。
|
|
5
|
-
> 真源指纹(SHA256 前16位):
|
|
5
|
+
> 真源指纹(SHA256 前16位):134dd7e69870405d
|
|
6
6
|
|
|
7
7
|
## 概述
|
|
8
8
|
|
|
9
|
-
工作纪律集: 4条方法论(理论先行/全面处理/白箱方法/根因) + 3条执行(验证/双副本/兜底) + 1条思考(中文思考/协作模式分级) + 1条保密(敏感信息隔离) + 3条图像线(选源护栏/历史查询优先/算法权威唯一) + 1条协作(访谈澄清:先问清再动手) + 1条合规(内容政策合规:公开产物过内容政策+隐私双清单) + 2条执行调度与运行环境(命令执行统一走python/UTF-8: 显式encoding+PYTHONUTF8=1, 规避GBK, 全仓文本与路径一律UTF-8(无BOM, 中文可进路径), 不做控制台兼容(取用走python);
|
|
9
|
+
工作纪律集: 4条方法论(理论先行/全面处理/白箱方法/根因) + 3条执行(验证/双副本/兜底) + 1条思考(中文思考/协作模式分级) + 1条保密(敏感信息隔离) + 3条图像线(选源护栏/历史查询优先/算法权威唯一) + 1条协作(访谈澄清:先问清再动手) + 1条合规(内容政策合规:公开产物过内容政策+隐私双清单) + 2条执行调度与运行环境(命令执行统一走python/UTF-8: 显式encoding+PYTHONUTF8=1, 规避GBK, 全仓文本与路径一律UTF-8(无BOM, 中文可进路径), 不做控制台兼容(取用走python); 任务派发统一走蜂巢(zcode端的工作流工具为等效蜂巢工具——使用者2026-10-04裁定; 蜂巢功能完全完善之前可优先使用zcode工作流): 任何执行性任务经蜂巢spawn/submit执行并留痕, agent本体只做编排, L1出口=产物落点为只读判定(不改仓库/外部状态)可直跑须留痕) + 1条记忆闭环(任务收尾归档:每次任务执行完只将核心修改(内容/原因/位置/验证结论)按CCG六要素(功能名/生效条件/子功能/执行/验证方式/不适用条件)成文存入灵枢记忆, 不写中间过程; text 类缺要素即被写入闸门拒并当场返回缺失清单) + 1条信息获取(工作区索引优先: 查工作区文件先读 WORKSPACE_INDEX.md(仓根, 由 scripts/workspace_index.py 从 git 追踪面管线生成), 尚无则先跑 --write 生成再读, 不以重复全盘浏览代替; 索引是会陈化的手工件, 故须管线生成+机械守卫, 否则退化为误导源)
|
|
10
10
|
|
|
11
11
|
## 执行公约
|
|
12
12
|
|
|
@@ -135,9 +135,9 @@
|
|
|
135
135
|
### 17. 任务派发统一走蜂巢
|
|
136
136
|
|
|
137
137
|
- **触发**:执行性任务|多智能体协作|批量/并行工作|探索盘点|运行测试/脚本
|
|
138
|
-
- **动作**:任务→按产物落点判据定层级(不改仓库/外部状态的只读判定=L1直跑并按任务留痕一条(复核者=使用者+GitHub使用者); 改状态=必派发, 不得自我豁免)→拆spec(LLM委托型=默认exec.py: model+user_prompt+context_files; 确定性执行=自定义worker, 契约「读spec.json写result.json」)→hive_spawn/CLI submit→poll→汇总; 兜底情形须声明「蜂巢不可用, 兜底降级」
|
|
138
|
+
- **动作**:任务→按产物落点判据定层级(不改仓库/外部状态的只读判定=L1直跑并按任务留痕一条(复核者=使用者+GitHub使用者); 改状态=必派发, 不得自我豁免)→拆spec(LLM委托型=默认exec.py: model+user_prompt+context_files; 确定性执行=自定义worker, 契约「读spec.json写result.json」)→hive_spawn/CLI submit(zcode端: 工作流工具为等效蜂巢工具——使用者2026-10-04裁定; 蜂巢功能完全完善之前, zcode端可优先使用zcode工作流处理; 留痕以工作流脚本与运行记录为凭)→poll→汇总; 兜底情形须声明「蜂巢不可用, 兜底降级」
|
|
139
139
|
- **不适用**:纯会话内对话/问答(无执行动作)|蜂巢不可用且任务必须立即执行(兜底须声明)|L1只读判定(产物落点=不改仓库/不改外部状态的本地只读命令, 秒级·可逆·高频, 如跑门禁/测试/回归/lint看结论; 判据是产物落点而非「任务简单」的主观感受, 直跑须输出一行「L1 直跑:<命令> — 风险/频次/可逆性」留痕, 留痕粒度按任务(同一任务的多条只读命令合并为一条留痕, 不逐命令逐行); 层级归属的复核者=使用者+GitHub使用者)|情感交互|闲聊
|
|
140
|
-
- **声明**:按工作纪律第17条: 任务派发统一走蜂巢——任何执行性任务经蜂巢 spawn/submit 执行并留痕(spec/status/result), agent本体只做编排; 宿主自带subagent/team
|
|
140
|
+
- **声明**:按工作纪律第17条: 任务派发统一走蜂巢——任何执行性任务经蜂巢 spawn/submit 执行并留痕(spec/status/result), agent本体只做编排; 宿主自带subagent/team不是等价通道(zcode端的工作流工具为等效蜂巢工具——使用者2026-10-04裁定; 蜂巢功能完全完善之前, zcode端可优先使用zcode工作流处理), 兜底须声明。L1只读判定(产物落点=不改仓库/外部状态)可直跑, 须输出「L1 直跑:<命令> — 风险/频次/可逆性」留痕。
|
|
141
141
|
|
|
142
142
|
### 18. 工作区索引优先
|
|
143
143
|
|
|
@@ -166,7 +166,7 @@
|
|
|
166
166
|
| 14 | 按工作纪律第14条: 内容政策合规——对外公开产物必须过「内容政策+隐私」双清单,过滤在生成阶段做;已发布发现违规要删条+重写git历史+通知平台清缓存。 |
|
|
167
167
|
| 15 | 按工作纪律第15条: 命令执行统一走python——argv列表+显式UTF-8+PYTHONUTF8=1, 不经Windows shell, 规避GBK解码异常。 另: 全仓文本与路径/文件名一律UTF-8(中文可进路径), 不做控制台兼容(取用走python, 控制台乱码属显示层)。 |
|
|
168
168
|
| 16 | 按工作纪律第16条: 任务收尾归档——每次任务执行完只提炼核心修改(内容/原因/位置/验证结论)并按 CCG 六要素(功能名/生效条件/子功能/执行/验证方式/不适用条件)成文存入灵枢记忆(text 类六要素缺失会被写入闸门当场拒绝并返回缺失清单——补齐后重写即可, 不必等读回确认才发现), 写入后发起一次读取查询确认写入成功且可检索, 禁写中间过程/试错/调试等无效信息, 与第2条形成「查记忆→执行→写记忆→读回确认」闭环。 |
|
|
169
|
-
| 17 | 按工作纪律第17条: 任务派发统一走蜂巢——任何执行性任务经蜂巢 spawn/submit 执行并留痕(spec/status/result), agent本体只做编排; 宿主自带subagent/team
|
|
169
|
+
| 17 | 按工作纪律第17条: 任务派发统一走蜂巢——任何执行性任务经蜂巢 spawn/submit 执行并留痕(spec/status/result), agent本体只做编排; 宿主自带subagent/team不是等价通道(zcode端的工作流工具为等效蜂巢工具——使用者2026-10-04裁定; 蜂巢功能完全完善之前, zcode端可优先使用zcode工作流处理), 兜底须声明。L1只读判定(产物落点=不改仓库/外部状态)可直跑, 须输出「L1 直跑:<命令> — 风险/频次/可逆性」留痕。 |
|
|
170
170
|
| 18 | 按工作纪律第18条: 工作区索引优先——查工作区文件先读 WORKSPACE_INDEX.md(仓根, 管线生成); 无则先跑 scripts/workspace_index.py --write 生成再读, 不以重复全盘浏览代替。 |
|
|
171
171
|
|
|
172
172
|
## 记忆接口速查(`cg` / `stg`)
|