@furongjun1999/dsh-memory 0.6.0 → 0.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (170) hide show
  1. package/README.md +588 -572
  2. package/codebuddy/CODEBUDDY.md +10 -10
  3. package/data/policy.json +27 -0
  4. package/docs/discipline/harnesses.yaml +31 -0
  5. package/docs/discipline/templates/full.md.tmpl +1 -1
  6. package/docs/discipline/templates/rules.mdc.tmpl +1 -1
  7. package/docs/eval/N225_/347/264/242/345/274/225/346/227/245/345/277/227/351/235/236/345/257/271/350/261/241/350/243/205/350/275/275/351/235/242/347/261/273/345/236/213/351/227/270_v1.0.md +418 -0
  8. package/docs/eval/issue43_/351/273/230/350/256/244/347/255/226/347/225/245/344/270/216/351/224/256/347/261/273/345/236/213/351/227/270_v1.1.md +441 -0
  9. package/docs/eval/issue43_/351/273/230/350/256/244/347/255/226/347/225/245/345/212/240/350/275/275/344/270/216/345/207/255/346/215/256/346/230/216/346/226/207/351/230/237/345/210/227_v1.0.md +398 -0
  10. package/docs/eval//344/274/230/345/214/226/347/254/254/344/270/200/346/211/271_/346/216/245/347/272/277/344/270/216/347/255/211/344/273/267/345/217/230/346/215/242_v1.0.md +498 -0
  11. package/docs/eval//344/274/230/345/214/226/347/254/254/344/270/211/346/211/271_/351/227/250/347/246/201/350/275/254/346/255/243/344/270/216/350/260/203/345/272/246/346/255/242/350/241/200/344/270/216/351/227/250/346/216/247/346/224/266/345/217/243_v1.0.md +669 -0
  12. package/docs/eval//344/274/230/345/214/226/347/254/254/344/272/214/346/211/271_/344/276/235/350/265/226/351/200/217/344/274/240/344/270/216/345/257/271/346/213/215/345/217/243/345/276/204/344/270/216/350/264/237/347/274/223/345/255/230_v1.0.md +683 -0
  13. package/docs/eval//345/217/221/345/270/20307_/345/244/226/351/203/250/346/212/245/345/221/212/345/233/233/346/211/271/344/277/256/345/244/215/344/270/216/346/217/222/344/273/266/351/235/242/345/212/240/345/233/272_v1.0.md +205 -0
  14. package/docs/eval//346/200/247/350/203/275/344/270/223/351/241/271_/345/206/267/346/237/245/350/257/242/344/270/216/345/206/205/345/255/230_v1.0.md +218 -0
  15. package/docs/eval//346/225/205/351/232/234/346/263/250/345/205/245/345/256/236/346/265/213_v1.0.md +10 -0
  16. package/docs/eval//347/274/226/347/240/201/351/235/242/345/211/215/347/275/256_/345/205/245/345/217/243/350/207/252/344/277/235/350/257/201UTF8/344/270/216/346/226/207/346/234/254open/345/256/210/345/215/253_v1.0.md +646 -0
  17. package/docs/eval//347/274/272/351/231/267/346/214/226/346/216/230_/350/207/252/344/270/273/350/277/255/344/273/243_v20.md +283 -0
  18. package/docs/eval//347/274/272/351/231/267/346/214/226/346/216/230_/350/207/252/344/270/273/350/277/255/344/273/243_v21.md +224 -0
  19. package/docs/eval//347/274/272/351/231/267/346/214/226/346/216/230_/350/207/252/344/270/273/350/277/255/344/273/243_v22.md +223 -0
  20. package/docs/eval//347/274/272/351/231/267/346/214/226/346/216/230_/350/207/252/344/270/273/350/277/255/344/273/243_v23.md +293 -0
  21. package/docs/eval//347/274/272/351/231/267/346/214/226/346/216/230_/350/207/252/344/270/273/350/277/255/344/273/243_v24.md +303 -0
  22. package/docs/eval//347/274/272/351/231/267/346/214/226/346/216/230_/350/207/252/344/270/273/350/277/255/344/273/243_v25.md +230 -0
  23. package/docs/hive//346/243/200/347/264/242/350/267/257/345/276/204/344/270/216/350/256/244/347/237/245/347/273/223/346/236/204/345/245/221/347/272/246_v0.1.md +20 -0
  24. 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
  25. package/docs/mdcg/README/350/257/246/347/273/206/347/211/210_v0.4.10.md +12 -0
  26. 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
  27. package/docs/mdcg//345/217/221/345/270/203/351/227/250/347/246/201/351/223/276_v0.1.md +21 -9
  28. package/docs/mdcg//347/201/265/346/236/242/350/256/260/345/277/206/345/212/250/350/257/215/345/215/217/350/256/256_v1.0-draft.md +7 -2
  29. package/docs/mdcg//350/256/244/347/237/245/345/233/276_/347/264/242/345/274/225/344/270/216/345/267/245/347/250/213/350/247/204/350/214/203/345/214/226_/350/256/241/345/210/222_v0.1.md +39 -3
  30. package/docs/plans//345/205/250/344/270/255/346/226/207/347/274/226/347/240/201/344/270/216/350/234/202/345/267/242/344/273/273/345/212/241/346/240/207/350/257/206/345/245/221/347/272/246_v2.0.md +220 -0
  31. package/docs/plans//347/234/237/346/272/220/347/264/242/345/274/225_/351/200/232/347/224/250/346/234/272/345/210/266_v0.3.md +227 -0
  32. package/docs/plans//350/234/202/345/267/242/346/250/241/345/236/213/345/257/206/351/222/245/351/205/215/347/275/256/351/235/242_v1.0.md +385 -0
  33. package/docs//345/267/245/344/275/234/347/272/252/345/276/213_/350/256/244/347/237/245/345/233/276/346/235/241/347/233/256_v1.1.json +460 -460
  34. package/lib/bridge.js +24 -2
  35. package/lib/hooks.d.ts +31 -2
  36. package/lib/hooks.js +219 -16
  37. package/lib/init.js +16 -1
  38. package/lib/lib/prompt_safety.d.ts +52 -1
  39. package/lib/lib/prompt_safety.js +76 -1
  40. package/md_cg/audit.py +652 -379
  41. package/md_cg/bench6_arms.py +1 -1
  42. package/md_cg/bench_p0.py +1 -1
  43. package/md_cg/branches.py +2 -2
  44. package/md_cg/ccgc.py +1071 -1006
  45. package/md_cg/chain.py +1 -1
  46. package/md_cg/consistency.py +97 -9
  47. package/md_cg/consolidate.py +34 -9
  48. package/md_cg/crypto.py +59 -26
  49. package/md_cg/docindex.py +16 -1
  50. package/md_cg/evolution.py +17 -1
  51. package/md_cg/export.py +1 -1
  52. package/md_cg/forgetting.py +309 -13
  53. package/md_cg/fsutil.py +454 -8
  54. package/md_cg/identity.py +3 -3
  55. package/md_cg/insight.py +24 -3
  56. package/md_cg/linkref.py +1 -1
  57. package/md_cg/logref.py +327 -0
  58. package/md_cg/mcp_server.py +4152 -3818
  59. package/md_cg/mdcg.py +4208 -3547
  60. package/md_cg/mdcos.py +4491 -4159
  61. package/md_cg/mreview/pipeline.py +15 -1
  62. package/md_cg/nodefile.py +639 -575
  63. package/md_cg/protect.py +158 -11
  64. package/md_cg/protocol.py +41 -2
  65. package/md_cg/provenance.py +22 -4
  66. package/md_cg/reach.py +4 -4
  67. package/md_cg/readcache.py +76 -15
  68. package/md_cg/reconcile.py +1 -1
  69. package/md_cg/refindex.py +246 -37
  70. package/md_cg/refine.py +1 -1
  71. package/md_cg/routing.py +32 -5
  72. package/md_cg/run_tests.py +17 -0
  73. package/md_cg/scrub.py +30 -3
  74. package/md_cg/security.py +47 -1
  75. package/md_cg/self_state.py +5 -5
  76. package/md_cg/sources.py +23 -6
  77. package/md_cg/srcindex.py +352 -0
  78. package/md_cg/stg.py +47 -3
  79. package/md_cg/subgraph.py +2 -2
  80. package/md_cg/sustain.py +1299 -1168
  81. package/md_cg/tasks.py +469 -470
  82. package/md_cg/test_b1_auto_id_multiproc.py +277 -0
  83. package/md_cg/test_b1b2_write_face.py +417 -0
  84. package/md_cg/test_b2_sensitivity_landing.py +223 -0
  85. package/md_cg/test_b3_merge_keeps_content.py +576 -0
  86. package/md_cg/test_b4_shard_dir_selfheal.py +843 -0
  87. package/md_cg/test_b4_shard_dir_selfheal_guard.py +238 -0
  88. package/md_cg/test_c3_transient_read_negative.py +793 -0
  89. package/md_cg/test_c8_search_rrf_gates.py +502 -0
  90. package/md_cg/test_ccg_form_parity.py +188 -0
  91. package/md_cg/test_ccgc.py +28 -3
  92. package/md_cg/test_govern_directread.py +17 -4
  93. package/md_cg/test_h2_session_view_norm.py +233 -0
  94. package/md_cg/test_h4_sustain_snapshot.py +1377 -0
  95. package/md_cg/test_hive_ingest.py +285 -285
  96. package/md_cg/test_issue39_utf8_stdio.py +307 -16
  97. package/md_cg/test_issue43_default_policy.py +865 -0
  98. package/md_cg/test_legacy_p3_node_id_type.py +375 -0
  99. package/md_cg/test_linkref.py +10 -3
  100. package/md_cg/test_lock.py +2 -2
  101. package/md_cg/test_logref.py +1109 -0
  102. package/md_cg/test_m3_h9_bucket_health_protect_mark.py +301 -0
  103. package/md_cg/test_m3_h9_semantic_guard.py +525 -0
  104. package/md_cg/test_mr_m2.py +15 -3
  105. package/md_cg/test_n130_verify_falsified_protect.py +1 -1
  106. package/md_cg/test_n139_dek_provision_failclosed.py +185 -0
  107. package/md_cg/test_n176_link_trust.py +221 -0
  108. package/md_cg/test_n178_units_jobid_gate.py +212 -0
  109. package/md_cg/test_n184_keys_concurrent_provision.py +307 -0
  110. package/md_cg/test_n195_writepath_reload.py +283 -0
  111. package/md_cg/test_n196_stale_gate_skip.py +169 -0
  112. package/md_cg/test_n197_n208_write_face_gates.py +471 -0
  113. package/md_cg/test_n198_tokens_corrupt_failclosed.py +225 -0
  114. package/md_cg/test_n199_tokens_concurrent_write.py +378 -0
  115. package/md_cg/test_n201_proposal_visibility.py +382 -0
  116. package/md_cg/test_n202_session_notes_visibility.py +461 -0
  117. package/md_cg/test_n204_n205_n226_n227_n228_n229_exit_gates.py +542 -0
  118. package/md_cg/test_n206_stdio_jsonrpc_type.py +403 -0
  119. package/md_cg/test_n209_verify_write_face_gates.py +461 -0
  120. package/md_cg/test_n212_n213_n224_generation_gates.py +513 -0
  121. package/md_cg/test_n214_n215_n221_n222_write_face_gates.py +563 -0
  122. package/md_cg/test_n225_nonobject_load.py +1110 -0
  123. package/md_cg/test_n62_tenant_bind_failclosed.py +200 -0
  124. package/md_cg/test_neg_condition_hits.py +333 -0
  125. package/md_cg/test_neg_tail_honesty.py +663 -0
  126. package/md_cg/test_none_id_write_guard.py +4 -3
  127. package/md_cg/test_opt_batch1_md_cg.py +451 -0
  128. package/md_cg/test_p1.py +5 -5
  129. package/md_cg/test_p11_consistency.py +161 -12
  130. package/md_cg/test_p26_refindex.py +2 -2
  131. package/md_cg/test_p27_docindex.py +777 -774
  132. package/md_cg/test_p28_refcheck.py +740 -13
  133. package/md_cg/test_p29_session_ingest_export.py +2 -2
  134. package/md_cg/test_p2_mcp.py +12 -1
  135. package/md_cg/test_p30_maintain.py +33 -2
  136. package/md_cg/test_p31_insight.py +1 -0
  137. package/md_cg/test_p9c_dedup_hints.py +276 -0
  138. package/md_cg/test_policy_required_ccg.py +426 -0
  139. package/md_cg/test_protocol.py +22 -0
  140. package/md_cg/test_rank_parity_score_mode.py +516 -0
  141. package/md_cg/test_read_face_input_gates.py +363 -0
  142. package/md_cg/test_read_face_semantics.py +489 -0
  143. package/md_cg/test_recall_face_guards.py +812 -0
  144. package/md_cg/test_rejected_credential_forms.py +188 -0
  145. package/md_cg/test_rejected_redact.py +146 -0
  146. package/md_cg/test_retr_s1b.py +2 -2
  147. package/md_cg/test_retr_s5.py +20 -7
  148. package/md_cg/test_review_conformance.py +21 -3
  149. package/md_cg/test_security_audit_v21.py +2 -2
  150. package/md_cg/test_server_version.py +67 -0
  151. package/md_cg/test_srcindex.py +171 -0
  152. package/md_cg/test_tenant_env_override_warn.py +63 -50
  153. package/md_cg/test_token_lowercase_form.py +315 -0
  154. package/md_cg/test_v21r1_package_version.py +310 -0
  155. package/md_cg/test_writepipe.py +10 -4
  156. package/md_cg/tokens.py +225 -95
  157. package/md_cg/tool_face.py +4 -4
  158. package/md_cg/trust.py +49 -5
  159. package/md_cg/units.py +204 -6
  160. package/md_cg/weights.py +4 -4
  161. package/md_cg/whitebox_kb/wisdom/knowledge_points.py +1 -1
  162. package/md_cg/writelimit.py +42 -8
  163. package/md_cg/writepipe.py +651 -554
  164. package/package.json +3 -2
  165. package/skills/plugin.json +1 -1
  166. package/src/bridge.ts +25 -2
  167. package/src/hooks.ts +229 -16
  168. package/src/init.ts +15 -1
  169. package/src/lib/prompt_safety.ts +88 -1
  170. package/zcode/AGENTS.md +10 -10
@@ -0,0 +1,303 @@
1
+ # 缺陷挖掘·自主迭代 第 25 轮报告(md_cg 核心面续深·第四圈首站)
2
+
3
+ > **元信息**:第 25 轮 = md_cg 核心面续深·第四圈首站(承接 v23 = 第 24 轮 md_cg 核心面·三圈第九站,编号用至 N208);落盘文件名 `缺陷挖掘_自主迭代_v24.md` 为编排指定(v23 之后顺延)。基线 HEAD `6a1d7823`(第 24 轮落码提交,批次 66);本轮修复落工作区、**未 commit**(提交由编排会话负责)。日期 2026-09-28。
4
+ > **本轮作业面**:四区并行深扫(recent = 批次 66 改动面对抗审查 / legacy = v23 留档清账复测 / writegate = md_cg 写面闸门族三轮深挖 / surface = scripts 管线与插件 TS 面)→ 分诊选 5 个修复目标 → 5 个修复批次落码。
5
+ > **报告会话核验口径**:本报告会话第一手实跑的有:工作区索引查阅与定位、21 处源码行号读码核实(第四节-6)、5 件新守卫逐件复跑(第四节-2)、`python -X utf8 scripts/run_tests.py --jobs 4` 全量(第四节-3)、`npm run gate` 与链式短路后四检查器逐条补跑(第四节-4/5)。四区攻防复现、修复红态与攻防前后对比为复核/修复会话第一手实测(材料附命令与输出),本报告如实转述并逐项标注归属;**凡本会话未跑的检查一律标注来源**(第七节集中列出)。
6
+
7
+ ---
8
+
9
+ ## 一、执行摘要
10
+
11
+ - **四区确认 32 条目 → 去重后 21 项独立缺陷**(recent 6 / legacy 11 / writegate 8 / surface 6;其中 verify 三态写面一条在 recent 与 writegate 各出现一次、N197/N199/N201/N202/N205/N207/N208 七条在 legacy 与 recent/writegate/surface 间重复出现)。**证伪·剔除 3 条**(见 3.3,均出自 writegate 区)。**另:修复过程中新发现 2 项**(N210 授权保留竞态、N211 会话读数第二出口),不在四区清单内但已随各自修复批次落码。故本轮在册缺陷 = **23 项**。
12
+ - 材料摘要记「四区确认 32 个」;本报告逐条清点为 **31 条目**(6+11+8+6),差 1 为材料口径与本报告清点的计数差,如实标注,不作粉饰。
13
+ - **修复 5 项(5 个修复批次,落工作区未 commit)**〔口径同 v23:材料「修复 5 项」为**批次**数;本报告按**缺陷粒度**计 **8 号**〕:
14
+ - **3 项 v23 次轮首补候选落地**:**N206**(stdio 主循环非对象 JSON 一行杀 server)、**N202**(会话要点读出口绕过可见性单点)、**N199**(令牌库读-改-写无锁 + 固定共享临时名:撕裂/丢更新/吊销回滚)。
15
+ - **2 项写面绕层闸族同批**:**N197**(insight.verify 直写 `_write_node` 无层闸无写保护闸)、**N208**(ccgc.recalibrate 自报判据免闸 + corrections 换行可注入正文行)。
16
+ - **1 项本轮新发现 high**:**N209**(verify 角色的写面只有 `falsified` 一态接层闸,`confirmed`/`weakened` 分支零闸——连带依赖者写面与搬迁写面也无线)。
17
+ - **2 项修复期新发现并同批修复**:**N210**(derive 与并发 revoke 交错可产出「父已吊销、子仍有效」凭据)、**N211**(`session_recall` ④ 未解问题段与 ① 会话要点同根、同样只按 layer 过滤就直读正文)。
18
+ - **留档未修 15 项**:**N212**(代际探活只看 `_index.json` 快照签名 → N195 写保护闸回退失活)、**N213**(陈旧条目的可见性判定与正文错位 = N196 相邻方向)、**N214**(gated MERGE 经 `forgetting.reinforce` 免双闸,high)、**N215**(gated CONVERGE 经 `writelimit.converge_into` 免写保护闸,low)、**N216**(发布件门禁清单可被 package.json 自身 `prepare` stdout 冒充,high)、**N217**(三件门禁脚本顶层非对象 JSON 崩点 + 退出码语义错位,medium)、**N218**(`dsh_log_index` 非对象 JSON 崩点中断整轮摄取,medium)、**N219**(`src/bridge.ts` stdout 行处理器一行 `null` 杀宿主,medium)、**N220**(`migrate_restricted --ids` 绕过脚本自陈判据 + 恒定 `override=True`,medium)+ **legacy 复测仍成立 6 项**:N200、N201、N203、N204、N205、N207。
19
+ - **编号裁定(本轮核心工程事实)**:树内 `N209` 被**两个不同缺陷**同时占用(`md_cg/test_n209_verify_write_face_gates.py` 文件名与内容=verify 三态写面;`md_cg/mdcos.py:309/:2647` 与 `md_cg/test_n202_session_notes_visibility.py` 注释=会话读数 ④ 段);修复席③又自编 `N210`(仓库树内并无该字样)。按 v22 3.0-② 先例(**树内实占·守卫文件名优先**)+「实际最大值 +1 续编」,本轮裁定见 **3.0**:`N209` 归 verify 三态写面,其余新号 **N210–N220 全局唯一**(N210–N220 在本会话 `grep` 扫描下均为树内零占号)。
20
+ - **门禁**:`python -X utf8 scripts/run_tests.py --jobs 4` → **237/237 通过、5 跳过、退出码 0**(本会话第一手实跑;较第 24 轮 234 增加 3 件新守卫 + 另 2 件计入既有计数面);5 件新守卫逐件复跑 **全绿(33/36/21/28/23 = 141 断言)**;hive/rust 面**零改动故 cargo 未运行**(如实标注);`npm run gate` → **VERDICT=FAIL、退出码 1**,内容面 R1/R2/R3 全 PASS,**唯一 FAIL 为 R4 非追踪件面 命中=5**(本轮 5 件新守卫尚未 `git add`,属**时序性**而非真实规则红;明细见第四节-4);`&&` 短路后四检查器由本会话**逐条补跑且全绿**(`cogmap_sync check` 通过 / `link_check` OK=228 BROKEN=0 / `workspace_index --check` OK / `verify_discipline --allow-missing` 9/9)。
21
+ - **纪律**:全部攻防复现在系统临时目录/临时库/哑令牌/哑密钥环境;未触真实令牌库、真实在役数据目录、真实 serve 与 DSH 探针节点;测毕清理(第六节)。
22
+
23
+ ---
24
+
25
+ ## 二、修复清单(8 号缺陷,5 个修复批次;落工作区未 commit)
26
+
27
+ 改动面:`git diff --stat` = `md_cg/ccgc.py` +51 / `md_cg/insight.py` +9 / `md_cg/mcp_server.py` +127 / `md_cg/mdcg.py` +11 / `md_cg/mdcos.py` +77 / `md_cg/protect.py` +73 / `md_cg/tokens.py` +190 / `md_cg/trust.py` +24(合计 8 文件 +450/−112);`git status` 另见 5 件未追踪新守卫。**未执行 `git commit`**。
28
+
29
+ ### 修复① N206(high,legacy·v23 第 24 轮首编号,次轮首补候选):stdio 主循环非对象 JSON 一行杀 server
30
+
31
+ - **where**:缺陷原行 `md_cg/mcp_server.py:3790-3795`(旧 `try` 只包 `json.loads`,`:3794` 裸调 `method = msg.get("method")`)/`:3807-3808`(`name = params.get("name")` 同型)。修复落点:新增模块级入口函数 `md_cg/mcp_server.py:3636 _serve_line(cg, msg) -> bool`——非对象类型闸 `:3655`(回 `id=null` 的 `-32600`、返回 False 继续服务)、tools/call 的 params 类型闸 `:3679`(回 `-32602` 且不进工具)、分派体兜底 `:3708`(`-32603`,stderr 留痕);主循环接线 `:3876`(`stop = _serve_line(cg, msg)`,读循环体 `:3874-3888` 外层再兜一层 `-32603`)。
32
+ - **缺陷面**:stdio 主循环不校验解析结果类型——一行非对象 JSON(`[]` / `123` / `null` / `"x"`)即抛未捕获 `AttributeError` 逃出 `main()`,**整个 MCP server 进程退出**(`tools/list` 永无应答);`params` 非对象同型死在 `:3808`。进程死亡期间记忆面(含写入)全不可用。对照面 `hive/hive_mcp/mcp_server.py` 的 `_rpc` 已在位(v2-N16)。
33
+ - **修复(最小改动 + fail-closed)**:把 `main()` 循环体内的 method 分派链**原样**提取为 `_serve_line`(唯一语义变化:notification 的 `continue`→`return False`、shutdown 的 `break`→`return True`),加三道入口闸;错误码口径与 hive `_rpc` 逐条对齐。未改任何 op/工具/marker 段,故 cogmap 行号锚零漂移(本会话实跑 `cogmap_sync check` 通过佐证)。
34
+ - **攻防前后对比(修复会话第一手;本会话未重跑攻击)**:修前喂一行 `[]` → rc=1、stdout 仅 initialize 一条、stderr 尾 `AttributeError: 'list' object has no attribute 'get'`(`line 3794, in main`);`123`/`null`/`"x"`/`true`/`params=[1,2]` 同型。修后同一批攻击行(6 条非对象 JSON + 3 条非 dict params)夹击 `initialize`/`tools/list`:rc=0,6 条各得一条 `id=null` 的 `-32600`、3 条各得 `-32602` 且 id 原样回带、夹击之后的 initialize 与两次 tools/list 全部应答;首行即 `[]`(握手前)亦 rc=0。
35
+ - **守卫**:`md_cg/test_n206_stdio_jsonrpc_type.py`(33 断言 / live=15;实弹 11 + 单测面 13 + control 2 + 握手前 2 + 源断言 3 + floor 1)。红→绿:修前 `6 passed, 25 failed (live=15)` → 修后 **33 passed / 0 failed**(本会话复跑同结果)。
36
+
37
+ ### 修复② N202 + N211(high;N202 = legacy·次轮首补候选;N211 = 本轮新发现·同族第二出口)
38
+
39
+ - **where**:缺陷面 `md_cg/mdcos.py:2548 _session_notes`(出口原为 `:2573` 的 `_read`,失效判据是「`_read` 返回 None 即视为不存在」)/`md_cg/mdcos.py:2586 session_recall` ① 段(同一出口)/**`md_cg/mdcos.py:2652` ④ 未解问题段(N211:同样只按 layer 过滤就直读正文抽 `# 问题:`)**。修复落点:判据单点 `md_cg/mdcos.py:308 _note_visible(cg, e)`,接线 `:2571`(① 段)与 `:2652`(④ 段);可见性单点 `_readable` 在 `md_cg/mdcos.py:3984`(子类 `MdCGSecure`)。
40
+ - **缺陷面**:restricted 档会话要点按设计**不加密、盘中明文**,故 `_read` 必成功、`content is None` 判据永不命中 → 不过 `_readable` → 持 session op 且 `can_read_restricted=False` 的身份(record / reflect / output / sustain,tokens 实测)经 `cg(op=session, action=recall)` 拿到 restricted 要点**正文摘要明文**;而同一节点经 `get`/`search` 已被正确拒绝(两出口口径矛盾)。④ 段同根:`unresolved=[{'id':…,'question':'限制级待解问题…'}]` 直出正文。
41
+ - **修复(最小改动 + fail-closed)**:两出口在 tag 过滤之后、直读正文之前接同一 `_note_visible`——不可见即视为不存在;判据取用按仓内既有**可选钩子**口径封装(与 `backfill._readable_guard` / `linkref._known_ids` 一致:有 `_readable` 走单点/判据抛异常按不可见 fail-closed/无该属性 = 纯 `MdCGOS` 无身份密级模型,不降级)。第③点是修中实测得到的必要修正:直调 `self._readable` 会让纯 `MdCGOS` 实例抛 `AttributeError` 被 `session_recall` 的 except 吞成 degraded(`md_cg.test_p29_session_ingest_export` 实测转红后修正)。未改 `_readable` 判据本身、未改角色集、未改 restricted 档落盘明文设计。
42
+ - **攻防前后对比(修复会话第一手)**:修前 record 身份 `session_recall(session=None, limit=5)` 的 `notes` 含「限制级会话要点…」正文摘要而 `get()` 恒 None;修后 notes 与 unresolved 均不含 restricted 正文、同响应的 internal 要点照常返回(出口未降级),处置链路 designer/orchestr/verify 仍可见(未误伤必读面);把 `_readable` 置恒真(复刻修前形态)两出口泄漏**双双复现**,还原后重新闭合——判据确为闸门而非碰巧为空。
43
+ - **守卫**:`md_cg/test_n202_session_notes_visibility.py`(36 断言 / live=16;含 ⓿ 隔离自证、两个出口 + 反向腿、工具面真身 `_session_call`、stdio 端到端、判据可证伪性、纯 `MdCGOS` 不降级、源断言)。红→绿:修前 `19 passed, 8 failed (live=12)` → 修后 **36 passed / 0 failed**(本会话复跑同结果)。
44
+
45
+ ### 修复③ N199 + N210(high/medium;N199 = legacy·次轮首补候选;N210 = 本轮新发现·授权保留竞态)
46
+
47
+ - **where**:缺陷面 `md_cg/tokens.py:343`(旧 `tmp = p + ".tmp"` 固定共享临时名)+ `:604-610`(旧 revoke 读改写无锁)/`:420-423`(issue 同型)。修复落点:`md_cg/tokens.py:294 _store_lock`(`FileLock(token_file(path), timeout=TOKEN_LOCK_TIMEOUT=10.0, strict=True)`,超时 → `TokenError` fail-closed,镜像 crypto 的 N184 口径)、`:361 _save`(写前对账 + 盘面独有记录逐键并入 + `fsutil.atomic_write` 落盘 `:386`)、`:436 issue`、`:555 derive`(持锁 + **锁内父令牌落盘态复核 `:565-577`**=N210)、`:660 revoke` / `:667 _revoke_locked`(拆持锁壳与非重入递归)、`:724 _write_secret`(`atomic_write`,原 `:666` 同款固定临时名)。
48
+ - **缺陷面**:①**N199 三害并存**——主令牌库被撕成不可解析(叠加批次 66 的 N198 写前对账 fail-closed 后 `_tokens.json` 永久不可写、`_load` 回落空表使全部既有令牌立即失效 = **自锁死**);`publish` 的 `os.replace` 撞上被对方消费的共享临时名 → 未捕获 `FileNotFoundError`/`PermissionError` 逃到调用方;**丢更新**——issue 已把明文返回调用方但盘面无记录(拿到即死)、revoke 返回 `ok=True` 而盘面 `revoked_at` 仍为 None(**吊销被静默回滚**)。②**N210**——`derive` 原先 `verify_token` 与 `_load/_save` 之间无锁,并发 revoke(级联只覆盖它落锁那刻盘上已存在的子令牌)与之交错会产出「**父已吊销、子仍有效**」的凭据 = 授权不可回收。
49
+ - **攻防前后对比(修复会话第一手,多进程 barrier 对齐风暴;修前/修后为同一隔离副本对照)**:
50
+ - **修前**:A 取快照 → B 签发 → A 写回 ⇒ 盘面只剩 `tk_stale_snapshot`,B 的明文令牌 verify 抛「令牌不存在」(陈旧快照整份写回);8 进程 × 8 枚并发签发 ⇒ fail=57/64,errs 含 `FileNotFoundError: …concurrent_issue.json.tmp -> concurrent_issue.json`(共享临时名被消费)与 `TokenError: 令牌库损坏/不可读(JSONDecodeError: Extra data: line 85…)`(库被撕裂),成功返回的 7 枚 **7/7 verify 全失败**;并发吊销 × 派生 ⇒ 仍造出子记录(父已吊销、子有效);源面 `grep -n "FileLock\|mkstemp\|atomic_write" md_cg/tokens.py` = **0 命中**、`= p + ".tmp"` 赋值 2 处。
51
+ - **修后**:并发 64 枚零失败零丢失、**每枚事后可验**、盘面记录数 == 成功签发数(24/24);并发期 40 次抽样读盘全部 json 可解析;父已吊销时**拒绝派生**且盘上零孤儿记录;`= p + ".tmp"` 赋值 **0 处**、`atomic_write(` 出现 2 次(两个写点)。
52
+ - **守卫**:`md_cg/test_n199_tokens_concurrent_write.py`(21 断言 / live=12,8 组;隔离前置自证默认令牌库与 aux 根都落在守卫临时目录否则 FATAL)。红→绿(隔离副本真跑):HEAD-reverted 副本 `7 passed / 14 failed`(含 3d「child=True」真造出父已吊销的子凭据)→ 修后 **21 passed / 0 failed**(本会话复跑同结果)。
53
+
54
+ ### 修复④ N197 + N208(medium/high,写面绕层闸族同批)
55
+
56
+ - **where**:新增单点 `md_cg/protect.py:286 guard_overwrite`(复用 `:236 _resolve_target` 解析单点 + `:263 require_layer`,进闸前 `_maybe_reload_index` 代际探活);三个绕闸写点全部接线——`md_cg/insight.py:304`(verify 写点,`_write_node` 在 `:306`)、`md_cg/ccgc.py:835`(link,写点 `:838`)、`md_cg/ccgc.py:925`(recalibrate,写点 `:927`);换行注入单点收口 `md_cg/ccgc.py:192 _has_line_break` / `:198 _upsert_ccg_line`,结构化错误码 `E022`(`:77` 登记,`:510`/`:547`/三槽边界触发)。分发面 `md_cg/mcp_server.py` **故意不动**(层闸所需目标层/敏感度真值只有引擎侧有,放分发面会成第二份判据)。
57
+ - **缺陷面**:`md_cg/insight.py` 的 verify 分支直接自我授予 `importance≥IMPORTANCE_FLOOR=0.9` + `protected=True`(`protection_reason='insight_verified'`)+ `verification_basis='data'`,整条走 `_write_node`、不经 add 的双闸(模块内 `require_layer_write|guard_write|guard_move|is_protected|is_immutable|require_admin` 计数 = 0);`md_cg/ccgc.py` 的 `recalibrate` 免层闸写入且 corrections 值未过滤换行——经 `nodefile.condition_space_text` 拼进「# 生效条件:」行、再由 `ccgc.py:201` 原样替换正文行 ⇒ **注入伪造正文行**(`ccg_field_value` 取首个命中行即被顶替);准入仅 `mcp_server.py` 的字符串比较 `verifier != compiled_by`(两个值都由调用方自报,无签章/凭据),与 attest/link 的 E052 凭据闸不对称。
58
+ - **攻防前后对比(修复会话第一手)**:修前 sustain(`layers_allow=('self',)`)对他人 contextual 层 pending 事件 `verify(verdict="verified", evidence=[])` → `{'state':'verified','importance':0.9,'protected':True}` 且 `_protected_history` / `_protected_audit.jsonl` 均无痕迹;record 身份 `recalibrate(apply=true)` 改写 **self 层 immutable 节点** → `LinkResult(ok=True, written=1)`;注入腿盘面出现独立行 `# 功能名:注入的新功能名…`。修后同一守卫:越层腿一律 `AccessDenied` 且盘面零改写、designer 对 self 层节点两处写面一律 `ProtectionError`、注入值在 compile 期/recalibrate 边界即 `E022` 拒(盘面零改写、无伪造行、读面未被顶替);**反向腿**(record 对 contextual 合法 verify、reflect 同层 verify、合法单行四槽修正、合法候选+令牌签章 link)全部仍成功——闸按「层 + 保护位」判定,不是一刀切收紧。
59
+ - **守卫**:`md_cg/test_n197_n208_write_face_gates.py`(28 断言 / live=20;每组越层腿/保护腿成对 + 反向腿 + 源断言)。红→绿(隔离副本真跑):HEAD-reverted 副本 `10 passed / 18 failed`(18 条全为实弹写面腿,含 2 条 `call_tool` 工具面端到端)→ 修后 **28 passed / 0 failed**(本会话复跑同结果)。
60
+
61
+ ### 修复⑤ N209(high,本轮新发现,编排方标签):verify 三态写面层闸 + set_state + _move_layer
62
+
63
+ - **where**:`md_cg/mdcos.py:3935 MdCGSecure.verify`(闸从「仅 `falsified`」放宽到 `v in ("confirmed","weakened","falsified")`,`:3953` 代际探活重载,`:3957` 调 `protect.require_layer`);`md_cg/trust.py:680 set_state`(`:703` 层闸、`:711` 越权转负路由 `{"ok": False, "error": "layer_denied", "changed": False}` 并写 `_trust.jsonl` 的 `set_state_denied` 台账——**刻意不 raise**,维持「非法迁移不抛异常 / mark_dependents 永不抛不阻断本次裁决」既有契约,但绝不写盘、不静默);`md_cg/mdcg.py:3386 _move_layer`(`:3403`/`:3405` 对**源层与目标层**各过一道层闸,在 `_write_node` 之前;次序对齐 N131:principal 层闸在先、`guard_move` 保护闸在后),同时覆盖 `scrub.demote` 与 evolution 回滚两个调用者。解析单点 `md_cg/protect.py:236/:263/:286`(N197/N208 批次新增,本批**复用而非重写**,消除第二份解析口径)。
64
+ - **缺陷面**:`MdCGSecure.verify` 只对 `verdict=='falsified'` 加层闸(N130/N196 的修面),`confirmed`/`weakened` 分支**零闸**——直写被验证节点本体 frontmatter(confidence/evidence_count/±evidence/evidence_log)后经 `md_cg/mdcg.py:3511 _write_node` 落盘;连带(a)经 `trust.mark_dependents→set_state→_write_node` 改写**任意层**(含 self 保护层)的依赖者为 doubted;(b)连续 weakened 经 `_move_layer` 把 knowledge 节点**搬出本层**到 contextual。`MdCGSecure` 对 `set_state`/`set_verification`/`mark_dependents` **无任何覆盖**(定义仅在 `md_cg/mdcg.py:1868` 与 `md_cg/trust.py:680`/`:733`),故 state/verification 写路径全程无层闸。持有 verify 角色令牌(`md_cg/tokens.py:159-176`:`layers_allow=["rejected","contextual"]`、`forbidden=["knowledge/self/anchor 层(不得改被验证内容)", …]`)即打穿该契约——**无需 admin、无需 override**。
65
+ - **攻防前后对比(修复会话第一手,临时包副本 + HEAD 覆盖法取红态)**:
66
+ - **修前(`8 passed / 15 failed`,live=16)**:① `op=verify verdict=weakened` 打 knowledge 节点 ⇒ `{'action':'weakened','confidence':0.45,'evidence_count':1,'verification':{'from':'unverified','to':'doubted'}}`,盘面 frontmatter 相应改写;② 同一跳把 **self 层依赖者**写盘为 `verification_state='doubted'`;③ 连发 weakened ⇒ 节点从 knowledge 层迁到 contextual 层;④ 同族邻接出口:`sustain` 直调 `scrub._weaken` 返回 `{'action':'weakened','confidence':0.45}`、`op=scrub action=decontaminate` 改四槽且层迁出;⑤ 直调 `trust.set_state(verify 令牌, self 层节点)` → `ok=True/changed=True` 且写盘。对照腿:同一令牌走 `falsified` 早已 `AccessDenied`(材料口径复现)。
67
+ - **修后(`23 passed / 0 failed`,live=16)**:① weakened/confirmed 同臂均 `AccessDenied`(`角色 verify 无权写入 knowledge 层(可写层 ['rejected','contextual'])`),盘面逐字段零改写;② self 层依赖者未被连带改写;③ 连发 4 次 weakened 全拒、节点仍在 knowledge 层;④ `trust.set_state` → `ok=False/error=layer_denied/changed=False` 且零写盘;⑤ `_move_layer` 越层搬迁被拒且文件未移动,`scrub.decontaminate` 零改写且 `done=['weaken_failed:AccessDenied','demote_failed:AccessDenied']` **显式留痕**(不静默假装成功);**反向腿**:verify 对 contextual 节点 confirmed 仍成功、sustain 对 self 层推进仍 ok、designer 搬迁仍生效(未一刀切收紧)。
68
+ - **守卫**:`md_cg/test_n209_verify_write_face_gates.py`(23 断言 / live=16,五组:⓿ 隔离自证 · ① verify 三态 + 依赖者连带面 + 对照 falsified + 反向腿 · ② 连续 weakened 降级迁出 + designer 反向腿 · ③ `trust.set_state` + 端到端传播 + sustain 反向腿 · ④ `_move_layer` + scrub 邻接调用者 + 反向腿 · ⑤ 四处源断言)。红→绿:HEAD 覆盖副本 `8 passed / 15 failed` → 修后 **23 passed / 0 failed**(本会话复跑同结果)。
69
+ - **修复席明确留档的未覆盖面(诚实申报)**:verify 的 `confirmed`/`weakened` 仍**不过引擎级 `protect.guard_write`**——同层身份(如 sustain 对 self 层、designer 对 anchor/immutable 节点)可无 override 改写其 frontmatter 计数。本批刻意只接层闸以对齐 N130「仅 falsified 走 guard_forget」的既有设计(避免掐死 sustain 的 self 层契约);若要把「不可覆盖」也覆盖到该分支须单独裁定(与 N197 席 insight.verify 因落 `protected=True`/0.9 而接了 `guard_write` 的口径差异已留档)。
70
+
71
+ ---
72
+
73
+ ## 三、发现明细
74
+
75
+ ### 3.0 编号裁定(本轮)
76
+
77
+ **裁定规则**:① 树内实占·守卫文件名优先(v22 3.0-② 先例);② 无树内实占时保留修复席已声明的编号;③ 其余按「已修新发现 → 未修新发现 → 未修 legacy」分区顺序顺延;④ 新号自实际最大值 +1 续编且全局唯一。
78
+
79
+ **树内实占扫描(本会话 `grep`,排除 `docs/eval/`)**:`N209` 命中 6 个产品/守卫文件(`md_cg/mdcg.py:3385/3398/3402`、`md_cg/mdcos.py:309/2647`、`md_cg/mdcos.py:3934/3946`、`md_cg/protect.py:266`、`md_cg/trust.py:679/687/694`、`md_cg/test_n209_verify_write_face_gates.py`、`md_cg/test_n202_session_notes_visibility.py`);**`N210`–`N214` 在树内零命中**(`md_cg/units.py` 等无占用,`grep` 实跑)。故 `N209` 已被两缺陷并号、`N210+` 为空档。
80
+
81
+ | 材料标签 | 所属区 | where 锚 | sev | 状态 | **本报告编号** |
82
+ |---|---|---|---|---|---|
83
+ | N209(recent 首条 / writegate「N210·新发现」/ 修复席⑤ 编排标签) | recent + writegate | `md_cg/mdcos.py:3935`、`md_cg/mdcg.py:3511`、`md_cg/trust.py:680`、`md_cg/mdcg.py:3386` | high | **已修** | **N209**(树内文件名实占,保留) |
84
+ | 修复席② 自编「N209」(会话读数 ④ 段) | 修复期新发现 | `md_cg/mdcos.py:2652` | high | **已修** | **N211**(原树内并号,改判) |
85
+ | 修复席③ 自编「N210」(derive × revoke 竞态) | 修复期新发现 | `md_cg/tokens.py:565-577` | medium | **已修** | **N210**(无树内实占,保留) |
86
+ | N210(recent:代际签名) | recent | `md_cg/mdcg.py:1023` | medium | 留档 | **N212**(改判) |
87
+ | N211(recent:陈旧 entry) | recent | `md_cg/mdcos.py:4098/4110`、`md_cg/protect.py:78` | medium | 留档 | **N213**(改判) |
88
+ | N211(writegate:MERGE→reinforce) | writegate | `md_cg/mdcos.py:3377`、`md_cg/forgetting.py:274` | high | 留档 | **N214**(改判) |
89
+ | N211(writegate:CONVERGE→converge_into) | writegate | `md_cg/mdcos.py:3332/3336`、`md_cg/writelimit.py:225` | low | 留档 | **N215**(改判) |
90
+ | N209(surface:发布件门禁清单可被 `prepare` 冒充) | surface | `scripts/check_publish_artifact.py:269/332` | high | 留档 | **N216**(改判) |
91
+ | N210(surface:门禁脚本顶层非对象 JSON;v23 第五节第 25 项「未修未编号」) | surface | `scripts/check_registry_tarball.py:42`、`check_publish_artifact.py:465/474`、`interop_gate.py:32` | medium | 留档 | **N217**(本轮正式编号,legacy 归属) |
92
+ | N211(surface:`dsh_log_index` 非对象 JSON) | surface | `scripts/dsh_log_index.py:290/302/318` | medium | 留档 | **N218**(改判) |
93
+ | N212(surface:`bridge.ts` 一行 `null` 杀宿主) | surface | `src/bridge.ts:235/240` | medium | 留档 | **N219**(保留) |
94
+ | N213(surface:`migrate_restricted --ids`) | surface | `scripts/migrate_restricted.py:47/85` | medium | 留档 | **N220**(保留) |
95
+ | N197 / N199 / N200 / N201 / N202 / N203 / N204 / N205 / N206 / N207 / N208 | legacy | 见 3.2 | — | 已修 5 / 留档 6 | **原号不变**(v23 首编号,树内实占) |
96
+
97
+ **编号残留(未代改,交编排会话)**:`md_cg/mdcos.py:309`(`N202/N209`)、`md_cg/mdcos.py:2647`(`N209…本族第二出口`)、`md_cg/test_n202_session_notes_visibility.py:33/54/214/372-373`(`N209` 字样)按本表应读作 **N211**;`md_cg/tokens.py` 内 N210 未落字样(该缺陷的代码注释并入 N199 族),故树内无 `N210` 对应的可改字串。报告员未代改任何源码注释/守卫文件名。
98
+
99
+ ### 3.1 本轮新发现(12 号:N209–N220;其中已修 3、留档 9)
100
+
101
+ **N209(high,已修)** verify 写面三态层闸缺口 —— 见 2-修复⑤(where/证据/攻防前后全在该条;复测轮数 = 首轮)。
102
+
103
+ **N210(medium,已修)** derive 与并发 revoke 交错产出「父已吊销、子仍有效」凭据,授权不可回收 —— 见 2-修复③(`md_cg/tokens.py:565-577`;证据:修复会话守卫 3d 腿 `child=True` / `kids=['tk_00dc85bae77f']`,本会话复跑守卫绿)。首轮。
104
+
105
+ **N211(high,已修)** `session_recall` ④ 未解问题段绕过可见性单点(与 ① 会话要点同根)—— 见 2-修复②(`md_cg/mdcos.py:2652`;证据:修复会话独立探针 `record get(unres_restricted)=None`、`_readable=False` 而 `unresolved=[{'question':'限制级待解问题…'}]` 判 LEAK)。首轮。
106
+
107
+ **N212(medium,留档)** 代际探活只看 `_index.json` 快照签名 ⇒ 存活且未 compact 的他进程写方不可见(N195 残留面)
108
+ - where:`md_cg/mdcg.py:1023 _index_signature`(= `_index.json` 的 `(mtime_ns,size)`);接线点 `md_cg/mdcg.py:1508`(add 的 `_maybe_reload_index`)。
109
+ - 缺陷面:`flush()` 只追加 `_index_log` 分片、不重写快照(`md_cg/mdcg.py:1038 _maybe_reload_index` 的实现与注释自述此边界)⇒ N195 修好的写保护闸回退失活:`add` 的 `protect.guard_write` 走 `protect._entry`/`_fm`(`md_cg/protect.py:63/:78`)得条目 None → 判 `(False,'')` → **anchor/self 层不可覆盖节点被无痕覆写**(不抛 `ProtectionError`、不落 `_protected_history`、不写 `_protected_audit.jsonl`);同因次生面:盘中文件已存在但本进程索引无条目时 `get`/`verify`/`forget`/`merge` 一律 not_found(**存在性假阴**)。
110
+ - 攻击面:多进程共享库(常驻 MCP serve 本身就是长期存活写方)+ 第二个写方;B 进程 `add(anchor)` + `flush` 后保持存活(触发窗口 = B 存活期,serve 级可达小时~天),A 进程(索引无该节点)`add(同 id)` 直接覆写,无需提权。既有守卫只覆盖 compact 场景(`md_cg/test_n195_writepath_reload.py`、`md_cg/test_n196_stale_gate_skip.py` 均用 `b.close()`)。
111
+ - 证据(复核会话第一手,三进程握手;本会话**未复跑**,只读码核实 `:1023`/`:1038`/`:1508` 三处在位):`_maybe_reload_index()` 返回 False、重载后索引仍无该节点、`protect.is_immutable(cg,'anchor1')=(False,'')`、`get(anchor1)=None` 而盘面文件在、**覆写成功**、快照/审计均不存在;对照(落快照后新实例)`is_immutable=(True,'层保护:anchor(不可篡改层)')` 且覆写被 `ProtectionError` 拦。
112
+ - 归属面 md_cg;复测轮数:本轮首报(v23 N195/N196 的次生面)。
113
+
114
+ **N213(medium,留档)** 陈旧索引条目的可见性判定与正文错位(N196 相邻方向)+ `protect._fm` 回退判据对当前代码恒假
115
+ - where:`md_cg/mdcos.py:4098 get`(`:4110` 用**本进程陈旧 entry** 判可见性)+ `md_cg/mdcos.py:3984 _readable`(取 `e['sensitivity']/e['session']`,仅 entry 缺 sensitivity 键才回读盘);次生面 `md_cg/protect.py:78`(`need_fallback = ("protected" not in e or "immutable" not in e or …)`)。
116
+ - 缺陷面:N196 只补了「索引缺条目」一态,「索引有条目但陈旧」未处理——可见性判定读旧 entry、正文却由 `super().get` 从盘上新版文件解出(**文件是真源、闸是旧快照**);次生面:`_node_entry`(`md_cg/mdcg.py:1290-1300` 附近)恒落 `protected`/`immutable` 两键(值可为 None)⇒ `need_fallback` 对普通节点恒假 ⇒ 他进程新打的 `protected`/`immutable` 标记对陈旧 entry 不可见,`guard_write`/`guard_forget`/`guard_move` 放行。
117
+ - 攻击面:同 actor 异 session(AEAD 的 AAD 只绑 `(node_id,tenant,actor)`):会话 S2 写方把既有节点 `add(sensitivity='private')` 并 flush(不 close)→ 会话 S1 常驻实例 `get` 仍解出 private 正文(触发窗口 = 升密写方存活期);次生面:他进程新打保护标记后,本进程 `add(同 id)` 覆写不被拦。
118
+ - 证据(复核会话第一手实测;本会话**未复跑**,读码核实 `:4110`/`:3984`/`protect.py:78` 三处在位):陈旧条目 `{'session':'S1','sensitivity':'internal'}` vs 盘面 frontmatter `{'sensitivity':'private','session':'S2'}`、`_maybe_reload_index()` 返回 False、`get` 解出 private 正文 ⇒ 泄漏;对照(B 退出后新实例)`get=None`。次生面:陈旧条目 protected/immutable 均为 null、盘面含 protected 标记 = True、`is_protected=(False,'')`、覆写成功。
119
+ - 归属面 md_cg;**与 N212 同根**(签名不含日志分片这一盲点),差别在危害面(N212 = 写保护与存在性 / N213 = 读隔离与保护标记)——修签名口径可一并覆盖两条。复测轮数:本轮首报。
120
+
121
+ **N214(high,留档)** gated 写入的 MERGE 替代落盘路径免费层闸/写保护闸
122
+ - where:`md_cg/mdcos.py:3377`(`remember_gated` 的 MERGE 分支)× `md_cg/forgetting.py:247/:266/:274`(`reinforce` 直写 `_write_node`);对照 `md_cg/mdcos.py:3889`(ACCEPT 分支走 `Secure.add`,层闸在位)。
123
+ - 缺陷面:MERGE 经 `forgetting.reinforce` 直写 `_write_node`(importance/merge_count/last_merge_at/lifecycle 字段,跨 `PROTECT_IMPORTANCE=0.70`(`forgetting.py:60`)还自赋 `protected=True`/`protection_reason`),既不调 `principal.require_layer_write` 也不调 `protect.guard_write`;**同一次请求若判为 ACCEPT 则被层闸拒**。
124
+ - 攻击面:持 write op 的非管理令牌(record,forbidden「self/anchor 层」)调 `mdcg_remember(gated=true, layer='self', role='user', importance_hint=0.0, content=<与目标 self/anchor 节点正文高度重复>)` → `assess` 判 MERGE → `reinforce(目标)` 覆写;`importance_hint=0` 是让闸门不落 ACCEPT 分支的关键。
125
+ - 证据(复核会话第一手工具面端到端;本会话**未复跑**,读码核实 `mdcos.py:3377`/`forgetting.py:274` 在位):`verdict='MERGE'`、盘面 `importance=0.45 merge_count=1`(改前 0.4)、快照/审计均不存在;对照 `hint=0.9 → ACCEPT` 被 `AccessDenied: 角色 record 无权写入 self 层`、`is_immutable(self_card)=(True,'层保护:self(不可篡改层)')`。
126
+ - 归属面 md_cg;复测轮数:本轮首报。**建议:与 N215 同批修(同一 `remember_gated` 的两个替代落盘分支,同族同修成本最低)**。
127
+
128
+ **N215(low,留档)** gated 写入的 CONVERGE 替代落盘路径免写保护闸
129
+ - where:`md_cg/mdcos.py:3332/3336`(CONVERGE 分支)× `md_cg/writelimit.py:211/:225`(`converge_into` 直写 `_write_node`)。
130
+ - 缺陷面:追加「- 【聚合 …】」行 + `merge_count+1`,不调 `protect.guard_write`——对 frontmatter 标了 `immutable:true` 的目标节点可无快照、无审计地追加正文,而**同一节点在 ACCEPT 面抛 `ProtectionError`**。层维度**不构成绕过**:`writelimit.check` 仅在 `layer=='contextual'` 时生效(`md_cg/writelimit.py:141`),故 CONVERGE 目标必为 contextual 节点(record 本就有 contextual 写权)。
131
+ - 攻击面(需外置/人工标记形态前置):目标 contextual 节点 frontmatter 有 `immutable:true`;① 先发一次让 `check` 预占签名(`node_id` → 该节点),② 同签名不同正文再发 → CONVERGE → `converge_into` 覆写。
132
+ - 证据(复核会话第一手;本会话**未复跑**,读码核实 `writelimit.py:141/:225` 在位):step1 被 `ProtectionError` 拦、step2 `verdict='MERGE' converged={'ok': True, 'merge_count': 1}`、目标正文末行被追加、`_protected_history` 不存在;普通 contextual 目标的 CONVERGE 可达性另测通过。
133
+ - 归属面 md_cg;复测轮数:本轮首报。
134
+
135
+ **N216(high,留档)** 发布件门禁的「发布清单」来源可被 `package.json` 自身生命周期脚本 stdout 冒充
136
+ - where:`scripts/check_publish_artifact.py:269 extract_first_json_value`(取「首个可解析 JSON 值」)→ `:332`(`data0.get("files")` 直接采信);消费方 `package.json:87 "gate"` / `:88 "prepublishOnly"`、`.github/workflows/publish-artifact-check.yml`(`on.pull_request.paths` 含 `package.json`)。
137
+ - 缺陷面:`npm pack` 会先跑 `prepare`/`prepack`,**其 stdout 排在真清单之前**——一段 `prepare` 即可让 R1/R2/R3/R4 四档**只扫这个伪造清单**,真发布件里的凭据文件不再被检查,门禁打印 `VERDICT=PASS`、exit 0(为容忍 prepare 文本污染而引入的健壮解析契约,把「文本污染」升级成「清单可被 prepare 完全替换」)。
138
+ - 攻击面:提交一个改 `package.json` 的 PR——把 `prepare`(或 `prepack`)写成一行打印 `{"files":[{"path":"package.json"}]}`(只列已追踪件以同时骗过 R4);CI 路径过滤含 `package.json`(改动即触发),本地发版 `prepublishOnly` 也调本脚本;门禁判绿后 `npm publish` 把真凭据随公开包发出。
139
+ - 证据(复核会话第一手,临时 git 仓 A/B;本会话**未复跑**,读码核实 `:269`/`:332` 两处在位):诱饵仓原始 stdout(`npm pack --dry-run --json`)首行即 `prepare` 的伪造清单 → 「发布清单:…文件数=1」、R1–R4 全 PASS、EXIT=0;同参对照仓(唯一差异 = 无 `prepare`)→ 「文件数=2」、`[FAIL] R1 凭据/密钥面 命中=2`、EXIT=1;解析环节直调 `extract_first_json_value` 返回诱饵对象;既有守卫 `scripts/test_check_publish_artifact_scan.py` 实跑 9 passed、`--selftest` 无该负例。**unfixed**(HEAD `6a1d7823`,v23 报告 grep `prepare`/`extract_first_json_value` 零命中;本会话复核仍无覆盖)。
140
+ - 归属面 scripts_ts;复测轮数:本轮首报。
141
+
142
+ **N217(medium,留档)** 三件门禁脚本对「合法 JSON 但顶层非对象」无类型闸 + 退出码语义错位
143
+ - where:`scripts/check_registry_tarball.py:42`(`json.load(fh)["version"]`,`:76` 只捕 OSError/ValueError/KeyError);`scripts/check_publish_artifact.py:465`(`pack.get`)、`:474`(`vdoc.get`);`scripts/interop_gate.py:32`(`k not in v`)。
144
+ - 缺陷面:`TypeError`/`AttributeError` 逃出 `main` → 裸 traceback,且退出码落到与本脚本契约**相反**的一侧(`check_registry_tarball` 头注承诺「读取失败=2」实际得 1=版本不一致;`check_publish_artifact` 承诺「环境错误=2」实际得 1=规则命中;`interop_gate` 文档承诺「形状非法=3」实际得 1=非放行态)。三例仍 **fail-closed**(非零即拦),属异常族缺口 + 退出码语义错位,**非放行绕过**。
145
+ - 攻击面:`package.json`/`verdict.json` 被写坏成「合法非对象 JSON」(`[]` / `123` / `"str"`),或 registry 侧(私有镜像/被劫持响应)返回顶层非对象 packument/version 文档 → 门禁以 traceback 崩掉,把「读不通」伪装成「语义判负」,排障方向被引偏。
146
+ - 证据(复核会话第一手逐例实测;本会话**未复跑**,读码核实三处 where 在位):`check_registry_tarball --package <[1,2,3]>` → `TypeError … EXIT=1`(`123`/`"0.6.0"`/`null` 同族);`check_publish_artifact --root <package.json=[1,2,3]>` → `AttributeError … EXIT=1`(**局部修正**:`null` 走 `pj is None` 分支正确 EXIT=2、不崩——崩值族为 list/int/str/bool,其余族成立故仍判 confirmed);registry 模式本地 HTTP 回 `[]` → `:465` 崩 EXIT=1,版本端点回 `[]` → `:474` 崩;`interop_gate --path v.json=123` → `TypeError … EXIT=1`,而 `[]`/`"str"`/`{}` 正确 `形状非法` EXIT=3(只差「非容器」这一族);既有守卫 `scripts/test_registry_tarball.py` 实跑 ALL PASS,未覆盖非对象形态。
147
+ - **原轮次**:v23(第 24 轮)第五节第 25 项以「范围外观察·未修未编号」登记;本轮复核确认仍在位并**新增两条腿**(`check_publish_artifact` 与 `interop_gate`),本报告为可追踪性**正式编号 N217**。归属面 scripts_ts;复测轮数:1(第 25 轮)。
148
+ - 边界:v23 材料曾写 `hive/…exec_empty_response` 与 `hive/wm…` 处同类崩点,本轮**未复跑**(不在本轮四区范围内),如需一并处置建议另开 ask。
149
+
150
+ **N218(medium,留档)** `dsh_log_index` 对非对象 JSON 崩点中断整轮摄取(v23-N206 的日志脚本消费面相邻入口)
151
+ - where:`scripts/dsh_log_index.py:249 parse_session_log`(`:290 t = o.get("type")`、`:302 mid = d.get("id")`、`:318 it.get("source")`);反证同文件已有 `json_error` 计数与 unknown 事件 fail-closed,独漏该族。
152
+ - 缺陷面:`json.loads` 成功但顶层非对象(`null`/`[]`/`123`/`"x"`)或 `data` 非对象时直接 `.get` → `AttributeError` 逃出 `main`,**整轮摄取中断**:已落盘节点与转写保留、其余会话不再处理,且 stderr 无任何显式告警,用户只见 traceback。
153
+ - 攻击面:被消费的会话日志(外部 DSH 写入、本工具只读)出现一行 `null`,或 `{"type":"user/message","data":[...]}` 这种 data 非对象形态(损坏/截断残留、未来版本事件形态、或任何能写该日志的一方);触发后本轮回合之后的会话全部不再摄取。
154
+ - 证据(复核会话第一手,临时合成 zstd 日志 + 临时会话根;本会话**未复跑**,读码核实 `:290/:302/:318` 在位):`parse_session_log` 直调 s-null → `AttributeError: 'NoneType' object has no attribute 'get'`、s-listdata → `'list' object has no attribute 'get'`;全 CLI 实跑 `--dry-run` → 首会话产物已出后 traceback 落 `line 302 / mid = d.get("id")`、EXIT=1、后续会话未处理、stderr 除栈无 WARNING;既有守卫 `scripts/test_dsh_log_index_token.py` 实跑 23 passed、对 null/非对象零覆盖。
155
+ - 归属面 scripts_ts;复测轮数:本轮首报(同族缺口:v23-N206 是 md_cg stdio 主循环,hive 侧已有兜底)。
156
+
157
+ **N219(medium,留档)** `src/bridge.ts` stdout 行处理器未校验 `JSON.parse` 结果类型——一行 `null` 杀宿主(DSH)进程
158
+ - where:`src/bridge.ts:235`(`JSON.parse`)→ `:240`(`msg['id']`);构建产物 `lib/bridge.js` 同款(实跑即用该产物)。
159
+ - 缺陷面:`:220` 的 `proc.stdin 'error'` 监听是 N119 的加固,**同一 spawn 段里的 readline `line` 处理器仍无 try**——一行 `null` → `msg['id']` 抛 `TypeError`,抛出点在流事件回调内且无任何 try → **未捕获异常直接终结宿主进程**;紧邻的「非 JSON」分支已挡并打印,独漏「合法 JSON 但为 null」(防御写成了一半)。与 N206 成对:**N206 杀 server,本条杀宿主进程本身**。
160
+ - 攻击面:子进程(md_cg 大脑)stdout 出现一行 `null`(大脑崩溃残留、被第三方交织写入管道、新版打印)即等价「一行杀宿主」;每行都在监听,无需凭据。
161
+ - 证据(复核会话第一手,真实构建产物 + 假 python;本会话**未复跑**,读码核实 `:235/:240` 在位):探针在 `uncaughtException` 钩子里主动退出 → `HOST-UNCAUGHT-EXCEPTION: TypeError Cannot read properties of null (reading 'id')`、退出码 42;类型面边界实测——`123`/`"x"`/`[]`/`true`/`{}` 五形态 → 取 `msg['id']` 均为 undefined、无异常(**触发值窄,仅 null 确定**);既有守卫 `test/bridge.test.ts` 对 null/JSON.parse/畸形零覆盖。
162
+ - 归属面 scripts_ts;复测轮数:本轮首报。
163
+
164
+ **N220(medium,留档)** `migrate_restricted --ids` 绕过脚本自陈判据 + 恒定 `override=True` 使层保护闸失效
165
+ - where:`scripts/migrate_restricted.py:47`(`--ids` 直取清单)+ `:53-58`(循环内只判 `cg.get` 真值、**不复查 sensitivity**)+ `:85`(恒定 `override=True`);同文件排除表(9 键:id/layer/sensitivity/state/lifecycle_state/verification_state/bucket/bucket_zh/writer)**不含 `tags`/`session`**(v23-N202 条款,本轮读码逐字复核未变化)。
166
+ - 缺陷面:任何**可读**节点被命名即被改写成 restricted,且恒定 `override=True` 使 `protect.guard_write` 的层保护闸(anchor/self 不可篡改层)失效——直调 `cg.add` 会被拒的节点在这里被无条件改写;脚本还把它计入「扫描到 private 节点 N 个」并回显其正文前 40 字。排除表缺 `tags`/`session` ⇒ 存量 private 会话要点迁 restricted 后仍带 `session:*` 落回 `session_recall` 面(与 N211 的读出口相邻)。
167
+ - 攻击面:运维误传/外部工具生成的 id 清单(或误解 `--ids` 语义)一次 dry-run→apply 即改写锚层节点;留痕仅 override 审计快照。非远程可达,属「写面入口缺结构/语义判据」族。
168
+ - 证据(复核会话第一手,临时库 + 哑 Principal;本会话**未复跑**,读码核实 `:47/:85` 与排除表在位):对照 `cg.add("anchor_node", …, layer="anchor")`(无 override)→ `ProtectionError:节点 … 受写保护(层保护:anchor(不可篡改层))`;`migrate_restricted --root <临时库> --apply --ids anchor_node,priv_node` → rc=0 后 anchor_node `sensitivity=internal→restricted` 且 `layer=anchor`(保护层节点被改写成功);dry-run 回显锚层正文前 40 字;迁移后 priv_node 仍带 `tags=['incident','session:sess-xyz']` 与 session 记录;既有守卫 `scripts/test_migrate_restricted.py` 实跑 15 通过、未覆盖 `--ids` 指向非 private/保护层目标。
169
+ - 归属面 scripts_ts;复测轮数:本轮首报(排除表面 = v23-N202 条款复测第 1 轮,仍成立)。
170
+
171
+ ### 3.2 legacy 复核(12 号:已修 5 / 留档 6 + N217 见 3.1)
172
+
173
+ 复测轮数按缺陷号在 `docs/eval/缺陷挖掘_自主迭代_v*.md` 现身文件数 grep 计(本会话实跑):`N197`/`N199`–`N208` **均只在 v23 现身**(v23 = 第 24 轮首编号)⇒ 本轮为**第 1 次复测**;`N217` 对应 v23 第五节的未编号登记项,同为本轮第 1 次复测。
174
+
175
+ | 编号 | 原轮次 | 复测轮数 | 本轮结论 | where(现码行号,本会话读码核实) |
176
+ |---|---|---|---|---|
177
+ | N206 | 第 24 轮(v23 首编号,次轮首补候选) | 1 | **已修** → 见 2-① | `md_cg/mcp_server.py:3636` |
178
+ | N202 | 第 24 轮(次轮首补候选) | 1 | **已修** → 见 2-② | `md_cg/mdcos.py:2548/:2571` |
179
+ | N199 | 第 24 轮(次轮首补候选) | 1 | **已修** → 见 2-③ | `md_cg/tokens.py:294/:361` |
180
+ | N197 | 第 24 轮 | 1 | **已修**(写面接线) → 见 2-④ | `md_cg/insight.py:304` |
181
+ | N208 | 第 24 轮 | 1 | **已修** → 见 2-④ | `md_cg/ccgc.py:835/:925` |
182
+ | N201 | 第 24 轮 | 1 | **仍成立·留档** | `md_cg/mdcos.py:1746`(propose 的 `rec` content 原样)/`:1948`(`review_list` 只按终态过滤)/`md_cg/mcp_server.py:3067`/`:3241`(仅映射 `read`) |
183
+ | N204 | 第 24 轮 | 1 | **仍成立·留档** | `md_cg/docindex.py:192`(父摘要先算满 `MAX_SUMMARY=200`)→ `:195-199`(并入子节摘要后整体截断) |
184
+ | N205 | 第 24 轮 | 1 | **仍成立·留档** | `md_cg/trust.py:934 describe`(回落索引条目仍 `ok=True`)/`:975 load_ledger`(仅按 `node_id` 相等过滤,**无 `_readable`**,本会话读码逐字确认)/`md_cg/mcp_server.py:1814/:1986` |
185
+ | N207 | 第 24 轮 | 1 | **仍成立·留档** | `src/cli.ts:46-57`/`src/init.ts:342-353`(`isMainEntry` 无 realpath 归一;本会话实跑 `src/`+`lib/` grep `realpathSync` **零命中**) |
186
+ | N200 | 第 24 轮(大设计决策) | 1 | **仍成立·留档(裁决建议见第五节)** | `hive/src/exec.rs:85-86/:134-135/:137`、`hive/exec.py:1325/:1329-1331`、`hive/src/keyres.rs:6/:14/:35`、唯一 spawn 点 `hive/src/scheduler.rs:454` |
187
+ | N203 | 第 24 轮(大设计决策) | 1 | **仍成立·留档(裁决建议见第五节)** | `scripts/dsh_log_index.py:478/:491-493`(token 派生)/`:551-553`(仅 `ext_token≠token` 才切)/`:565-567`(位置化 id + 已存在即 skip) |
188
+
189
+ **N201 补充(复核会话第一手证据,本会话未复跑)**:designer `propose(sensitivity='private')` 后 guest 令牌 `mdcg_review_list` 命中正文明文;`hippocampus/inbox.jsonl` 记录键无任何封套/密文字段(落库节点正文已加密而 inbox 仍明文);对照 guest `require_op('review')` 被拒,说明其只可能经 `read` 映射的 `mdcg_review_list` 命中。**N204 补充**:合成样本直调 `docindex.extract` → [A 顶层父节直接正文 440 字 + 小子节] 节点数=1、任何节点摘要含子节独有文本=False;[B 父节无直接正文]/[C 父摘要未满] 均 True ⇒ 失效条件确为「父摘要占满 200 字」。**N205 补充**:guest `get=None` 但 `op=status` → `ok=True verification_state=verified depends_on=[…]`;`action=ledger` → `reason='外部裁决 confirmed:…'`(证据串明文)。**N207 补充**:win32 + Node 实测经 junction 跑 `init` → EXIT=0、stdout/stderr 0 字节、snippet 未落盘;直跑同参 → stdout 输出片段并落盘 snippet(**POSIX `.bin` 符号链接与 npx 实链路本机不可执行,未实测,依 Node 主入口 realpath 语义推得**)。
190
+
191
+ ### 3.3 证伪·剔除(3 项,全部出自 writegate 区)
192
+
193
+ | # | 被证伪的候选 | 证伪理由(复核会话第一手实测) |
194
+ |---|---|---|
195
+ | 1 | N202 候选的 attack_vector:「任何持 read 的令牌(guest…)调 `cg(op=session, action=recall)`」 | **触发身份错**:cg 分发先 `require_op(op)`,guest 的 `ops_allow` 无 `session`,实测 `guest require_op('session') → AccessDenied / op('session')=False`(逐角色扫描:record/reflect/output/sustain 可达,guest 与 verify 不可达)。该条的**泄漏本身成立**(已按可达身份修正并列为 N202 confirmed),只是攻击面描述错。 |
196
+ | 2 | N197 候选的 problem/attack_vector:「自证即自置保护位,此后该节点**任一覆写**都需 override」 | **后果措辞证伪**:`protect.is_immutable` 只认 self/anchor 层与 `frontmatter.immutable` 标记(`md_cg/protect.py:118-134`),**不认 `protected` 位**;insight 节点落 contextual 层且无 immutable,实测 `guard_write` 放行、事后 `add(该节点)` 未被拦。自置保护位的**真实后果是「不可遗忘」**(`guard_forget` 被拦,删除需 override)——措辞已按实测收紧。 |
197
+ | 3 | N211 候选的论证「`writelimit.converge_into` 构成**层闸**绕过」 | **层维度证伪**:`writelimit.check` 仅在 `layer=='contextual'` 时进入限流/聚合判定(`md_cg/writelimit.py:141`),故 CONVERGE 目标必为 contextual 层节点,record 令牌本就有 contextual 写权——该支路绕过的只是 `protect.guard_write` 的 **immutable 维度**(已单独确认为 N215,low),不构成层闸绕过。同类实测:`remember_gated(hint=0.8)` 走 ACCEPT 不触发 check。 |
198
+
199
+ ### 3.4 v23 留档清账(本轮闭合 / 仍欠)
200
+
201
+ - **本轮闭合 5 号**:N206、N202、N199、N197、N208(v23 第五节第 4/5/6/7/8 项)。
202
+ - **仍欠**:N200(第 1 项,大设计决策)、N203(第 3 项,大设计决策)、N201(第 9 项)、N204(第 10 项)、N205(第 11 项)、N207(第 12 项)、N217 = v23 第 25 项(本轮正式编号)。
203
+ - **v23 第 23 项(cogmap FUNCMAP 行号漂移)已闭合**:本会话实跑 `python -X utf8 scripts/cogmap_sync.py check` → **通过**(cg 36 op / stg 4 op / whitebox 6 action / mdcg_* 31 个;双文档标记段、op/工具引用、文件链接、锚点、FUNC_DESC 覆盖全部一致),且本轮 `md_cg/mcp_server.py` 净增 100+ 行后锚零漂移(新增函数未改既有 op/marker 段)。
204
+ - **v23 第 24 项(历轮编号字样残留)**:v22 第 18 项(`hive/src/exec.rs` / `keyres.rs` 的「N185」按 v22 裁定应读作 N190)本轮**扫描仍在位**,未代改;本轮新增两条编号残留(见 3.0 末)。
205
+
206
+ ---
207
+
208
+ ## 四、验证(门禁)
209
+
210
+ 以下全部为**本报告会话第一手实跑**(命令与结果原样):
211
+
212
+ 1. **工作区索引与定位**:读 `WORKSPACE_INDEX.md`(顶层目录职责表 + 关键入口)后按 `md_cg/`(mcp_server.py · mdcg.py · mdcos.py)、`scripts/`、`src/`、`hive/` 定位,未做全盘浏览。
213
+ 2. **5 件新守卫逐件复跑**(`python -X utf8 -m <module>`):`md_cg.test_n206_stdio_jsonrpc_type` → **33 passed, 0 failed (live=15)**;`md_cg.test_n202_session_notes_visibility` → **36 passed, 0 failed (live=16)**;`md_cg.test_n199_tokens_concurrent_write` → **21 passed, 0 failed (live=12)**;`md_cg.test_n197_n208_write_face_gates` → **28 passed, 0 failed (live=20)**;`md_cg.test_n209_verify_write_face_gates` → **`[RESULT] 23 passed / 0 failed · live=16`**。合计 **141 断言全绿**、各件 rc=0。
214
+ 3. **运行器发现性**(防「守卫未被套件发现」的假绿):`python -X utf8 scripts/run_tests.py -k n199|n202|n206|n209|n197_n208` 逐条 → 各 `PASS md_cg.test_*`、`SUMMARY 1/1 通过`。
215
+ 4. **全量回归**:`python -X utf8 scripts/run_tests.py --jobs 4` → **`===== SUMMARY 237/237 通过,5 跳过(依赖缺失/平台不符) =====`、退出码 0**(较第 24 轮 234 增 3;另 2 件新守卫计入既有计数面)。
216
+ 5. **`npm run gate`**:`cmd /c npm run gate` → 六步链式:`check_unreachable` 「450 文件,命中 0,语法失败 0」→ `check_publish_artifact` 「文件数=1437」「[PASS] R1 凭据/密钥面;文件面=0 内容面=0」「[PASS] R2 私有数据面」「[PASS] R3 隐私文本」「**[FAIL] R4 非追踪件面 命中=5;非追踪=33,允许=28**」→ `=== 结论 === FAIL 规则数=1`、**`VERDICT=FAIL`、退出码 1**。
217
+ - **FAIL 归因(本会话实测)**:`&&` 链在 `check_publish_artifact` 处以非零退出码**短路**,**故 `cogmap_sync check` / `link_check` / `workspace_index --check` / `verify_discipline` 四步在本次 gate 实跑中并未执行**。R4 命中的 5 条为**本轮新守卫尚未 `git add`**(`test_n197_n208_write_face_gates.py` / `test_n199_tokens_concurrent_write.py` / `test_n202_session_notes_visibility.py` / `test_n206_stdio_jsonrpc_type.py` / `test_n209_verify_write_face_gates.py`)——**属时序性(未追踪新件),非真实规则红**;内容面 R1/R2/R3 全 PASS(R1 仅 6 条 `[NOTE]` 文本命中,均为既有测试/文档中的**哑值样例**,非新引入)。落盘本报告后 R4 的未追踪数将再 +1(34/28),提交后即消失(判据同 v21/v22/v23 先例)。
218
+ 6. **短路后四检查器逐条补跑(本会话实跑,全绿)**:`python -X utf8 scripts/cogmap_sync.py check` → `通过(…)`、rc=0;`python -X utf8 scripts/link_check.py` → `OK=228 ALLOWED=0 BROKEN=0 UNTRACKED=0`、rc=0;`python -X utf8 scripts/workspace_index.py --check` → `[OK] 工作区索引与仓库一致(顶层目录 20 · 追踪 2021 件)`、rc=0;`python -X utf8 scripts/verify_discipline.py --allow-missing` → `结论:9/9 目标一致`、rc=0。
219
+ 7. **cargo:未运行**(本轮 Rust 面无任何改动——`git status` 与 `git diff --stat` 均无 `.rs`/`hive/` 条目;材料记「未运行(Rust 面无改动)」,本会话**未实跑**,如实标注)。**hive 面零改动**同理(改动集仅 8 个 `md_cg/*.py` + 5 个新守卫)。
220
+ 8. **读码核实(本会话第一手,逐处 `grep -n`/`sed -n` 定位;共 21 组)**:`md_cg/mdcg.py:1023/:1038/:1508/:1868/:3386/:3403/:3405/:3439/:3511`;`md_cg/mdcos.py:308/:2548/:2571/:2586/:2652/:3302/:3332/:3336/:3377/:3380/:3935/:3953/:3957/:3984/:4098/:4108/:4110`;`md_cg/protect.py:63/:78/:194/:210/:222/:236/:263/:286`;`md_cg/trust.py:680/:703/:711/:733/:934/:975`;`md_cg/insight.py:291/:293/:304/:306`;`md_cg/ccgc.py:77/:192/:198/:510/:547/:616/:741/:835/:838/:859/:925/:927`;`md_cg/forgetting.py:60/:247/:266/:274`;`md_cg/writelimit.py:141/:211/:225`;`md_cg/tokens.py:39/:159-176/:294/:361/:386/:436/:555/:565-577/:660/:667/:724`;`md_cg/mcp_server.py:1814/:1986/:2241/:2587/:2816/:2836/:2999/:3067/:3241/:3636/:3655/:3679/:3708/:3714/:3876`;`md_cg/docindex.py:40-42/:186/:190/:192/:195-199`;`scripts/check_publish_artifact.py:256/:269/:332/:465/:474/:662`;`scripts/check_registry_tarball.py:42/:93`;`scripts/interop_gate.py:32/:43`;`scripts/dsh_log_index.py:249/:290/:302/:318/:478/:491-493/:551-553/:565-567`;`scripts/migrate_restricted.py:27/:47/:85`;`src/cli.ts:46-58`;`src/init.ts:342-353`;`src/bridge.ts:235/:240-241`;`hive/src/exec.rs:85-86/:134-135/:137/:257/:264-265`;`hive/exec.py:18/:1325/:1329-1331`;`hive/src/keyres.rs:6/:14/:35`;`hive/src/scheduler.rs:454`。**行号均为现工作区(含本轮修复)行号**;材料中的旧行号已因修复偏移,本报告一律改用现码行号并在此说明。
221
+ 9. **本报告自身检查**:隐私/路径门禁(与编排管线的同一判据)本地自查——不写本机绝对路径、不含真实凭据/PII 形态串(见第六节-5、附录)。
222
+
223
+ ---
224
+
225
+ ## 五、遗留清单(15 项 + 2 项大设计决策裁决建议)
226
+
227
+ | # | 编号 | where | sev | 原轮次/复测 | 处置去向与建议 |
228
+ |---|---|---|---|---|---|
229
+ | 1 | **N212** | `md_cg/mdcg.py:1023` × `:1508` | medium | 本轮首报 | **与 N213 同根(签名口径),建议同批修**:`_index_signature` 纳入 `_index_log` 分片(最新分片 mtime/size 或分片数 + 尾部偏移),使存活未 compact 的写方可见;守卫须补「B 不 close 只 flush」的腿(既有两件守卫都用 `b.close()`)。修复风险:`_maybe_reload_index` 在读面高频调用,需保证签名比较仍是「一次 stat 级」成本(可分片目录 mtime + 已有快照签名两级) |
230
+ | 2 | **N213** | `md_cg/mdcos.py:4098/:4110` × `:3984` × `md_cg/protect.py:78` | medium | 本轮首报 | 上述签名口径修好后,本条的两个面(陈旧条目的 sensitivity/session、`_fm` 回读判据)**是否仍需独立修**须实测复核:`_readable` 应改为「entry 与盘面签名不一致即回读 frontmatter 的 sensitivity/session」;`protect._fm` 的 `need_fallback` 换成「entry 无 `_gate_sig` 或签名陈旧」而非「缺键」(当前判据对恒落键的 `_node_entry` 恒假) |
231
+ | 3 | **N214** | `md_cg/mdcos.py:3377` × `md_cg/forgetting.py:247/:274` | **high** | 本轮首报 | **建议优先(本轮留档中的最高危害:越层覆写 + 自授保护位 + 无痕)**:`reinforce` 写点前接 `protect.guard_overwrite`(层闸 + 保护闸,复用既有单点);与 N215 同批(同一 `remember_gated` 的两个替代落盘分支)。反向腿须保「同层身份 MERGE 仍生效、合法强化不被掐死」 |
232
+ | 4 | **N215** | `md_cg/mdcos.py:3332/:3336` × `md_cg/writelimit.py:211/:225` | low | 本轮首报 | 与 N214 同批:`converge_into` 写点前接 `protect.guard_write`(层维度不构成绕过,只补 immutable 维度);前置形态(人工 `immutable:true` 标记的 contextual 节点)全仓无写入方,故 low |
233
+ | 5 | **N216** | `scripts/check_publish_artifact.py:269/:332` | **high** | 本轮首报 | 解析契约收紧:不用「首个可解析 JSON 值」,改为「取最后一段可解析 JSON **数组**(`npm pack --json` 的真清单形态)」或给 `npm pack` 传 `--ignore-scripts`(若 npm 版本支持)/显式剥掉 `prepare`/`prepack` 的 stdout;并加负例守卫(`--selftest` 补「前置 JSON 诱饵」用例)。**发版链路影响面大,建议单列一个修复批次** |
234
+ | 6 | **N217** | `scripts/check_registry_tarball.py:42` × `check_publish_artifact.py:465/:474` × `interop_gate.py:32` | medium | 第 24 轮未编号登记,复测 1 | 三件门禁脚本统一加「顶层须为对象」类型闸 + 退出码回契约值(读不通=2 / 形状非法=3);fail-closed 已成立,属**可观测性与排障面**修,随低危批次 |
235
+ | 7 | **N218** | `scripts/dsh_log_index.py:249/:290/:302/:318` | medium | 本轮首报 | `parse_session_log` 对非对象行按 `json_error`/unknown 事件同款 fail-closed 记账后跳过(不中断整轮摄取)+ stderr 显式告警;守卫补 null/非对象 data 样本 |
236
+ | 8 | **N219** | `src/bridge.ts:235/:240` | medium | 本轮首报 | `JSON.parse` 结果加 `typeof msg === 'object' && msg !== null` 类型闸(与紧邻「非 JSON」分支合并处理);`lib/` 构建产物同步(随 `npm run build`);守卫补一行 `null` 的假 python 用例(现有 `test/bridge.test.ts` 零覆盖) |
237
+ | 9 | **N220** | `scripts/migrate_restricted.py:47/:53-58/:85` | medium | 本轮首报(排除表面 = v23-N202 条款复测 1) | ①`--ids` 入口复查目标 `sensitivity` 与层(非 private/保护层目标拒改并显式报出);②`override=True` 改为「仅在目标确为 private 档且用户显式 `--force` 时置位」;③排除表补 `tags`/`session`(与前一条 N211 读出口相邻,建议同批) |
238
+ | 10 | **N201** | `md_cg/mdcos.py:1746/:1948` × `md_cg/mcp_server.py:3067/:3241` | high | 第 24 轮,复测 1 | inbox 记录加可见性过滤(或落盘即封套)+ `review_list` 过 `_readable`;与「提案落地策略」一并定(材料口径同 v23) |
239
+ | 11 | **N204** | `md_cg/docindex.py:192-199` | medium | 第 24 轮,复测 1 | 低风险机械修:合并段**先截断父摘要再拼接**(或对 `merged` 单独限长)+ `small` 子节记账;随低危批次 |
240
+ | 12 | **N205** | `md_cg/trust.py:934/:975` × `md_cg/mcp_server.py:1814/:1986` | medium | 第 24 轮,复测 1 | `describe`/`load_ledger` 出口统一过 `_readable`(与 get/search 同口径);随低危批次 |
241
+ | 13 | **N207** | `src/cli.ts:46-57` × `src/init.ts:342-353` | medium | 第 24 轮,复测 1 | `realpathSync` 双向归一 + 一条 junction/软链形态守卫;随低危批次(POSIX 实链路仍属未实测腿) |
242
+ | 14 | **N200** | `hive/src/exec.rs:85-86/:134-135/:137` × `hive/exec.py:1325/:1329-1331` × `keyres.rs:6/:14/:35` | **high** | 第 24 轮,复测 1(仅读码) | **大设计决策 → 见下方 5.1 裁决建议** |
243
+ | 15 | **N203** | `scripts/dsh_log_index.py:478/:551-553/:565-567` | **high** | 第 24 轮,复测 1 | **大设计决策 → 见下方 5.2 裁决建议** |
244
+
245
+ ### 5.1 N200 裁决建议(一变量双角色:锚密钥链尾 × LLM 网关密钥)——**只出建议,本报告不改码**
246
+
247
+ **事实面(读码核实,本会话逐处定位;动态 A/B 未复跑)**:`hive/src/exec.rs:85-86` 常量 `ORCH_SECRET_ENV_KEYS` 三键含 `HIVE_API_KEY`;`:134-135` 对三键**无条件** `env_remove`;`:137` 起**仅** `spec_orchestrate` 为真时重注 `HIVE_ORCH_TOKEN`/`HIVE_ORCH_TOKEN_FILE`,**`HIVE_API_KEY` 永不重注**;`exec.rs:257` 与 `:264-265`/`:294` 三条既有断言已把「`HIVE_API_KEY` 不在执行器 env」**固化为契约**;`hive/exec.py:1325/:1329-1331` 的 `_post_chat` 读不到该键即 `RuntimeError`(`exec.py:18` 头注亦声明「必填,缺失即 fail」);`hive/src/keyres.rs:6/:14/:35` 锚密钥链尾即该键;serve 侧唯一 spawn 点为 `hive/src/scheduler.rs:454`。⇒ **标准部署下 serve 派发的全部 LLM 任务(直路与编排路复用同一 `_post_chat`)在首次 API 调用前确定性落 error,主能力全断**(无需攻击者)。
248
+
249
+ **可选方案与推荐**:
250
+ - **方案 A(拆分变量)— 推荐**:为执行器引入**专用网关密钥**(如 `HIVE_LLM_API_KEY`),由 serve 侧供给、随 LLM 任务 spec **条件注入**(注入面与 `spec_orchestrate` 同款条件形态);`exec.py` 侧改为「优先读专用键、缺失回落原键」(回落仅为本地直跑/单机形态的兼容面)。**三键剥离面不变、`keyres` 锚链尾不变、`exec.rs:257` 契约零改动**;风险是新增一把密钥的运维面(需在 `hive/config.local.example.json` 与 README 明写供方)。
251
+ - **方案 B(按需重注原键)**:在 LLM 任务(含 `spec.orchestrate`)显式重注 `HIVE_API_KEY`,并**同步改** `exec.rs:257/:264-265/:294` 三处守卫与 README 契约。优点:零新变量、改动集中;缺点:**改契约**,且「执行器不持锚密钥」的不可伪造性论证被削弱(LLM 任务与编排任务同池执行,剥离面从此按任务类型而非进程类型)。
252
+ - **方案 C(短时作用域凭据)**:serve 每次 LLM 任务签发短时、限用途网关凭据——最严但改动最大(需网关侧支持多凭据/限流与吊销),本轮不建议。
253
+
254
+ **推荐 A,须用户拍板的三个点**:① 新变量的命名与供给方(serve env 供 / 配置文件供);② 是否保留「专用键缺失回落原键」的兼容面(保则本地直跑无感,弃则契约更纯但本地形态需同步改);③ 注入判据用「任务类型字段」还是「spec 中显式声明需要 LLM」——后者更精确、可审计,建议随 A 一并落地。
255
+
256
+ ### 5.2 N203 裁决建议(位置化 id 契约变更与既有节点迁移)——**只出建议,本报告不改码**
257
+
258
+ **事实面**:`scripts/dsh_log_index.py:478/:491-493` token = `sha256(createdAt毫秒 | 首条 user 全文)[:16]`;`:551-553` 仅在既有节点指向**别会话**(`ext_token != token`)时才切兜底标识;`:565-567` 节点 id = `dsh-log-{token}-{i:04d}`(抽取序位)+ 「已存在即 skip」。日志中途插话 ⇒ createdAt 与首条 user 未变 ⇒ token 不变 ⇒ 碰撞核验整段跳过 ⇒ **新正文落已占用 id 被静默跳过(永久丢索引)+ 尾部旧正文重复落节点**,跑批 rc=0、stderr 无告警(合成动态复现:V1 = 21 章/21 节点;V2 插一条 → 22 章/1 新/21 skip,插话正文在全部节点零命中)。
259
+
260
+ **可选方案与推荐**:
261
+ - **方案 A(id 增内容因子 + 位置化兼容读 + 显式记账)— 推荐目标态**:会话空间 token 保留原口径(createdAt + 首条 user,**避免「日志追加=新会话空间」引发全量重编号**),把「位置化序位」替换为**该章节正文内容哈希前 8 位**(`dsh-log-<token>-<内容哈希8>`);新增节点因此天然幂等且不依赖序位。存量位置化节点保留可读(旧 id 仍在盘上、检索照常命中),仅新增章节走新 id ⇒ **无迁移、无重写、可回滚**;跑批统计里显式输出「检测到同一 token 下的序位漂移」条目数(含被跳过 id 与新正文摘要),把静默变成可观测。
262
+ - **方案 B(一次性迁移存量节点)**:同 A 的 id 口径 + 对存量位置化节点做一次性重命名/重写(幂等 + 备份 + 回滚脚本)。收益:索引面统一;成本/风险:历史库节点量级大,迁移本身的幂等与中断恢复需要独立守卫。
263
+ - **方案 C(仅止血,不改 id)**:保留位置化 id,但在检测到「同 token 同序位正文哈希与既有节点不一致」时**显式报错/非零退出**(fail-loud)而非静默 skip,并在统计里记账。最小改动,消除静默丢索引,但检索面仍缺新内容。**建议随低危批次先落 C。**
264
+
265
+ **推荐路径**:**先 C(可观测止血)→ 再 A(契约变更:内容因子 id + 兼容读 + 漂移记账)**;B 仅在确认必须清理历史节点时执行(且必须先出迁移/回滚脚本与中断恢复守卫)。**须用户拍板的三个点**:① 章节 id 的稳定因子取「正文哈希」还是「正文 + 角色 + 序位」组合(后者在正文不变而序位变时仍会漂移);② 是否保留位置化旧 id 的兼容读(保则检索面双 id 并存,弃则需 B);③ 跑批在检测到漂移时是**记账继续**还是**非零退出**(前者可用性优先、后者正确性优先)。
266
+
267
+ ---
268
+
269
+ ## 六、纪律合规声明
270
+
271
+ 1. **复现环境红线**:四区攻防复现与修复验证全部在系统临时目录/临时库/哑令牌/哑密钥环境进行(哑主密钥 32 字节常量、哑令牌、临时会话根与包根,绝不出网)——**未触真实令牌库、真实在役 serve、真实在役数据目录与 DSH 探针节点(`prop_*` 未动)**。本报告会话额外确认:本轮**未启动任何 MCP serve 实例**(守卫自带隔离自证,其子进程自报戳由守卫按 pid 自删)。
272
+ 2. **本报告会话第一手实跑清单**:`WORKSPACE_INDEX.md` 定位;5 件新守卫逐件复跑(第四节-2);`run_tests.py -k` 发现性抽验(第四节-3);全量 `scripts/run_tests.py --jobs 4` = 237/237、退出码 0(第四节-4);`npm run gate` 与短路后四检查器逐条补跑(第四节-5/6);21 组源码行号读码核实(第四节-8)。**未跑项**:`cargo test`(Rust 面零改动)、TS 面 `npm test`/`tsc`(`src/` 零改动)、`git commit`(编排会话负责)、报告落盘后重跑 gate(落盘会新增 1 件未追踪件,R4 计数将变为 34/28,属已知时序性)。
273
+ 3. **转述与归属**:四区攻防复现、修复红态与攻防前后对比为复核/修复会话第一手实测(材料附命令与输出),本报告如实转述并在每处标注「修复会话第一手 / 复核会话第一手」;**本会话未重复执行任何 git stash 双态操作**(避免扰动并行会话工作区)。凡本会话未跑的动态检查,文中一律标注来源,不冒充第一手结论。
274
+ 4. **编号裁定与纪律**:见 3.0——`N209` 树内并号按「守卫文件名实占优先」归 verify 三态写面,其余 11 个新号自 `N210` 续编且全局唯一(`N210`–`N214` 树内零命中经本会话 grep 核实);`N217` 为 v23 未编号登记项的本轮正式编号(并如实标注 legacy 归属与首轮复测)。报告员**未代改任何源码注释/守卫文件名/测试期望值**,编号残留清单交编排会话处置。
275
+ 5. **报告面隐私卫生**:本报告**不含真实凭据/密钥/令牌与 PII**——哑值一律以「哑主密钥/哑令牌/哑密钥」类目表述,不写本机绝对路径(盘符/用户目录/临时目录一律中性表述),不含任何形如令牌前缀 + 长随机串的可误复制串;本会话已按编排管线同一判据对落盘文件本地自查(附录-清单 A)。
276
+ 6. **门禁如实呈现**:`npm run gate` **实跑 VERDICT=FAIL、退出码 1**——本报告按原样输出逐面归因(内容面 R1/R2/R3 全 PASS;R4 非追踪件面 命中=5、点名在册,属**未追踪新件时序性**而非真实规则红;`&&` 短路未及的四检查器由本会话**逐条补跑并全绿**)。**未以「内容面合格」替代整体 VERDICT,也未把 FAIL 说成 PASS**;全量回归 237/237 与 141 条守卫断言为本会话实跑结果。
277
+ 7. **未修项如实声明**:本轮留档 15 项**均未改码**(含 2 项大设计决策只出裁决建议);N209 修复席留下的「verify 的 confirmed/weakened 仍不过 `guard_write`」与「`trust.set_state` 同态 noop 迁移口径变化」两条未覆盖面已在其条目内原样转述。修复会话披露的非本席位临时目录(其无法归属本 ask、为避免误删并行会话产物而未清理)已随材料转述,未由本会话处置。
278
+
279
+ ---
280
+
281
+ ## 七、跳过项及原因
282
+
283
+ 1. **`npm run gate` 的「入库后转绿」断言**:本会话实跑结果已全量如实呈报(第四节-5/6)。**未做**:①未在报告落盘后再跑一次 gate(落盘即新增 1 件未追踪件,实跑时点与落盘稿不同,属已知时序性);②未代编排会话断言「`git add` 后 gate 必绿」——依赖提交动作,本报告不越位执行。
284
+ 2. **修复项红态的重复执行**:报告员未重复 `git stash` 双态验证(避免扰动并行会话工作区);红态输出以修复会话材料为准,**绿态已由本会话独立复跑佐证(141 断言 / 237 全量)**。
285
+ 3. **`cargo test` / TS 面 `npm test` / `tsc`**:本轮 Rust 面与 `src/` **零改动**,故未跑(材料记「未运行(Rust 面无改动)」,本会话沿用并如实标注)。
286
+ 4. **POSIX 侧 N207 复现腿**(`.bin` 符号链接 / `npx` 实链路):本机无 POSIX Node,Windows 文件符号链接创建无权限,该腿依 Node 主入口默认 realpath 解析语义**推得,未实测**(材料与 3.2-N207 均如实声明)。
287
+ 5. **N200 的 hive/Rust 动态 A/B**:材料只做读码核实(六处逐处在位),本会话**亦只读码**(第四节-8),**未跑** hive/Rust 动态实验,故 5.1 的建议基于契约与调用点事实,不含「改了之后确实恢复」的实证。
288
+ 6. **v23 遗留的 hive/低危 10 项未复测**:`N68` / `N177` / `A1-e` / `N90` / `N91` / `N186` / `N187` / `N188` / `N192` / `N193` 不在本轮四区范围内(本轮作业面 = md_cg 核心面 + scripts/TS 面),**本轮未复测、未改码**,去向仍以 v23 第五节为准。
289
+ 7. **本轮部分动态证据未由报告会话复跑**:N212/N213/N214/N215/N216/N217/N218/N219/N220/N201/N204/N205/N207 的攻击复现输出均来自复核会话材料(临时库/临时目录/哑值),本会话只做**读码核实**(行号见第四节-8);如需重新实测,建议随其对应修复批次同批进行(避免重复消耗临时环境)。
290
+ 8. **形式化验证与全格故障注入**:不属本轮作业面,未做。
291
+
292
+ ---
293
+
294
+ ## 附录:双清单自查
295
+
296
+ **清单 A(隐私/路径/凭据面,本会话本地自查;判据 = 编排管线同一扫描:盘符路径前缀、用户目录前缀、`sk-` + 20 位以上、以及常见凭据前缀/赋值形态)**
297
+ - 结果:**0 命中**。
298
+ - 自查要点:全文无本机绝对路径(临时目录/仓内构建目录一律中性表述);无真实密钥/令牌值(哑值均为类目表述,且刻意不书写「令牌前缀 + 长随机串」形态以免被复制误用);无 PII;hash/锚值仅以「哑主密钥 32 字节常量」类目出现。
299
+
300
+ **清单 B(编号唯一性与覆盖完整性)**
301
+ - 新号 `N209`–`N220` **全局唯一**:本会话 `grep` 扫描确认 `N210`–`N214` 树内零命中;`N209` 树内并号已按 3.0 裁定归口并列出两条编号残留(未代改)。
302
+ - 覆盖完整性:四区确认 31 条目 **逐条有归口**(去重 21 项独立缺陷 → 3.1/3.2 全列);证伪 3 条 **逐条有理由**(3.3);修复 8 号 **逐条有 where/缺陷面/修复/攻防前后对比/守卫**(第二节);留档 15 项 **逐条有 where/sev/原轮次与复测轮数/去向**(第五节);2 项大设计决策 **各有可选方案 + 推荐方向 + 须拍板点**(5.1/5.2)。
303
+ - 门禁覆盖完整性:`npm run gate` 六步 **全部有本会话实跑结果或明确归属**(第四节-5/6)——其中四步因 `&&` 短路而由本会话**另行逐条实跑**,未以「gate 未跑到」为由不报。