@furongjun1999/dsh-memory 0.7.0 → 0.7.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.
Files changed (65) hide show
  1. package/README.md +7 -6
  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 +223 -0
  3. 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 +340 -0
  4. 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 +437 -0
  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 +488 -0
  6. 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 +428 -0
  7. package/docs/eval/issue50_/350/257/273/351/235/242/344/277/235/346/212/244/345/217/252/350/256/244/346/230/276/345/274/217/346/235/245/346/272/220_/344/277/256/345/244/215/350/256/260/345/275/225_v1.0.md +271 -0
  8. 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 +306 -0
  9. 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 +58 -0
  10. 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 +360 -0
  11. package/docs/eval//344/270/211/346/241/243/350/207/252/346/262/273_/346/255/245/351/252/244/342/221/241/346/241/243/344/275/215/345/215/225/344/270/200/345/205/245/345/217/243_/350/220/275/347/240/201/350/256/260/345/275/225_v1.0.md +651 -0
  12. 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 +439 -0
  13. 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 +1337 -0
  14. package/docs/eval//344/270/211/346/241/243/350/207/252/346/262/273_/346/255/245/351/252/244/342/221/244/346/224/266/345/256/230/344/270/216/345/205/250/351/223/276/351/252/214/346/224/266_/350/220/275/347/240/201/350/256/260/345/275/225_v1.0.md +1817 -0
  15. 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 +66 -0
  16. 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 +250 -0
  17. 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 +25 -25
  18. package/docs/plans/stg/346/235/241/344/273/266/345/214/226/344/270/216/347/273/223/346/236/204/347/264/242/345/274/225_/350/256/276/350/256/241_v0.1.md +124 -0
  19. package/docs/plans//350/256/260/345/277/206/350/207/252/345/244/204/347/220/206/344/270/211/346/241/243/350/207/252/346/262/273_/350/256/276/350/256/241_v0.2.md +207 -0
  20. package/md_cg/admission.py +718 -0
  21. package/md_cg/autonomy_modes.py +642 -0
  22. package/md_cg/consistency.py +133 -8
  23. package/md_cg/forgetting.py +38 -6
  24. package/md_cg/freshness.py +17 -1
  25. package/md_cg/lifecycle.py +30 -2
  26. package/md_cg/mcp_server.py +63 -7
  27. package/md_cg/mdcg.py +85 -17
  28. package/md_cg/mdcos.py +666 -16
  29. package/md_cg/protect.py +79 -4
  30. package/md_cg/provenance.py +1 -1
  31. package/md_cg/review_cli.py +31 -2
  32. package/md_cg/rollback.py +411 -0
  33. package/md_cg/rollback_cli.py +104 -0
  34. package/md_cg/stg.py +400 -50
  35. package/md_cg/stgidx.py +281 -0
  36. package/md_cg/test_autonomy_admission.py +1098 -0
  37. package/md_cg/test_autonomy_modes.py +1972 -0
  38. package/md_cg/test_b1_auto_id_multiproc.py +7 -0
  39. package/md_cg/test_b1b2_write_face.py +7 -0
  40. package/md_cg/test_b3_merge_keeps_content.py +7 -0
  41. package/md_cg/test_cons200_scan_selection.py +791 -0
  42. package/md_cg/test_i50a_half_dup_defer.py +408 -0
  43. package/md_cg/test_i50b_defer_to_review_queue.py +545 -0
  44. package/md_cg/test_i50c_meta_passthrough.py +608 -0
  45. package/md_cg/test_i50d_importance_source.py +531 -0
  46. package/md_cg/test_i50e_readside_protection.py +656 -0
  47. package/md_cg/test_issue39_utf8_stdio.py +9 -1
  48. package/md_cg/test_issue52_scan_condition_first.py +677 -0
  49. package/md_cg/test_linkref.py +7 -0
  50. package/md_cg/test_mode_parity.py +1393 -0
  51. package/md_cg/test_mutation_rollback.py +805 -0
  52. package/md_cg/test_n204_n205_n226_n227_n228_n229_exit_gates.py +1 -1
  53. package/md_cg/test_n214_n215_n221_n222_write_face_gates.py +7 -0
  54. package/md_cg/test_p2_mcp.py +8 -0
  55. package/md_cg/test_p9_forget_protect.py +7 -0
  56. package/md_cg/test_p9c_dedup_hints.py +7 -0
  57. package/md_cg/test_policy_required_ccg.py +5 -1
  58. package/md_cg/test_protocol.py +7 -0
  59. package/md_cg/test_stgidx_index_parity.py +1020 -0
  60. package/md_cg/test_writelimit.py +36 -9
  61. package/md_cg/test_writepipe.py +5 -2
  62. package/md_cg/weights.py +15 -1
  63. package/md_cg/writepipe.py +101 -2
  64. package/package.json +1 -1
  65. package/skills/plugin.json +1 -1
@@ -6,7 +6,7 @@
6
6
  B 同构聚合:第 2+ 条并入既有(merge_count+1,不新增节点)
7
7
  C 频率限制:窗口内超量 DEFER;其他 role 不受影响
8
8
  D 保护优先:hint≥0.7 不限流
9
- E 层豁免:knowledge 不聚合不限流
9
+ E 层豁免:knowledge 不聚合不限流(限流器未介入;半重复交遗忘闸门判 DEFER)
10
10
  F env 关闭:MDCG_WRITELIMIT=0 放行
11
11
  G 留痕:_forgetting.jsonl 有 limiter 记录
12
12
  H tidy:已落盘同构组聚合 + 成员降权 + 永不删除
@@ -22,6 +22,13 @@ import shutil
22
22
  import tempfile
23
23
  import time
24
24
 
25
+ # 三档自治批次②(2026-10-02)**夹具隔离**:本守卫考的不是档位面(档位守卫 =
26
+ # md_cg/test_autonomy_modes.py),故显式置 full 档——回到改动前「动作直落」的
27
+ # 行为,使本文件的断言意图(合并/覆写/软删真的发生)逐条不变;env 键名从唯一
28
+ # 真源表取(本文件不构成第二处字面量)。
29
+ from . import autonomy_modes as _autonomy_modes # noqa: E402
30
+ os.environ[_autonomy_modes.AUTONOMY_ENV_KEYS["mode"]] = "full"
31
+
25
32
  from .mdcos import MdCGOS
26
33
  from . import writelimit
27
34
 
@@ -122,9 +129,26 @@ check("D1 hint≥0.7 跳过限流(不聚合)",
122
129
  writelimit._save(cg, {"sigs": {}, "rate": {}}) # 清状态隔离本节
123
130
  r_k1 = _write("wl_k1", "知识条目甲乙丙丁1号", layer="knowledge")
124
131
  r_k2 = _write("wl_k2", "知识条目甲乙丙丁2号", layer="knowledge")
125
- check("E1 knowledge 不聚合不限流",
126
- r_k1["verdict"] == "ACCEPT" and r_k2["verdict"] == "ACCEPT",
132
+ # E1 判据按标题所指重编码(issue50-b):层豁免的本体是**限流器未介入**,
133
+ # 而限流器介入的判据是 gate 里带 `limiter` 键(与 G 节 `rec.get("limiter")`
134
+ # 同口径)。原先编码用「r_k2 判 ACCEPT」代理该语义——那是**旧遗忘闸门裁决**的
135
+ # 副产物(半重复被重要度拦成 ACCEPT),不是层豁免的证据,issue50-a 判半重复
136
+ # 为 DEFER 后必然转红。改判据不改意图。
137
+ check("E1 knowledge 层豁免:限流器未介入(两条写入的 gate 均无 limiter 键)",
138
+ r_k1["verdict"] == "ACCEPT"
139
+ and "limiter" not in (r_k1.get("gate") or {})
140
+ and "limiter" not in (r_k2.get("gate") or {}),
127
141
  f"{r_k1.get('gate')}/{r_k2.get('gate')}")
142
+ # E2 钉住新行为(issue50-a 落地后的既成事实):knowledge 层半重复(两条载荷仅
143
+ # 尾字不同,实测 dup=0.7778)由**遗忘闸门**判 DEFER——判据带 redundancy 读数
144
+ # 且无 limiter 键,与限流 DEFER 可区分。
145
+ check("E2 knowledge 层半重复交遗忘闸门判 DEFER(dup=0.7778)",
146
+ r_k2["verdict"] == "DEFER"
147
+ and "redundancy" in (r_k2.get("gate") or {})
148
+ and "limiter" not in (r_k2.get("gate") or {})
149
+ and round(float((r_k2.get("gate") or {})
150
+ .get("redundancy", {}).get("max") or 0), 4) == 0.7778,
151
+ f"{r_k2.get('gate')}")
128
152
 
129
153
  # ---------------------------------------------------------------- F env 关闭
130
154
 
@@ -158,12 +182,15 @@ check("G1 限流留痕 _forgetting.jsonl",
158
182
  # ---------------------------------------------------------------- H tidy
159
183
 
160
184
  writelimit._save(cg, {"sigs": {}, "rate": {}})
161
- os.environ["MDCG_WRITELIMIT"] = "0" # 绕过写入限流,直造存量同构组
162
- try:
163
- for i in range(5):
164
- _write(f"wl_t{i}", f"巡检批次{i}收官记忆")
165
- finally:
166
- os.environ.pop("MDCG_WRITELIMIT", None)
185
+ # 夹具口径(issue50-b,2026-10-01):本节的目的是「**直造存量同构组**」,让 tidy 有已落盘
186
+ # 的存量可聚合(tidy_contextual 按 template_signature(content) 扫索引分组,不依赖限流签名表)。
187
+ # 原夹具用 MDCG_WRITELIMIT=0 绕限流——那只绕过 writelimit;issue50-a 之后**遗忘闸门**在半重复
188
+ # (dup∈[0.60,0.85))处先拦,5 条只落 1 条(实测 nodes=1、tidy dry groups=0),H1/H3/H4 因此
189
+ # 转红。改用 gated=False 直写:它与本节注释的意图「直造存量」逐字相符,且比绕限流更直白——
190
+ # tidy 的作用面本就是**已落盘的存量组**(历史遗留与绕闸写入),写入前的拦截归遗忘闸门。
191
+ # **H 节四条断言一字未动**(改的是夹具造法,不是判据)。
192
+ for i in range(5):
193
+ _write(f"wl_t{i}", f"巡检批次{i}收官记忆", gated=False)
167
194
  dry = writelimit.tidy_contextual(cg, apply=False)
168
195
  check("H1 dry-run 盘出同构组", dry["groups"] >= 1
169
196
  and dry["members"] >= 4, str(dry["groups"]))
@@ -135,9 +135,12 @@ 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(2026-09-19 deps 闸门新增)
138
+ # 默认 before 链含 linkref→deps→audit→consistency→gated→autonomy
139
+ # (2026-09-19 deps 闸门新增;2026-10-02 三档自治批次② 档位闸**恒在链尾**
140
+ # ——档位判定必须晚于全部既有资格闸,见 writepipe._gate_autonomy docstring。
141
+ # 断言意图不变:默认链的注册表与执行序 = 这一串。)
139
142
  _check("单例默认链", singleton.names()["before"]
140
- == ["linkref", "deps", "audit", "consistency", "gated"],
143
+ == ["linkref", "deps", "audit", "consistency", "gated", "autonomy"],
141
144
  repr(singleton.names()))
142
145
  _check("单例默认after链", singleton.names()["after"]
143
146
  == ["linkref", "trust"],
package/md_cg/weights.py CHANGED
@@ -468,7 +468,7 @@ def node_importance(cg, nid, indeg=None, red=None, entry=None):
468
468
  "protected_floor": protected}
469
469
 
470
470
 
471
- # 生效条件:给定 cg 且 nodes = cg.index["nodes"] 时按 layer 过滤、limit 为真值才 ids = ids[:int(limit)] 逐节点重算,abs(after-before) < float(min_delta) 记 unchanged;仅 apply=True 才把 frontmatter.importance/importance_source/importance_components 写回(after >= IMPORTANCE_PROTECT 且未 protected 时补写 protected/protection_reason),并向 cg.root 下 append_jsonl(..., MAINTAIN_LOG) 记 batch 后 rebuild_index。
471
+ # 生效条件:给定 cg 且 nodes = cg.index["nodes"] 时按 layer 过滤、limit 为真值才 ids = ids[:int(limit)] 逐节点重算,abs(after-before) < float(min_delta) 记 unchanged(**预演面不接档位闸**);仅 apply=True 才在**任何写盘之前**先经 autonomy_modes.e_gate() 档位判定(补强批次 v1.2·E 面接线)——非 ALLOW(plan 档无计划)即经 forbidden_result() fail-closed 早退(零写盘、零审计行),ALLOW(confirm/full)才把 frontmatter.importance/importance_source/importance_components 写回(after >= IMPORTANCE_PROTECT 且未 protected 时补写 protected/protection_reason),并向 cg.root 下 append_jsonl(..., MAINTAIN_LOG) 记 batch 后 rebuild_index。
472
472
  def recalc(cg, layer=None, limit=None, apply=False, min_delta=APPLY_DELTA,
473
473
  actor="maintain", dry_run_samples=10):
474
474
  """结构重要性重算:覆盖度 + 冗余度 + 验证基底 → 重排节点重要性。
@@ -476,6 +476,11 @@ def recalc(cg, layer=None, limit=None, apply=False, min_delta=APPLY_DELTA,
476
476
  apply=False(默认)只出报表,不写盘(对应计划「可预演」)。
477
477
  apply=True 逐节点改写 `frontmatter.importance`,每批写一条 `_maintain.jsonl`
478
478
  (before/after/分量/actor),并提供 rollback 反向应用。
479
+
480
+ **档位闸(补强批次 v1.2·E 面接线)**:`apply=True` 的**写盘前**先经
481
+ `autonomy_modes.e_gate()`(设计 §三 plan 档 E「须命中计划步骤」)——plan 档
482
+ 无计划 ⇒ `forbidden_result()` fail-closed 早退(零写盘、零审计行);confirm/
483
+ full ⇒ ALLOW、**原链原样**(逐位不变)。`apply=False`(预演)不接闸。
479
484
  """
480
485
  nodes = (getattr(cg, "index", None) or {}).get("nodes") or {}
481
486
  indeg = coverage_index(cg)
@@ -519,6 +524,15 @@ def recalc(cg, layer=None, limit=None, apply=False, min_delta=APPLY_DELTA,
519
524
  bl["avg_after"] = 0.0
520
525
  written = 0
521
526
  if apply:
527
+ # 三档自治(补强批次 v1.2·E 面接线,设计 §三 plan 档 E「须命中计划步骤」):
528
+ # E 类动作**写盘前**的档位判定单点(同 `freshness.recalc` 口径)——
529
+ # 本函数无独立资格闸,闸口取「进入写盘支路的第一行」=任何盘面写入之前;
530
+ # ALLOW(confirm/full)原链原样,plan 档无计划 ⇒ fail-closed 早退。
531
+ from . import autonomy_modes as _am
532
+ _dec = _am.e_gate()
533
+ if _dec["decision"] != _am.ALLOW:
534
+ return _am.forbidden_result(_dec, action="importance", dry_run=False,
535
+ written=0, batch=batch)
522
536
  for plan in planned:
523
537
  nid = plan["node_id"]
524
538
  node = cg.get(nid)
@@ -20,7 +20,8 @@ write 的六道闸(audit 校验 / consistency 冲突 / review 审核 / gated
20
20
  绕过(反面清单:不学 pi 的全权信任,信任必须结构强制)。
21
21
 
22
22
  默认链(install_default_gates,与重构前 _cg_dispatch write 分支行为逐字
23
- 节一致):audit → consistency → gated → _executor。
23
+ 节一致):audit → consistency → gated → **autonomy(三档自治档位闸,
24
+ 2026-10-02 批次②)** → _executor。
24
25
 
25
26
  验收口径(交接文档 §3⑥):全部既有写入测试零改动通过;新增/移除一个
26
27
  拦截器不改核心文件(register_before / unregister_before 即插即拔)。
@@ -401,6 +402,12 @@ def _gate_gated(ctx):
401
402
  out["moved_to"] = "merged_into:" + str(res.get("merged_into"))
402
403
  elif v in ("DROP", "DEFER"):
403
404
  out["moved_to"] = v.lower()
405
+ elif v == "CONFIRM":
406
+ # 三档自治批次②:变更确认档下 B 合并已出变更单(未合并)——去向如实
407
+ # 透出 review_queue(DROP/DEFER 两态的字面量分支一字未动)。
408
+ out["moved_to"] = res.get("moved_to") or "review_queue"
409
+ out["mutation"] = res.get("mutation")
410
+ out["autonomy"] = res.get("autonomy")
404
411
  # gated 是**替代落盘路径**(自行落盘/合并后直接返回终态、不跑 after 链),故
405
412
  # 一跳同步传播须在此单独触发——否则 MERGE 类覆写会漏传下游(非对称边界,
406
413
  # 与 `_after_trust` 注释互指)。
@@ -592,6 +599,89 @@ def _executor(ctx):
592
599
  return out
593
600
 
594
601
 
602
+ # ---- 三档自治闸(设计 v0.2 §三 · 批次②) ----------------------------------
603
+
604
+ #: 改写单要复现「原动作」所需的载荷键(**写链 payload 白名单**):变更单裁决
605
+ #: accept 时按此重放 cg.add,使「直接写」与「出单→确认→写」两条路径落盘的
606
+ #: 节点 fm 等价(issue50-c F1 的同一教训:入队时丢声明 = 两条路径元数据不等价)。
607
+ #: `sensitivity`/`layer` 不在列(各自有专属槽:rec.sensitivity / rec.layer);
608
+ #: `override` 在列(受保护节点的覆写授权是原动作的一部分,不能替调用方补)。
609
+ _AUTONOMY_META_KEYS = ("tags", "condition_space", "verification_basis",
610
+ "non_applicable_conditions", "derived_from", "relation",
611
+ "depends_on", "valid_from", "valid_until",
612
+ "verification_state", "importance", "importance_source",
613
+ "override")
614
+
615
+
616
+ # 生效条件:ctx 的 a/cg/nid 就绪时以 forgetting.prior_node 判「同 id 已存在」(存在=C 改写、不存在=A 新增)并交 autonomy_modes.decide 判定——ALLOW 返回 None 放行(与改动前同一条链);FORBID 返回 ok=False/moved_to="autonomy_forbidden" 的终态(fail-closed,不落盘、不出单);CONFIRM 经 autonomy_modes.propose_mutation 出变更单(复用既有 propose 单点)并返回 ok=True/committed=False/moved_to="review_queue" 的终态(**不落盘**);
617
+ def _gate_autonomy(ctx):
618
+ """档位闸:`cg(op=write)` 对**既有 node_id 的覆写**(C 改写)在变更确认档出单。
619
+
620
+ 位置(设计 §三 硬约束②「纯加严」):before 链**末位**——audit / consistency
621
+ / gated 三道既有资格闸全部放行之后、链尾执行器落盘之前。故:
622
+ · 档位**不参与**资格判定(内容政策/冲突/限流/遗忘裁决一律照旧先跑);
623
+ · 档位**不放宽**任何既有判据(放行分支就是「返回 None」= 原链原样);
624
+ · 覆写面的其余资格闸(层闸 + 写保护闸 + 降级闸)住在 `cg.add` 内部,
625
+ 故出单**之前**先经 `cg.write_qualify`(同一批 protect 单点,只判不写)
626
+ 跑一遍——受保护/越权覆写照旧当场被拒,不会变成「静默入队」。
627
+
628
+ 动作类判定:`forgetting.prior_node` 同 id 存在 ⇒ **C 改写**;不存在 ⇒
629
+ **A 新增**(A 在缺省档与会话档都放行,只有计划档会 fail-closed 拦下——
630
+ 设计 §三「计划外零变更」)。判据复用既有单点,不另写一份存在性判据。
631
+
632
+ 不落盘的边界(如实):`gated=true` 的写入在上游 `_gate_gated` 已是**替代
633
+ 执行路径**(自行落盘/合并后直接返回终态、不走本闸),故本闸不覆盖它;
634
+ 该面的档位判定由 `remember_gated` 自己的单点承担——**C 改写 / A 新增**在
635
+ `MdCGOS._autonomy_gate_rewrite`、**B 合并**在 `MdCGOS._autonomy_gate_merge`。
636
+ 本闸只覆盖走链尾执行器的直写(`gated=false` 的 `cg(op=write)`)。
637
+ 三处共用同一张矩阵(`autonomy_modes.decide`),不各写一份判据。
638
+
639
+ 订正记录(2026-10-02,补强批次):本句此前写「其覆写/合并分别由
640
+ `remember_gated` 的 C/B 判定覆盖」——B 合并确已覆盖,**C 覆写当时没有**
641
+ (该分支直调 `self.add`),属失实陈述(独立复核发现 1 的代码根据)。
642
+ 补强批次在 `_autonomy_gate_rewrite` 落码后本句才与实现逐句一致。
643
+ """
644
+ a = ctx["a"]
645
+ cg = ctx["cg"]
646
+ nid = ctx["nid"]
647
+ from . import autonomy_modes as _am
648
+ from . import forgetting as _forgetting
649
+ prior = _forgetting.prior_node(cg, nid)
650
+ action = _am.C_REWRITE if prior is not None else _am.A_ADD
651
+ if prior is not None:
652
+ # 资格在先(纯加严):覆写的层闸/写保护闸住在 `cg.add` 内部,若档位闸
653
+ # 先出单,一次本该 `AccessDenied`/`ProtectionError` 的覆写会变成静默入队
654
+ # ——那是放宽既有判据。故按**同一批 protect 单点**先跑一遍资格(只判不写)。
655
+ cg.write_qualify(nid, target_layer=a.get("layer"),
656
+ override=bool(a.get("override")),
657
+ actor=getattr(cg, "actor", None))
658
+ dec = _am.decide(action)
659
+ if dec["decision"] == _am.ALLOW:
660
+ return None
661
+ # 档位判定读数(**不参与资格判定**,只如实透出档位/动作类/判定)
662
+ _aut = {"mode": dec["mode"], "action": dec["action"],
663
+ "action_name": dec["action_name"], "decision": dec["decision"]}
664
+ if dec["decision"] == _am.FORBID:
665
+ return {"ok": False, "id": nid, "committed": False,
666
+ "moved_to": "autonomy_forbidden", "error": "autonomy_forbid",
667
+ "autonomy": _aut, "verdict": ctx.get("verdict"),
668
+ "hint": dec["hint"]}
669
+ meta = {k: a[k] for k in _AUTONOMY_META_KEYS if a.get(k) is not None}
670
+ pay = _am.mutation_payload(
671
+ action, nid, after=a.get("content", ""),
672
+ reason="写链覆写(cg(op=write) 对既有节点 %s 的 %s)"
673
+ % (nid, _am.ACTION_NAMES[action]),
674
+ primitive="add", meta=meta)
675
+ pr = _am.propose_mutation(cg, action, nid, payload=pay,
676
+ layer=a.get("layer") or "knowledge",
677
+ sensitivity=a.get("sensitivity"), info=True)
678
+ return {"ok": True, "id": nid, "pid": pr["pid"], "committed": False,
679
+ "moved_to": "review_queue", "autonomy": _aut, "mutation": pay,
680
+ "verdict": ctx.get("verdict"),
681
+ "hint": ("%s 本次未落盘:变更单已入审核队列(pid=%s,目标 %s)。"
682
+ % (dec["hint"], pr["pid"], nid))}
683
+
684
+
595
685
  # 生效条件:value 传入即无条件延迟导入并转调 mcp_server._split_ids 后原样返回其结果(本符号无自身分支);
596
686
  def _split_ids(value):
597
687
  # 与 mcp_server._split_ids 同源(延迟导入,单一真源)
@@ -612,9 +702,12 @@ def install_default_gates(pipe):
612
702
 
613
703
  链序(2026-09-19 起):
614
704
  before = linkref(解析) → deps(依赖声明) → audit → consistency → gated
615
- → 链尾执行器
705
+ → autonomy(档位) → 链尾执行器
616
706
  after = linkref(建边) → trust(一跳传播)
617
707
 
708
+ autonomy(三档自治批次②)恒在最末:档位判定只决定「立即落」还是「出变更
709
+ 单」,必须晚于全部既有资格闸(纯加严,设计 §三 硬约束②)。
710
+
618
711
  linkref 置于链首的理由:正文引用解析是**纯读、无副作用**,且其结果必须
619
712
  先于任何短路闸写入 ctx,供 after 链消费。短路闸(REJECT/DEFER/gated)
620
713
  返回终态时 `execute` 不跑 after 链,故未落盘的写入不会建边——语义正确。
@@ -638,6 +731,12 @@ def install_default_gates(pipe):
638
731
  pipe.register_before("audit", _gate_audit)
639
732
  pipe.register_before("consistency", _gate_consistency)
640
733
  pipe.register_before("gated", _gate_gated)
734
+ # 档位闸**链尾**(三档自治批次②,设计 §三 硬约束②):既有资格闸全部通过
735
+ # 之后、链尾执行器落盘之前——档位只决定「立即落」还是「出变更单」,不参与
736
+ # 资格判定。gated 分支是替代执行路径(自行落盘并已 return),其合并/覆写由
737
+ # remember_gated 的 B/C 判定覆盖(`_autonomy_gate_merge` / 补强批次的
738
+ # `_autonomy_gate_rewrite`),故本闸登记在 gated 之后即可。
739
+ pipe.register_before("autonomy", _gate_autonomy)
641
740
  pipe.register_after("linkref", linkref.after_hook())
642
741
  pipe.register_after("trust", _after_trust)
643
742
  return pipe
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@furongjun1999/dsh-memory",
3
- "version": "0.7.0",
3
+ "version": "0.7.1",
4
4
  "description": "灵枢(Lingshu·líng shū)DeepSeek Harness 插件:完整大脑——长期记忆/知识飞轮/自我认知/递归反思接入 DSH,对话自动沉淀进 md_cg 认知图(md 文档)",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
@@ -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.0",
4
+ "version": "0.7.1",
5
5
  "description": "灵枢(AEIS)自我认知技能包——白箱条件化知识(Agent Skills 兼容导出)。本质是灵枢了解自身的工具:每个技能描述灵枢在什么条件下能做什么、怎么执行、克制什么(KCCS 四要素),由条件路由图精确路由,由灵枢 MCP 工具执行验证(物理基底)。",
6
6
  "author": {
7
7
  "name": "灵枢(AEIS)· CommonTrustProtocol"