@furongjun1999/dsh-memory 0.7.1 → 0.7.3
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 +58 -48
- package/codebuddy/CODEBUDDY.md +9 -9
- package/docs/README.md +148 -144
- package/docs/discipline/harnesses.yaml +16 -8
- package/docs/discipline/templates/zcode-user.md.tmpl +49 -0
- package/docs/eval/AGI/344/270/203/347/273/264/350/257/204/345/210/206/346/212/245/345/221/212_/345/205/255/345/256/266/350/256/260/345/277/206/347/263/273/347/273/237/345/220/214/345/260/272/345/256/236/346/265/213_v3.1.md +340 -0
- 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 +2 -2
- package/docs/eval/cons200_/345/206/262/347/252/201/346/243/200/346/265/213/351/200/211/351/235/242_/345/256/236/346/226/275/350/256/260/345/275/225_v1.0.md +1 -1
- package/docs/eval/cons200_/345/206/262/347/252/201/346/243/200/346/265/213/351/200/211/351/235/242_/345/256/236/346/226/275/350/256/260/345/275/225_v1.1.md +1 -1
- 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 +1 -1
- 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 +2 -2
- package/docs/eval/issue50_/345/205/203/346/225/260/346/215/256/351/200/217/344/274/240/344/270/216/345/205/234/345/272/225_/344/277/256/345/244/215/350/256/260/345/275/225_v1.0.md +5 -5
- package/docs/eval/issue50_/345/215/212/351/207/215/345/244/215/345/276/205/345/256/232/345/244/215/346/240/270_/344/277/256/345/244/215/350/256/260/345/275/225_v1.0.md +9 -9
- package/docs/eval/issue50_/345/276/205/345/256/232/345/244/215/346/240/270/345/205/245/351/230/237_/344/277/256/345/244/215/350/256/260/345/275/225_v1.0.md +4 -4
- package/docs/eval/issue50_/351/207/215/350/246/201/345/272/246/345/220/214/346/272/220/344/270/216/344/277/235/346/212/244/350/257/255/344/271/211_/344/277/256/345/244/215/350/256/260/345/275/225_v1.0.md +1 -1
- package/docs/eval/issue51_/344/270/200/351/224/256/345/256/211/350/243/205/345/244/261/350/264/245_/345/275/222/345/261/236/345/210/244/345/256/232_v1.0.md +1 -1
- package/docs/eval/issue52_/346/235/241/344/273/266/345/205/210/350/241/214/344/270/216/346/210/252/346/226/255/345/217/257/350/247/202/346/265/213_/344/277/256/345/244/215/350/256/260/345/275/225_v1.0.md +3 -3
- package/docs/eval//344/270/211/346/241/243/350/207/252/346/262/273_/346/255/245/351/252/244/342/221/242/345/217/230/346/233/264/345/215/225/344/270/216/345/233/236/346/273/232/345/216/237/350/257/255_/350/220/275/347/240/201/350/256/260/345/275/225_v1.0.md +3 -3
- package/docs/eval//344/270/211/346/241/243/350/207/252/346/262/273_/346/255/245/351/252/244/342/221/243/345/207/206/345/205/245/350/257/273/346/225/260_/350/220/275/347/240/201/350/256/260/345/275/225_v1.0.md +7 -7
- 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 +2 -2
- 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 +1 -1
- 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 +7 -7
- 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 +10 -10
- 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 +4 -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 +1 -1
- 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/eval//345/217/221/345/270/20311_/345/244/226/346/212/245/345/205/255/350/277/236/344/277/256/344/270/216/344/270/226/347/225/214/346/250/241/345/236/213/345/272/225/345/272/247_v1.0.md +91 -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 +2 -2
- package/docs/eval//347/224/250/346/210/267/350/243/201/345/206/263/345/244/204/347/275/256_/347/254/254/344/270/200/346/211/271_v1.0.md +195 -0
- package/docs/eval//347/224/250/346/210/267/350/243/201/345/206/263/345/244/204/347/275/256_/347/254/254/344/272/214/346/211/271B6_/346/234/254/346/234/272/350/267/257/345/276/204/347/233/270/345/257/271/345/214/226_v1.0.md +78 -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 +1 -1
- 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 +3 -3
- package/docs/eval//347/254/2543/345/261/202stg/347/273/223/346/236/204/347/264/242/345/274/225_/345/256/236/346/226/275/350/256/260/345/275/225_v1.0.md +1 -1
- 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 +1 -1
- 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_v26.md +196 -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_v27.md +259 -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_v28.md +217 -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_v29.md +270 -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_v30.md +231 -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_v31.md +281 -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 +1 -1
- 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/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 +1 -1
- package/docs/hive//350/234/202/345/267/242/350/256/276/350/256/241_/347/220/206/350/256/272/345/257/271/351/275/220_v0.1.md +17 -17
- package/docs/hive//350/234/202/345/267/242/350/277/255/344/273/243_/345/256/217/350/247/202/344/270/216/347/276/244/344/275/223/350/260/203/345/272/246_v0.1.md +1 -1
- package/docs/mdcg/README/350/257/246/347/273/206/347/211/210_v0.4.10.md +2 -2
- 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 +42 -41
- package/docs/mdcg//345/217/221/345/270/203/351/227/250/347/246/201/351/223/276_v0.1.md +29 -8
- 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 +71 -1
- 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 +1 -1
- package/docs/plans//345/244/232/344/270/273/344/275/223/344/270/226/347/225/214/346/250/241/345/236/213_/345/257/271/351/275/220/350/257/204/344/274/260/344/270/216/350/220/275/345/234/260/350/256/276/350/256/241_v0.1.md +141 -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 +2 -2
- package/docs/plans//350/257/255/344/271/211/346/227/266/347/251/272/345/233/276/350/241/245/345/205/250_/344/270/226/347/225/214/346/250/241/345/236/213/345/212/237/350/203/275/347/253/257_/350/256/276/350/256/241_v0.1.md +107 -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 +9 -8
- package/docs//347/247/244_/350/256/260/345/277/206/347/263/273/347/273/237/350/257/204/346/265/213/350/247/204/350/214/203_v2.1.md +225 -0
- package/dsh/cordis-patch-profile-web.example.yml +2 -2
- package/lib/bridge.js +9 -0
- package/lib/hooks.d.ts +8 -1
- package/lib/hooks.js +94 -32
- package/lib/lib/hook_audit.d.ts +55 -0
- package/lib/lib/hook_audit.js +208 -0
- package/lib/lib/mutual.js +5 -1
- package/lib/lib/roleplay_web.js +17 -2
- package/md_cg/auditview.py +248 -0
- package/md_cg/bench6_arms.py +10 -6
- package/md_cg/bench_e2e_judge.py +4 -2
- package/md_cg/branches.py +29 -3
- package/md_cg/census.py +2 -1
- package/md_cg/consistency.py +38 -13
- package/md_cg/datapath.py +38 -11
- package/md_cg/forgetting.py +15 -2
- package/md_cg/freshness.py +32 -11
- package/md_cg/ghostref.py +130 -0
- package/md_cg/judgment_manifest.py +7 -0
- package/md_cg/mcp_server.py +58 -1
- package/md_cg/mdcg.py +4923 -4764
- package/md_cg/mdcos.py +40 -15
- package/md_cg/mreview/locate.py +1 -1
- package/md_cg/nodefile.py +77 -13
- package/md_cg/readcache.py +29 -4
- package/md_cg/rollback.py +12 -3
- package/md_cg/routing.py +10 -1
- package/md_cg/selfreport.py +12 -3
- package/md_cg/sleep.py +129 -13
- package/md_cg/sources.py +1 -1
- package/md_cg/state_events.py +131 -0
- package/md_cg/sustain.py +1436 -1436
- package/md_cg/test_auditview.py +277 -0
- package/md_cg/test_autonomy_modes.py +2 -1
- package/md_cg/test_boot_openblas_guard.py +197 -0
- package/md_cg/test_ccg_form_parity.py +95 -4
- package/md_cg/test_cons200_scan_selection.py +4 -2
- package/md_cg/test_corrupt_utf8_read_surfaces.py +451 -0
- package/md_cg/test_datapath_device_name.py +62 -3
- package/md_cg/test_generation_guard.py +20 -11
- package/md_cg/test_ghostref.py +182 -0
- package/md_cg/test_health_corrupt_utf8.py +37 -0
- package/md_cg/test_index_crossprocess_reload.py +39 -24
- package/md_cg/test_interop.py +1 -1
- package/md_cg/test_issue52_scan_condition_first.py +1 -1
- package/md_cg/test_issue53_cipher_health_hash.py +385 -0
- package/md_cg/test_lock.py +16 -7
- package/md_cg/test_m3_h9_semantic_guard.py +1 -1
- package/md_cg/test_n230_dirty_replay.py +375 -375
- package/md_cg/test_n238_body_text_parity.py +156 -0
- package/md_cg/test_n276_probe_hoist.py +199 -0
- package/md_cg/test_p4_freshness.py +95 -1
- package/md_cg/test_policy_required_ccg.py +3 -1
- package/md_cg/test_readcache_prodpath.py +66 -0
- package/md_cg/test_sleep.py +58 -1
- package/md_cg/test_sleep_p1.py +218 -2
- package/md_cg/test_state_events.py +178 -0
- package/md_cg/test_time_core_lint.py +8 -1
- package/md_cg/test_writepipe.py +8 -5
- package/md_cg/tokens.py +11 -2
- package/md_cg/writepipe.py +76 -0
- package/package.json +2 -2
- package/skills/plugin.json +1 -1
- package/src/bridge.ts +9 -0
- package/src/hooks.ts +576 -517
- package/src/lib/hook_audit.ts +243 -0
- package/src/lib/mutual.ts +5 -1
- package/src/lib/roleplay_web.ts +17 -2
- package/zcode/AGENTS.md +9 -9
package/md_cg/test_sleep_p1.py
CHANGED
|
@@ -34,6 +34,14 @@
|
|
|
34
34
|
G7 冲突挂起——主库/影子同 id 并发改动 ⇒ 该 id 进 `conflicts`、**未写进主库**、
|
|
35
35
|
也未进影子分支的树;另一条(未冲突的)正常合并。
|
|
36
36
|
G8 回滚——对合并提交 `revert`:主库真源面回到合并前,历史不丢(只增不减)。
|
|
37
|
+
G9 物化窗口内漂移(issue #60)——`ok` 与 `face_stable` 拆开后,活跃工作区
|
|
38
|
+
(每轮都有真源面改动)**照常迭代**:phases 含 iterate、②③④ 不再记
|
|
39
|
+
「本轮未迭代」、台账落一条含 iterate 的记录。
|
|
40
|
+
G10 手动入口(issue #61)——`--once` 不等 tick 即落台账并打印与台账同源的
|
|
41
|
+
「本轮未迭代原因」;`--once --dry-run` 只预览(零台账新增、零物化);
|
|
42
|
+
`--dry-run` 单独使用报错;`--status` 带 `last_cycle` 段。
|
|
43
|
+
G11 物化未完成(父进程持根级锁 ⇒ busy)——②③④ 记「物化未完成:…」,
|
|
44
|
+
与门拦 gate_reason **可区分**(`rec["skipped"]` 为空、两处文案同源)。
|
|
37
45
|
|
|
38
46
|
运行:python -X utf8 -m md_cg.test_sleep_p1
|
|
39
47
|
python -X utf8 -m md_cg.test_sleep_p1 --mutate # 定点变异自证
|
|
@@ -59,6 +67,7 @@ import types
|
|
|
59
67
|
|
|
60
68
|
from . import sleep as SL
|
|
61
69
|
from . import sustain as SUS
|
|
70
|
+
from .fsutil import FileLock
|
|
62
71
|
|
|
63
72
|
_PASS = []
|
|
64
73
|
_FAIL = []
|
|
@@ -611,7 +620,203 @@ def g8_revert():
|
|
|
611
620
|
"G8e 历史只增不减(revert 自身也进历史)")
|
|
612
621
|
|
|
613
622
|
|
|
614
|
-
|
|
623
|
+
# ---------------------------------------------------------------- G9
|
|
624
|
+
class _Cap(io.StringIO):
|
|
625
|
+
"""捕获 `_main` 的输出——它开头会调 `sys.stdout.reconfigure`,纯 StringIO
|
|
626
|
+
没有这个方法(真 stdout 才有),故补一个空实现。"""
|
|
627
|
+
|
|
628
|
+
def reconfigure(self, **_kw): # noqa: D102 —— 空实现
|
|
629
|
+
pass
|
|
630
|
+
|
|
631
|
+
|
|
632
|
+
def _drift_probe(S, *, at=2, rel="knowledge/n_drift.md"):
|
|
633
|
+
"""物化窗口内漂移模拟(补丁就地装上):第 `at` 次调用 `source_face_hashes` 时
|
|
634
|
+
多返回一条新增路径(其余调用照真源);返回**还原函数**。
|
|
635
|
+
|
|
636
|
+
补丁打在**函数实际读取的命名空间**上(`materialize.__globals__`)而不是模块
|
|
637
|
+
属性:变异自证时 `SL` 是 exec 出来的模块,其函数读 exec 命名空间,而模块对象
|
|
638
|
+
的 `__dict__` 只是它的副本——打在模块属性上,变异后的模块内部调用看不到补丁
|
|
639
|
+
(本机实测:G9 会在变异下空转)。真源码下两者是同一个 dict。
|
|
640
|
+
"""
|
|
641
|
+
ns = S.materialize.__globals__
|
|
642
|
+
real = ns["source_face_hashes"]
|
|
643
|
+
seen = {"n": 0}
|
|
644
|
+
|
|
645
|
+
def _wrap(r):
|
|
646
|
+
seen["n"] += 1
|
|
647
|
+
d = dict(real(r))
|
|
648
|
+
if seen["n"] == at:
|
|
649
|
+
d[rel] = "0" * 64
|
|
650
|
+
return d
|
|
651
|
+
|
|
652
|
+
ns["source_face_hashes"] = _wrap
|
|
653
|
+
|
|
654
|
+
def _restore():
|
|
655
|
+
ns["source_face_hashes"] = real
|
|
656
|
+
|
|
657
|
+
return _restore
|
|
658
|
+
|
|
659
|
+
|
|
660
|
+
def g9():
|
|
661
|
+
print("== G9 物化窗口内漂移 ⇒ 照常迭代(#60:不再自我否决)==")
|
|
662
|
+
S = globals()["SL"]
|
|
663
|
+
gen, root, state = _fixture(contextual_hot=1, unverified=1)
|
|
664
|
+
from .mdcos import MdCGOS
|
|
665
|
+
cg = MdCGOS(root)
|
|
666
|
+
n0 = len(S.read_ledger())
|
|
667
|
+
restore = _drift_probe(S)
|
|
668
|
+
try:
|
|
669
|
+
r = S.run_cycle(cg, batch="G9B1", round_index=1, now="03:00")
|
|
670
|
+
finally:
|
|
671
|
+
restore()
|
|
672
|
+
ph = r["steps"][6]["phases"]
|
|
673
|
+
mat = ph.get("materialize") or {}
|
|
674
|
+
ok(mat.get("ok") is True and mat.get("face_stable") is False,
|
|
675
|
+
"G9a 漂移窗口内物化仍记 ok=True、face_stable=False", mat)
|
|
676
|
+
ok(bool(ph.get("iterate")),
|
|
677
|
+
"G9b **物化成功即迭代**:phases 含 iterate 读数(非 None)", sorted(ph))
|
|
678
|
+
ok(all(not s.get("skipped") for s in r["steps"][1:4]),
|
|
679
|
+
"G9c 九步 ②③④ 不再是「本轮未迭代」(裁在门拦或物化未完成,二者皆非)",
|
|
680
|
+
[s.get("skipped") for s in r["steps"][1:4]])
|
|
681
|
+
ok(len(S.read_ledger()) == n0 + 1, "G9d 本轮记账(台账 +1)",
|
|
682
|
+
(n0, len(S.read_ledger())))
|
|
683
|
+
led = S.read_ledger()[-1]
|
|
684
|
+
ok(led.get("batch") == "G9B1"
|
|
685
|
+
and "iterate" in ((led.get("steps") or [{}] * 7)[6].get("phases") or {}),
|
|
686
|
+
"G9e 台账落一条**含 iterate** 的记录(可回看「上次为何迭代/未迭代」)",
|
|
687
|
+
(led.get("batch"), sorted((((led.get("steps") or [{}] * 7)[6]
|
|
688
|
+
.get("phases") or {})))))
|
|
689
|
+
|
|
690
|
+
|
|
691
|
+
# ---------------------------------------------------------------- G10
|
|
692
|
+
def g10():
|
|
693
|
+
print("== G10 手动入口:--once / --once --dry-run / --status(#61 不等 tick)==")
|
|
694
|
+
S = globals()["SL"]
|
|
695
|
+
import time as _t
|
|
696
|
+
gen, root, state = _fixture(contextual_hot=1, unverified=1)
|
|
697
|
+
lt = _t.localtime()
|
|
698
|
+
cur = lt.tm_hour * 60 + lt.tm_min
|
|
699
|
+
a0, b0 = (cur + 120) % 1440, (cur + 180) % 1440
|
|
700
|
+
win = "%02d:%02d-%02d:%02d" % (a0 // 60, a0 % 60, b0 // 60, b0 % 60)
|
|
701
|
+
ok(S.in_window(win, None) is False,
|
|
702
|
+
"G10 前置:构造出的窗口确定性地不含当前时刻(%s)" % win)
|
|
703
|
+
# ① --once --dry-run(窗口内):零台账新增、零物化,打印「将执行…」
|
|
704
|
+
os.environ["MDCG_SLEEP_WINDOW"] = ""
|
|
705
|
+
n0 = len(S.read_ledger())
|
|
706
|
+
cap = _Cap()
|
|
707
|
+
with contextlib.redirect_stdout(cap):
|
|
708
|
+
rc = S._main(["--root", root, "--once", "--dry-run"])
|
|
709
|
+
txt = cap.getvalue()
|
|
710
|
+
ok(rc == 0 and len(S.read_ledger()) == n0,
|
|
711
|
+
"G10a --once --dry-run **零台账新增**", (rc, n0, len(S.read_ledger())))
|
|
712
|
+
ok("将执行物化→迭代→对账→合并" in txt,
|
|
713
|
+
"G10b 预览打印「将执行物化→迭代→对账→合并」",
|
|
714
|
+
[l for l in txt.splitlines() if l.startswith("预览")])
|
|
715
|
+
ok(not os.path.isdir(os.path.join(state, "sleep", "shadow"))
|
|
716
|
+
and not os.path.isdir(os.path.join(state, "sleep", "lib.git")),
|
|
717
|
+
"G10c dry-run **不物化**(影子与版本库都没建出来)")
|
|
718
|
+
# ② --dry-run 单独使用 → 报错
|
|
719
|
+
cap2, err2 = _Cap(), io.StringIO()
|
|
720
|
+
with contextlib.redirect_stdout(cap2), contextlib.redirect_stderr(err2):
|
|
721
|
+
rc2 = S._main(["--root", root, "--dry-run"])
|
|
722
|
+
ok(rc2 == 2 and "--dry-run" in err2.getvalue(),
|
|
723
|
+
"G10d --dry-run 单独使用给出报错(返回 2,不执行任何动作)",
|
|
724
|
+
(rc2, err2.getvalue().strip()))
|
|
725
|
+
# ③ --once(窗口外,确定性构造):落台账 + 打印与台账同源的原因
|
|
726
|
+
os.environ["MDCG_SLEEP_WINDOW"] = win
|
|
727
|
+
n1 = len(S.read_ledger())
|
|
728
|
+
cap3 = _Cap()
|
|
729
|
+
with contextlib.redirect_stdout(cap3):
|
|
730
|
+
rc3 = S._main(["--root", root, "--once"])
|
|
731
|
+
txt3 = cap3.getvalue()
|
|
732
|
+
led = S.read_ledger()
|
|
733
|
+
ok(rc3 == 0 and len(led) == n1 + 1 and len(led) > n0,
|
|
734
|
+
"G10e --once 不等 tick 即落一条台账记录", (rc3, n1, len(led)))
|
|
735
|
+
rec = led[-1]
|
|
736
|
+
ok("窗口外" in (rec.get("skipped") or ""),
|
|
737
|
+
"G10e2 台账记门拦原因(窗口外)", rec.get("skipped"))
|
|
738
|
+
ok("本轮未迭代原因:" in txt3 and (rec.get("skipped") or "") in txt3,
|
|
739
|
+
"G10e3 --once 显式打印「本轮未迭代原因」,文案与台账同源",
|
|
740
|
+
[l for l in txt3.splitlines() if l.startswith("本轮未迭代原因")])
|
|
741
|
+
ok(all(s.get("skipped") == rec.get("skipped") for s in rec["steps"][1:4]),
|
|
742
|
+
"G10e4 台账 ②③④ 的 skip 文案 == 同一门拦原因(不静默)",
|
|
743
|
+
[s.get("skipped") for s in rec["steps"][1:4]])
|
|
744
|
+
# ④ --status:last_cycle = 最近一条台账记录
|
|
745
|
+
def _status():
|
|
746
|
+
capx = _Cap()
|
|
747
|
+
with contextlib.redirect_stdout(capx):
|
|
748
|
+
rcx = S._main(["--root", root, "--status"])
|
|
749
|
+
return rcx, json.loads(capx.getvalue())
|
|
750
|
+
|
|
751
|
+
rcx, doc = _status()
|
|
752
|
+
lc = doc.get("last_cycle") or {}
|
|
753
|
+
ok(rcx == 0 and lc.get("batch") == rec.get("batch")
|
|
754
|
+
and lc.get("round") == rec.get("round")
|
|
755
|
+
and lc.get("candidates") == rec.get("candidates")
|
|
756
|
+
and lc.get("skipped") == rec.get("skipped"),
|
|
757
|
+
"G10f --status 的 last_cycle = 最近一条台账记录(t/batch/round/"
|
|
758
|
+
"candidates/skipped)", lc)
|
|
759
|
+
ok(lc.get("not_iterated_reason") == rec.get("skipped")
|
|
760
|
+
and "窗口外" in (lc.get("not_iterated_reason") or ""),
|
|
761
|
+
"G10f2 last_cycle 带「上次为何没迭代」的一致原因",
|
|
762
|
+
lc.get("not_iterated_reason"))
|
|
763
|
+
ok(lc.get("steps_skip") == {s.get("step"): s.get("skipped")
|
|
764
|
+
for s in rec["steps"][1:4]},
|
|
765
|
+
"G10f3 last_cycle 带 ②③④ 的 skip 文案", lc.get("steps_skip"))
|
|
766
|
+
# ⑤ --once(窗口内):真跑一轮(物化→迭代→对账→合并)后再看 --status
|
|
767
|
+
os.environ["MDCG_SLEEP_WINDOW"] = ""
|
|
768
|
+
cap4 = _Cap()
|
|
769
|
+
with contextlib.redirect_stdout(cap4):
|
|
770
|
+
rc4 = S._main(["--root", root, "--once"])
|
|
771
|
+
rec4 = S.read_ledger()[-1]
|
|
772
|
+
ok(rc4 == 0 and rec4.get("skipped") is None
|
|
773
|
+
and (rec4.get("candidates") or 0) > 0,
|
|
774
|
+
"G10g 窗口内 --once 真迭代(非只记账)",
|
|
775
|
+
{k: rec4.get(k) for k in ("skipped", "candidates", "merged")})
|
|
776
|
+
rcx2, doc2 = _status()
|
|
777
|
+
lc2 = doc2.get("last_cycle") or {}
|
|
778
|
+
mz = lc2.get("materialize") or {}
|
|
779
|
+
ok(isinstance(mz, dict) and all(k in mz for k in ("ok", "busy", "face_stable"))
|
|
780
|
+
and mz.get("ok") is True and mz.get("busy") is False
|
|
781
|
+
and mz.get("face_stable") is True,
|
|
782
|
+
"G10h last_cycle 带 phases.materialize 摘要(ok/busy/face_stable)", mz)
|
|
783
|
+
ok(lc2.get("not_iterated_reason") is None,
|
|
784
|
+
"G10h2 迭代过的一轮不报「未迭代原因」", lc2.get("not_iterated_reason"))
|
|
785
|
+
os.environ.pop("MDCG_SLEEP_WINDOW", None)
|
|
786
|
+
|
|
787
|
+
|
|
788
|
+
# ---------------------------------------------------------------- G11
|
|
789
|
+
def g11():
|
|
790
|
+
print("== G11 物化未完成(根级锁被占)⇒ ②③④ 记**可区分**的 skip 文案 ==")
|
|
791
|
+
S = globals()["SL"]
|
|
792
|
+
gen, root, state = _fixture(contextual_hot=1, unverified=1)
|
|
793
|
+
from .mdcos import MdCGOS
|
|
794
|
+
cg = MdCGOS(root)
|
|
795
|
+
lp = S.lock_path()
|
|
796
|
+
with FileLock(lp, timeout=5.0, strict=False) as lk:
|
|
797
|
+
ok(lk.acquired is True, "G11 前置:父进程已持根级锁", lp)
|
|
798
|
+
r = S.run_cycle(cg, batch="G11B1", round_index=1, now="03:00",
|
|
799
|
+
timeout=0.3)
|
|
800
|
+
ph = r["steps"][6]["phases"].get("materialize") or {}
|
|
801
|
+
ok(ph.get("ok") is False and ph.get("busy") is True,
|
|
802
|
+
"G11a 取不到根级锁 ⇒ 物化 ok=False、busy=True(早退路径语义不变)", ph)
|
|
803
|
+
texts = [s.get("skipped") for s in r["steps"][1:4]]
|
|
804
|
+
ok(all(t and t.startswith("物化未完成:") for t in texts),
|
|
805
|
+
"G11b ②③④ 记「物化未完成:…」——与门拦 gate_reason **可区分**", texts)
|
|
806
|
+
ok(r.get("skipped") is None,
|
|
807
|
+
"G11c 四道门全过 ⇒ rec['skipped'] 为空:两类未迭代原因不混同",
|
|
808
|
+
r.get("skipped"))
|
|
809
|
+
ok(S.cycle_not_iterated_reason(r) == texts[0],
|
|
810
|
+
"G11d 「本轮未迭代原因」== 台账 ②③④ 的同一文案(CLI 与台账同源)",
|
|
811
|
+
S.cycle_not_iterated_reason(r))
|
|
812
|
+
led = S.read_ledger()[-1]
|
|
813
|
+
ok(led.get("batch") == "G11B1"
|
|
814
|
+
and all((s.get("skipped") or "").startswith("物化未完成:")
|
|
815
|
+
for s in led["steps"][1:4]),
|
|
816
|
+
"G11e 物化未完成的一轮同样落台账(可回看)", led.get("batch"))
|
|
817
|
+
|
|
818
|
+
|
|
819
|
+
_GROUPS = (g0, g8, g1, g2, g3, g4, g5, g6, g7, g8_revert, g9, g10, g11)
|
|
615
820
|
|
|
616
821
|
|
|
617
822
|
def _run_groups() -> int:
|
|
@@ -639,8 +844,14 @@ _MUTATIONS = (
|
|
|
639
844
|
("窗口跨午夜判据写反", "md_cg/sleep.py",
|
|
640
845
|
" return t >= start or t < end",
|
|
641
846
|
" return t >= start and t < end"),
|
|
847
|
+
# 锚点同步(2026-10-06):本条此前锚在裁定⑰之前的旧 parse_window 形态上,实现
|
|
848
|
+
# 改了而表未同步 ⇒ `--mutate` 恒以 ANCHOR-MISS 退出 2(**先存**缺陷,非本批引入;
|
|
849
|
+
# 判据 scripts/run_tests.py 不跑 --mutate,故一直未被门禁兜住)。此处按当前实现
|
|
850
|
+
# 重新锚定**同一意图**的变异(留空/非法不再当作全时段,回落 (0,1) 窄窗)——不放宽
|
|
851
|
+
# 也不删除任何既有断言。实测红集 7 项 = G1f/G1h/G1h3 + 依赖空窗全时段入口的新
|
|
852
|
+
# 断言组 G10b/G10g/G10h(编排侧 --mutate 亲跑读数)。
|
|
642
853
|
("留空/非法窗口不再视作全时段", "md_cg/sleep.py",
|
|
643
|
-
" s = str(spec or \"\").strip()\n if not s
|
|
854
|
+
" s = str(spec or \"\").strip()\n if not s:\n return None # 留空 = 全时段(唯一显式入口)",
|
|
644
855
|
" s = str(spec or \"\").strip()\n if not s or \"-\" not in s:\n return (0, 1)"),
|
|
645
856
|
("密文项不再被闸拦下(会随分支入册)", "md_cg/sleep.py",
|
|
646
857
|
' "reason": ("影子侧为密文节点(无密钥不重放、"\n'
|
|
@@ -676,6 +887,11 @@ _MUTATIONS = (
|
|
|
676
887
|
" pass # 睡眠周期失败不中断常驻\n"
|
|
677
888
|
" next_sleep = now + self.sleep_interval\n",
|
|
678
889
|
""),
|
|
890
|
+
# issue #60:物化 ok 语义回退成旧「真源面未变才算成功」——活跃工作区(每轮
|
|
891
|
+
# 都有真源面改动)将永不自迭代;预期红集 = 断言组 G9(G9b/G9c/G9e)。
|
|
892
|
+
("物化 ok 语义回退(漂移即不迭代)", "md_cg/sleep.py",
|
|
893
|
+
' if m.get("ok"):\n # **物化成功即迭代**(issue #60)',
|
|
894
|
+
' if m.get("ok") and m.get("face_stable"):\n # **物化成功即迭代**(issue #60)'),
|
|
679
895
|
("两入口不再共用 sleep 读取器(写死 auto)", "md_cg/mcp_server.py",
|
|
680
896
|
" sleep_interval=_sleep.sleep_interval(),\n"
|
|
681
897
|
" auto_sleep=_sleep.sleep_enabled(),\n"
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""test_state_events · 状态事件账本(append-only)+检验强度字段(check_strength)
|
|
3
|
+
|
|
4
|
+
多主体世界模型对齐 v0.1 §2(L0「事件必记账」/ L1「检验强度」)落地的行为守卫。
|
|
5
|
+
|
|
6
|
+
运行:python -m md_cg.test_state_events
|
|
7
|
+
"""
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import json
|
|
11
|
+
import os
|
|
12
|
+
import tempfile
|
|
13
|
+
|
|
14
|
+
from . import state_events
|
|
15
|
+
from .mdcos import MdCGOS
|
|
16
|
+
from .writepipe import _AUTONOMY_META_KEYS, default_pipeline
|
|
17
|
+
|
|
18
|
+
_ok = 0
|
|
19
|
+
_fail = []
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def check(name, cond, detail=""):
|
|
23
|
+
global _ok
|
|
24
|
+
if cond:
|
|
25
|
+
_ok += 1
|
|
26
|
+
print("[ok] " + name)
|
|
27
|
+
else:
|
|
28
|
+
_fail.append(name)
|
|
29
|
+
print("[FAIL] %s · %s" % (name, str(detail)[:240]))
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def _dump(r):
|
|
33
|
+
return json.dumps(r, sort_keys=True, ensure_ascii=False)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def main():
|
|
37
|
+
tmp = tempfile.mkdtemp(prefix="mdcg_se_test_")
|
|
38
|
+
cg = MdCGOS(os.path.join(tmp, "root"))
|
|
39
|
+
|
|
40
|
+
# ---- 1. append 与读回(落盘序)----
|
|
41
|
+
state_events.append(cg, "鲸娘", "住所", old="帐篷", new="海景别墅",
|
|
42
|
+
kind="migration", seq=553, evidence="用户侧 r0553")
|
|
43
|
+
state_events.append(cg, "鲸娘", "房贷", old="有", new=None,
|
|
44
|
+
kind="retraction", seq=577, evidence="用户侧 r0577")
|
|
45
|
+
state_events.append(cg, "鲸娘", "烤箱", old=None, new="已启用",
|
|
46
|
+
kind="enablement", seq=595, evidence="AI 侧 r0595")
|
|
47
|
+
recs = state_events.read(cg)
|
|
48
|
+
check("append 3 条 → read 回 3 条", len(recs) == 3, len(recs))
|
|
49
|
+
check("落盘序保持(首条=迁移)", recs and recs[0]["kind"] == "migration",
|
|
50
|
+
recs[0] if recs else None)
|
|
51
|
+
check("五元在场", bool(recs) and all(
|
|
52
|
+
k in recs[0] for k in ("subject", "slot", "old", "new", "seq", "evidence")),
|
|
53
|
+
sorted(recs[0].keys()) if recs else None)
|
|
54
|
+
|
|
55
|
+
# ---- 2. append-only 不覆盖(连写后旧记录逐条原样)----
|
|
56
|
+
before = [_dump(r) for r in recs]
|
|
57
|
+
state_events.append(cg, "鲸娘", "住所", old="海景别墅", new="海边小屋",
|
|
58
|
+
kind="migration", seq=700, evidence="r0700")
|
|
59
|
+
after = state_events.read(cg)
|
|
60
|
+
check("追加不覆盖(4 条)", len(after) == 4, len(after))
|
|
61
|
+
check("旧 3 条逐条原样", [_dump(r) for r in after[:3]] == before)
|
|
62
|
+
|
|
63
|
+
# ---- 3. 重启(新实例)读回 ----
|
|
64
|
+
cg2 = MdCGOS(cg.root)
|
|
65
|
+
check("新实例读回 4 条", len(state_events.read(cg2)) == 4)
|
|
66
|
+
|
|
67
|
+
# ---- 4. 过滤与 limit ----
|
|
68
|
+
check("按 slot 过滤", len(state_events.read(cg, slot="住所")) == 2)
|
|
69
|
+
check("按 kind 过滤", len(state_events.read(cg, kind="retraction")) == 1)
|
|
70
|
+
check("limit 取末 2 条", len(state_events.read(cg, limit=2)) == 2)
|
|
71
|
+
check("limit 末条为最后写入", state_events.read(cg, limit=1)[0]["seq"] == 700)
|
|
72
|
+
|
|
73
|
+
# ---- 5. 参数校验(fail-closed)----
|
|
74
|
+
# 注:空 subject/空白 slot 两用例须带 new="v"——否则同时命中「old/new 同空」
|
|
75
|
+
# 校验(2026-10-05 复核:无判别力断言,删目标校验后仍照绿)。
|
|
76
|
+
# 独立库:拒绝用例若在某处校验被误删的变异态下真落盘,只污染本库,
|
|
77
|
+
# 不波及第 6 段条数统计(编排侧变异自证:1 变异 → 1 红的干净定点)。
|
|
78
|
+
bad_root = MdCGOS(os.path.join(tmp, "bad_root"))
|
|
79
|
+
for bad, nm in [
|
|
80
|
+
(dict(subject="", slot="x", new="v"), "空 subject 拒绝"),
|
|
81
|
+
(dict(subject="x", slot=" ", new="v"), "空白 slot 拒绝"),
|
|
82
|
+
(dict(subject="x", slot="y", old=None, new=None), "old/new 同空拒绝"),
|
|
83
|
+
]:
|
|
84
|
+
try:
|
|
85
|
+
state_events.append(bad_root, **bad)
|
|
86
|
+
check(nm, False, "未抛 ValueError")
|
|
87
|
+
except ValueError:
|
|
88
|
+
check(nm, True)
|
|
89
|
+
try:
|
|
90
|
+
state_events.append(bad_root, "x", "y", new="v", kind="teleport")
|
|
91
|
+
check("未知 kind 拒绝", False, "未抛 ValueError")
|
|
92
|
+
except ValueError:
|
|
93
|
+
check("未知 kind 拒绝", True)
|
|
94
|
+
|
|
95
|
+
# ---- 6. 坏行容忍(跳过 + 记账 + 不中断 + 样本有界)----
|
|
96
|
+
# 坏行数(12)须**越过** _BAD_ROW_SAMPLE_CAP(8):只造 2 行时
|
|
97
|
+
# `len(samples) <= cap` 是平凡真(空列表也满足),且删掉记录逻辑后
|
|
98
|
+
# 仍全绿——无判别力(2026-10-05 复核 NEW-red 实证)。故断言取
|
|
99
|
+
# **精确等式** `min(前值+12, cap)` 且要求 >0:既抓「不记录」也抓「无界」。
|
|
100
|
+
p = state_events.ledger_path(cg)
|
|
101
|
+
bad_lines = 12
|
|
102
|
+
with open(p, "a", encoding="utf-8") as f:
|
|
103
|
+
f.write("{broken json\n")
|
|
104
|
+
f.write("[1, 2, 3]\n")
|
|
105
|
+
for i in range(bad_lines - 2):
|
|
106
|
+
f.write("not-json-%d\n" % i)
|
|
107
|
+
n0 = state_events.BAD_ROWS
|
|
108
|
+
s0 = len(state_events._BAD_ROW_SAMPLES)
|
|
109
|
+
recs2 = state_events.read(cg)
|
|
110
|
+
check("坏行跳过后好行全在", len(recs2) == 4, len(recs2))
|
|
111
|
+
check("坏行记账(+%d)" % bad_lines, state_events.BAD_ROWS == n0 + bad_lines,
|
|
112
|
+
state_events.BAD_ROWS - n0)
|
|
113
|
+
_cap = state_events._BAD_ROW_SAMPLE_CAP
|
|
114
|
+
_want = min(s0 + bad_lines, _cap)
|
|
115
|
+
check("坏行样本有界(cap %d,期望 %d,实际 %d)"
|
|
116
|
+
% (_cap, _want, len(state_events._BAD_ROW_SAMPLES)),
|
|
117
|
+
len(state_events._BAD_ROW_SAMPLES) == _want
|
|
118
|
+
and len(state_events._BAD_ROW_SAMPLES) > 0,
|
|
119
|
+
len(state_events._BAD_ROW_SAMPLES))
|
|
120
|
+
|
|
121
|
+
# ---- 7. check_strength 字段(缺省零迁移 / 非法拒绝)----
|
|
122
|
+
cg.add("cs_a", "# 功能名:断言甲\n# 生效条件:测\n", layer="knowledge",
|
|
123
|
+
verification_basis="test", check_strength="hoop")
|
|
124
|
+
fm_a = (cg.get("cs_a") or {}).get("frontmatter") or {}
|
|
125
|
+
check("带 check_strength 落 fm", fm_a.get("check_strength") == "hoop",
|
|
126
|
+
fm_a.get("check_strength"))
|
|
127
|
+
cg.add("cs_b", "# 功能名:断言乙\n# 生效条件:测\n", layer="knowledge",
|
|
128
|
+
verification_basis="test")
|
|
129
|
+
fm_b = (cg.get("cs_b") or {}).get("frontmatter") or {}
|
|
130
|
+
check("缺省不落键(存量零迁移)", "check_strength" not in fm_b,
|
|
131
|
+
fm_b.get("check_strength"))
|
|
132
|
+
try:
|
|
133
|
+
cg.add("cs_c", "# 功能名:断言丙\n# 生效条件:测\n", layer="knowledge",
|
|
134
|
+
verification_basis="test", check_strength="nuke")
|
|
135
|
+
check("非法强度拒绝", False, "未抛 ValueError")
|
|
136
|
+
except ValueError:
|
|
137
|
+
check("非法强度拒绝", True)
|
|
138
|
+
|
|
139
|
+
# ---- 8. 覆写继承(全量重建 fm 的坑)----
|
|
140
|
+
cg.add("cs_a", "# 功能名:断言甲\n# 生效条件:测(覆写)\n", layer="knowledge",
|
|
141
|
+
verification_basis="test", override=True)
|
|
142
|
+
fm_a2 = (cg.get("cs_a") or {}).get("frontmatter") or {}
|
|
143
|
+
check("覆写默认继承", fm_a2.get("check_strength") == "hoop",
|
|
144
|
+
fm_a2.get("check_strength"))
|
|
145
|
+
cg.add("cs_a", "# 功能名:断言甲\n# 生效条件:测(再覆写)\n",
|
|
146
|
+
layer="knowledge", verification_basis="test", override=True,
|
|
147
|
+
check_strength="doubly_decisive")
|
|
148
|
+
fm_a3 = (cg.get("cs_a") or {}).get("frontmatter") or {}
|
|
149
|
+
check("覆写显式改值", fm_a3.get("check_strength") == "doubly_decisive",
|
|
150
|
+
fm_a3.get("check_strength"))
|
|
151
|
+
|
|
152
|
+
# ---- 9. 写链透传(A1 补接,2026-10-05:经路线 C 的 audit 现值面发现
|
|
153
|
+
# 写链实参表漏传 check_strength——经 MCP op=write 声明一律静默丢弃,
|
|
154
|
+
# 与 B2 sensitivity 漏传同族。此处钉住「写链透传」与「变更单载荷白名单」
|
|
155
|
+
# 两个面,防复发。)----
|
|
156
|
+
_pipe = default_pipeline()
|
|
157
|
+
_o = _pipe.execute(cg, {
|
|
158
|
+
"op": "write", "node_id": "cs_wire",
|
|
159
|
+
"content": "# 功能名:写链透传\n# 生效条件:测\n# 子功能:无\n"
|
|
160
|
+
"# 执行:无\n# 验证方式:test\n# 不适用条件:无\n",
|
|
161
|
+
"layer": "knowledge", "content_kind": "text",
|
|
162
|
+
"verification_basis": "test", "check_strength": "smoking_gun"})
|
|
163
|
+
fm_w = (cg.get("cs_wire") or {}).get("frontmatter") or {}
|
|
164
|
+
check("写链透传 check_strength(A1 补接)",
|
|
165
|
+
_o.get("committed") is True
|
|
166
|
+
and fm_w.get("check_strength") == "smoking_gun",
|
|
167
|
+
(_o.get("committed"), fm_w.get("check_strength")))
|
|
168
|
+
check("变更单载荷白名单含 check_strength(A1 补接)",
|
|
169
|
+
"check_strength" in _AUTONOMY_META_KEYS,
|
|
170
|
+
list(_AUTONOMY_META_KEYS))
|
|
171
|
+
|
|
172
|
+
print("\ntest_state_events: %d 通过 / %d 失败" % (_ok, len(_fail)))
|
|
173
|
+
if _fail:
|
|
174
|
+
raise SystemExit(1)
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
if __name__ == "__main__":
|
|
178
|
+
main()
|
|
@@ -631,7 +631,9 @@ def g9():
|
|
|
631
631
|
"confidence": 0.8}],
|
|
632
632
|
"subgraph": {"nodes": ["n2"]},
|
|
633
633
|
"valid_from": ts - 100, "valid_until": ts + 100000,
|
|
634
|
-
"effective_from": ts - 100, "effective_until": ts + 100000
|
|
634
|
+
"effective_from": ts - 100, "effective_until": ts + 100000,
|
|
635
|
+
# A1(2026-10-05):检验强度键存活断言(G9f)的夹具值。
|
|
636
|
+
"check_strength": "hoop"},
|
|
635
637
|
body="# 功能名:n1\n# 生效条件:无条件\n# 子功能:无\n"
|
|
636
638
|
"# 执行:无\n# 验证方式:test\n# 不适用条件:无\n\n"
|
|
637
639
|
"上游条件 葡萄 触发。\n")
|
|
@@ -651,6 +653,11 @@ def g9():
|
|
|
651
653
|
for k in ("valid_from", "valid_until", "effective_from", "effective_until"):
|
|
652
654
|
ok(k in e1 and e1.get(k) == e0.get(k),
|
|
653
655
|
"G9d 双时间轴键 %s 存活(trust.validity 免读盘判定面)" % k)
|
|
656
|
+
ok(nodefile.CHECK_STRENGTH_FIELD in e1
|
|
657
|
+
and e1.get(nodefile.CHECK_STRENGTH_FIELD)
|
|
658
|
+
== e0.get(nodefile.CHECK_STRENGTH_FIELD),
|
|
659
|
+
"G9f 检验强度键存活(check_strength,A1 新键;与 G9b–d 同族防漂移,"
|
|
660
|
+
"2026-10-05 复核补)")
|
|
654
661
|
# 端到端:搬迁后因果路仍能经边召到下游(新路不再静默拿不到边)
|
|
655
662
|
# 种子 = 词法命中 n1(上游),目标 n2 词面无重叠 —— 正是因果路的靶区。
|
|
656
663
|
_r, m = cg.search_rrf("葡萄", k=5, record=False, paths=("lexical", "chain"))
|
package/md_cg/test_writepipe.py
CHANGED
|
@@ -135,12 +135,15 @@ def _run(tmp):
|
|
|
135
135
|
"content": "PASSED SKIPME",
|
|
136
136
|
"layer": "knowledge"})
|
|
137
137
|
_check("移除即恢复(核心文件零改动)", out.get("committed") is True, repr(out))
|
|
138
|
-
# 默认 before 链含 linkref→deps→audit→consistency→gated→autonomy
|
|
139
|
-
# (2026-09-19 deps 闸门新增;2026-10-
|
|
140
|
-
#
|
|
141
|
-
#
|
|
138
|
+
# 默认 before 链含 linkref→ghostref→deps→audit→consistency→gated→autonomy
|
|
139
|
+
# (2026-09-19 deps 闸门新增;2026-10-05 A2 幽灵引用闸插 linkref 之后——
|
|
140
|
+
# 消费 ctx["linkref_targets"] 免重解析、永不短路;2026-10-02 三档自治
|
|
141
|
+
# 批次② 档位闸**恒在链尾**——档位判定必须晚于全部既有资格闸,见
|
|
142
|
+
# writepipe._gate_autonomy docstring。断言意图不变:默认链的注册表与执行序
|
|
143
|
+
# = 这一串。)
|
|
142
144
|
_check("单例默认链", singleton.names()["before"]
|
|
143
|
-
== ["linkref", "deps", "audit", "consistency", "gated",
|
|
145
|
+
== ["linkref", "ghostref", "deps", "audit", "consistency", "gated",
|
|
146
|
+
"autonomy"],
|
|
144
147
|
repr(singleton.names()))
|
|
145
148
|
_check("单例默认after链", singleton.names()["after"]
|
|
146
149
|
== ["linkref", "trust"],
|
package/md_cg/tokens.py
CHANGED
|
@@ -90,7 +90,14 @@ ALL_OPS = ("help", "info", "route", "read", "write", "goal", "task", "recent", "
|
|
|
90
90
|
# —— 「这条记忆从哪来 / 谁由它派生」的读面;真源
|
|
91
91
|
# md_cg/provenance.py(find_edges)。**只读 op**:不含任何
|
|
92
92
|
# 写入 path,故读面角色一律放行(与 status 同档)
|
|
93
|
-
"ccg", "status", "edges"
|
|
93
|
+
"ccg", "status", "edges",
|
|
94
|
+
# 路线 C(2026-10-05,多主体世界模型对齐 v0.1 §4):证据审计面
|
|
95
|
+
# audit 只读组装器(现值/出处链/两存备择/盲区/退役史)
|
|
96
|
+
# —— 真源 md_cg/auditview.py;只读、零写路径。角色白名单
|
|
97
|
+
# 缺省不给(fail-closed,新增 op 默认不在任何清单内);
|
|
98
|
+
# 本轮开给:designer(* 自动含)+ verify(证据审计=验证
|
|
99
|
+
# 单元的本职读面,见其 ROLE_SPECS 注释)。
|
|
100
|
+
"audit")
|
|
94
101
|
|
|
95
102
|
|
|
96
103
|
class TokenError(Exception):
|
|
@@ -166,8 +173,10 @@ ROLE_SPECS = OrderedDict([
|
|
|
166
173
|
# 豁免被分型取代(同日裁定迭代)。
|
|
167
174
|
"can_write": True, "can_admin": False, "clearance_cap": "internal",
|
|
168
175
|
"layers_allow": ["rejected", "contextual"],
|
|
176
|
+
# audit(2026-10-05 路线 C):证据审计视图=验证单元的本职读面
|
|
177
|
+
#(现值/出处链/两存备择/盲区/退役史),只读、无写路径。
|
|
169
178
|
"ops_allow": ["info", "route", "read", "write", "verify", "insight",
|
|
170
|
-
"status", "edges"],
|
|
179
|
+
"status", "edges", "audit"],
|
|
171
180
|
"delegable": False,
|
|
172
181
|
"forbidden": ["knowledge/self/anchor 层(不得改被验证内容)",
|
|
173
182
|
"private/secret 密级(restricted 错误处置标记属本职,"
|
package/md_cg/writepipe.py
CHANGED
|
@@ -422,6 +422,45 @@ def _gate_gated(ctx):
|
|
|
422
422
|
return out
|
|
423
423
|
|
|
424
424
|
|
|
425
|
+
# 生效条件:a.get("ghostref") 为 False 时跳过(opt-out,与 linkref=False 同款);否则对 a["content"]
|
|
426
|
+
# 做幽灵引用检测(ghostref.check:短语层 + id 层),结果非空时分别写入 ctx["ghost_phrases"] /
|
|
427
|
+
# ctx["late_refs"] 供链尾执行器落 fm 与出口告警消费;**恒返回 None(永不短路)**——本闸是标记/告警级,
|
|
428
|
+
# 不参与拒收判定。
|
|
429
|
+
def _gate_ghostref(ctx):
|
|
430
|
+
"""幽灵引用闸(before 链:linkref 之后、deps 之前):标记 + 降级告警,**不拒收**。
|
|
431
|
+
|
|
432
|
+
两条防线(对齐评估 v0.1 §2 L1「转引不得升级」;CD-WHALE-01 发现 C):
|
|
433
|
+
① 短语层:回指短语(「上次的/之前的/明明讲过」)且句内无可解析出处
|
|
434
|
+
⇒ ctx["ghost_phrases"] → 落 fm.uncertain_refs(A2 标记面);
|
|
435
|
+
② id 层:引用目标创建时刻晚于本文档声明的生效起点(valid_from/
|
|
436
|
+
effective_from)⇒ ctx["late_refs"] → 出口降级告警(不改建边判定)。
|
|
437
|
+
|
|
438
|
+
为何不短路(与 deps 硬拒的边界):回指是现实写作的常态——「上次的帐篷」
|
|
439
|
+
在对话记忆里天然存在,缺失的是**出处记录**而非内容本身。拒收会把正常
|
|
440
|
+
记忆挡在门外;标记让「出处不确定」这事可见、可裁决(未决不进二值)。
|
|
441
|
+
|
|
442
|
+
为何消费 ctx["linkref_targets"] 而非重算:linkref 是 targets 的唯一
|
|
443
|
+
写入点(同一次解析的两个面);重算会引入「两次解析结果漂移」的可能。
|
|
444
|
+
"""
|
|
445
|
+
a = ctx["a"]
|
|
446
|
+
if a.get("ghostref") is False:
|
|
447
|
+
return None
|
|
448
|
+
from . import ghostref as _ghostref
|
|
449
|
+
# own_from 取显式声明(**is None 判定**:0/空串不做假值跳转——
|
|
450
|
+
# 与 budget_tokens「显式 0 也是显式」同款口径)。
|
|
451
|
+
_own = a.get("valid_from")
|
|
452
|
+
if _own is None:
|
|
453
|
+
_own = a.get("effective_from")
|
|
454
|
+
rep = _ghostref.check(ctx["cg"], a.get("content") or "",
|
|
455
|
+
linkref_targets=ctx.get("linkref_targets"),
|
|
456
|
+
own_from=_own)
|
|
457
|
+
if rep["ghost_phrases"]:
|
|
458
|
+
ctx["ghost_phrases"] = rep["ghost_phrases"]
|
|
459
|
+
if rep["late_refs"]:
|
|
460
|
+
ctx["late_refs"] = rep["late_refs"]
|
|
461
|
+
return None
|
|
462
|
+
|
|
463
|
+
|
|
425
464
|
# 生效条件:ctx["a"]["content"] 的「# 子功能:」行含显式跨节点引用(`@<节点 id>`)且 depends_on 解析为空时返回 ok=False/error="E050" 的终态;depends_on 含库中不存在的 id 时返回 ok=False/error="E051" 的终态;其余(无该行 / 哨兵 / 自然语言自述 / 声明且目标齐备)返回 None 放行;
|
|
426
465
|
def _gate_deps(ctx):
|
|
427
466
|
"""依赖声明闸(before 链:linkref 之后、audit 之前):**硬拒条件缺失**。
|
|
@@ -578,11 +617,38 @@ def _executor(ctx):
|
|
|
578
617
|
depends_on=trust.as_deps(a.get("depends_on")),
|
|
579
618
|
valid_from=a.get("valid_from"), valid_until=a.get("valid_until"),
|
|
580
619
|
verification_state=a.get("verification_state"),
|
|
620
|
+
# A1 补接(2026-10-05,路线 C 动工时经 audit 现值面发现):检验强度
|
|
621
|
+
# 透传。此前 add 层已支持该形参,但**写链实参表漏传**——经 MCP
|
|
622
|
+
# op=write 声明 check_strength 一律静默丢弃(与 B2 sensitivity 漏传
|
|
623
|
+
# 同族:上游声明、落盘面丢字段,比报错难发现)。
|
|
624
|
+
check_strength=a.get("check_strength"),
|
|
625
|
+
# A2 幽灵引用标记(正文属性:每次写入按当次检测重算,不继承——
|
|
626
|
+
# 与 check_strength 的声明继承相反;缺省 None 不落键)。
|
|
627
|
+
uncertain_refs=ctx.get("ghost_phrases") or None,
|
|
581
628
|
**_hyperedge_extra(a))
|
|
582
629
|
out = {"ok": True, "id": ctx["nid"], "committed": True,
|
|
583
630
|
"verdict": ctx["verdict"]}
|
|
584
631
|
if ctx.get("cvd") is not None:
|
|
585
632
|
out["consistency"] = ctx["cvd"]
|
|
633
|
+
# A2 幽灵引用出口(标记/告警级,不改写入语义):短语层标记已随
|
|
634
|
+
# uncertain_refs 落 fm,此处把「值写进了哪里、怎么处置」如实带回。
|
|
635
|
+
if ctx.get("ghost_phrases"):
|
|
636
|
+
from . import nodefile
|
|
637
|
+
_gp = list(ctx["ghost_phrases"])
|
|
638
|
+
out["ghost_refs"] = _gp[:16]
|
|
639
|
+
out["ghost_hint"] = (
|
|
640
|
+
"幽灵引用标记(A2):正文含无可解析出处的回指短语(%s)——"
|
|
641
|
+
"已落 fm.%s(**未拒收**)。若其指代某条既有记忆,请改为可解析引用"
|
|
642
|
+
"(正文写裸 id,写入链自动建 reference 边);确为首次出现时,"
|
|
643
|
+
"该标记即「出处待补」的诚实记录。"
|
|
644
|
+
% ("、".join(_gp[:5]), nodefile.UNCERTAIN_REFS_FIELD))
|
|
645
|
+
if ctx.get("late_refs"):
|
|
646
|
+
out["late_refs"] = list(ctx["late_refs"])[:10]
|
|
647
|
+
out["late_hint"] = (
|
|
648
|
+
"目标晚于自身(A2 降级告警):本文档声明的生效起点早于被引目标的"
|
|
649
|
+
"创建时刻——引用了「当时尚不存在」的目标。边照常建立(linkref 不看"
|
|
650
|
+
"时间轴);请核对两条时间轴:若引用意图成立可忽略,若是回填/转写"
|
|
651
|
+
"产生的时序错觉,请修正 valid_from。")
|
|
586
652
|
# ⑥:直写提示(不改落盘行为、不改 verdict;见本函数开头注释)
|
|
587
653
|
if _prior is not None:
|
|
588
654
|
out["overwrite_of"] = _prior
|
|
@@ -610,6 +676,10 @@ _AUTONOMY_META_KEYS = ("tags", "condition_space", "verification_basis",
|
|
|
610
676
|
"non_applicable_conditions", "derived_from", "relation",
|
|
611
677
|
"depends_on", "valid_from", "valid_until",
|
|
612
678
|
"verification_state", "importance", "importance_source",
|
|
679
|
+
# A1 补接(2026-10-05):检验强度同族入载荷——否则
|
|
680
|
+
# confirm 档出单 → accept 重放会丢该声明(与
|
|
681
|
+
# verification_state 同款的「两条路径元数据不等价」坑)。
|
|
682
|
+
"check_strength",
|
|
613
683
|
"override")
|
|
614
684
|
|
|
615
685
|
|
|
@@ -727,6 +797,12 @@ def install_default_gates(pipe):
|
|
|
727
797
|
"""
|
|
728
798
|
from . import linkref
|
|
729
799
|
pipe.register_before("linkref", linkref.before_hook(), position=0)
|
|
800
|
+
# A2 幽灵引用闸(2026-10-05):插在 linkref 之后——判据 ② 要消费
|
|
801
|
+
# ctx["linkref_targets"](linkref 是 targets 的唯一写入点);判据 ① 的
|
|
802
|
+
# 「句内无可解析出处」与 linkref 同源(同一次检测的两个面)。**永不短路**
|
|
803
|
+
# (标记/告警级,不做拒收——设计稿 v0.1 复核修订:幽灵引用检查器限定
|
|
804
|
+
# 短语层标记;回指是现实写作常态,拒收会把正常记忆挡在门外)。
|
|
805
|
+
pipe.register_before("ghostref", _gate_ghostref, position=1)
|
|
730
806
|
pipe.register_before("deps", _gate_deps)
|
|
731
807
|
pipe.register_before("audit", _gate_audit)
|
|
732
808
|
pipe.register_before("consistency", _gate_consistency)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@furongjun1999/dsh-memory",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.3",
|
|
4
4
|
"description": "灵枢(Lingshu·líng shū)DeepSeek Harness 插件:完整大脑——长期记忆/知识飞轮/自我认知/递归反思接入 DSH,对话自动沉淀进 md_cg 认知图(md 文档)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|
|
@@ -86,7 +86,7 @@
|
|
|
86
86
|
"build": "node -e \"require('node:fs').rmSync('lib',{recursive:true,force:true})\" && tsc -p tsconfig.json",
|
|
87
87
|
"test": "npm run build && node --import tsx --test \"test/*.test.ts\"",
|
|
88
88
|
"prepare": "node -e \"const fs=require('fs');const tsc='node_modules/typescript/bin/tsc';fs.existsSync(tsc)?(fs.rmSync('lib',{recursive:true,force:true}),require('child_process').execFileSync(process.execPath,[tsc,'-p','tsconfig.json'],{stdio:'inherit'})):console.log('[prepare] 跳过构建:typescript 未安装(NODE_ENV=production 或 --omit=dev 会省略 devDependencies)——需要构建请用 npm install --include=dev')\"",
|
|
89
|
-
"gate": "python scripts/check_unreachable.py && python scripts/check_publish_artifact.py && python scripts/check_publish_smoke.py && python scripts/cogmap_sync.py check && python scripts/link_check.py && python scripts/workspace_index.py --check && python scripts/verify_discipline.py --allow-missing && python scripts/gate_rust_crate_test.py && python scripts/gate_rust_parity.py",
|
|
89
|
+
"gate": "python scripts/check_unreachable.py && python scripts/check_publish_artifact.py && python scripts/check_publish_smoke.py && python scripts/cogmap_sync.py check && python scripts/link_check.py && python scripts/workspace_index.py --check && python scripts/discipline_nodes.py --init --write --cg-root .tmp/discipline-cg && python scripts/verify_discipline.py --allow-missing --cg-root .tmp/discipline-cg && python scripts/gate_rust_crate_test.py && python scripts/gate_rust_parity.py && python scripts/check_local_paths.py",
|
|
90
90
|
"prepublishOnly": "npm run build && npm test && npm run gate"
|
|
91
91
|
},
|
|
92
92
|
"repository": {
|
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.3",
|
|
5
5
|
"description": "灵枢(AEIS)自我认知技能包——白箱条件化知识(Agent Skills 兼容导出)。本质是灵枢了解自身的工具:每个技能描述灵枢在什么条件下能做什么、怎么执行、克制什么(KCCS 四要素),由条件路由图精确路由,由灵枢 MCP 工具执行验证(物理基底)。",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "灵枢(AEIS)· CommonTrustProtocol"
|