@a9i5k4/dsh-auto-memory 3.0.0 → 3.0.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 (90) hide show
  1. package/README.md +19 -7
  2. package/README.zh-CN.md +19 -7
  3. package/docs/FRONTEND-CO-CREATION.md +191 -0
  4. package/docs/GM53-HOMEPAGE-PROMPT.md +323 -0
  5. package/docs/HOMEPAGE-CONTENT-FOR-GM53.md +299 -0
  6. package/docs/PROMO-PROMPT-3.0.md +100 -0
  7. package/docs/USER-GUIDE.en.md +2 -2
  8. package/docs/USER-GUIDE.zh-CN.md +2 -2
  9. package/docs/WHITEPAPER.md +207 -0
  10. package/docs/internal/ARCHITECTURE-FOR-ZCODE-20260920.md +397 -0
  11. package/docs/internal/ART-DIRECTION-DEEPSEEK-20260920.md +351 -0
  12. package/docs/internal/ART-DIRECTION-WIREFRAME.md +191 -181
  13. package/docs/internal/ART-DIRECTION-WIREFRAME.md.bak-superseded +181 -0
  14. package/docs/internal/BATTLE-PLAN-20260917.md +871 -0
  15. package/docs/internal/FEATURE-INVENTORY.md +531 -0
  16. package/docs/internal/G-SERIES-EXECUTION-20260917.md +248 -0
  17. package/docs/internal/G3-DESIGN-20260918.md +82 -0
  18. package/docs/internal/G3-DISK-FORMAT-GAP-20260919.md +92 -0
  19. package/docs/internal/HANDOFF-TO-ZCODE-20260920.md +309 -0
  20. package/docs/internal/HERMES-DATA-VERIFICATION-20260919.md +120 -0
  21. package/docs/internal/HERMES-LEGACY-STATUS-20260919.md +74 -0
  22. package/docs/internal/ISSUE-55-58-VERIFICATION-20260918.md +175 -0
  23. package/docs/internal/ISSUE10-FIX-EXECUTION-20260919.md +389 -0
  24. package/docs/internal/ISSUE10-PLAN-20260919.md +254 -0
  25. package/docs/internal/ISSUE10B-FORENSICS-20260919.md +468 -0
  26. package/docs/internal/ISSUE9-PURGE-AND-R1-PLAIN-20260919.md +150 -0
  27. package/docs/internal/ISSUE9-RESIDUAL-FORENSICS-20260919.md +114 -0
  28. package/docs/internal/LESSON-TO-CANDIDATE-STATUS-20260919.md +79 -0
  29. package/docs/internal/MEMORY-GOVERNANCE-20260917.md +309 -0
  30. package/docs/internal/PRE-FRONTEND-CHECKLIST-20260919.md +705 -0
  31. package/docs/internal/PRE-FRONTEND-CHECKLIST-20260919.md.bak-s10 +649 -0
  32. package/docs/internal/PROCEDURAL-MEMORY-AND-APPROVAL-DESIGN-20260918.md +225 -0
  33. package/docs/internal/PROGRESS-20260917.md +93 -0
  34. package/docs/internal/PROMPT-GAP-AUDIT-20260920.md +128 -0
  35. package/docs/internal/R1-DEGRADE-AUDIT-20260918.md +163 -0
  36. package/docs/internal/R1-READABILITY-FORENSICS-20260919.md +127 -0
  37. package/docs/internal/R2-EVIDENCE-DEEP-AUDIT-20260918.md +140 -0
  38. package/docs/internal/R3-DEGRADE-LEDGER-DESIGN-20260918.md +138 -0
  39. package/docs/internal/R4-RECALL-QUOTA-PLAN-20260918.md +218 -0
  40. package/docs/internal/RESUME-20260918.md +171 -0
  41. package/docs/internal/RESUME-20260919.md +104 -0
  42. package/docs/internal/RHINELAB-TO-DEEPSEEK-FEASIBILITY.md +198 -0
  43. package/docs/internal/ROADMAP-20260917-WEEK.md +134 -0
  44. package/docs/internal/S10-CONSTRUCTION-HANDOFF-20260917.md +13 -3
  45. package/docs/internal/S10-GAP-INVENTORY-20260917.md +239 -0
  46. package/docs/internal/T6-EXECUTION-20260920.md +130 -0
  47. package/docs/internal/TELEMETRY-EFFECT-REPORT-DESIGN-20260918.md +146 -0
  48. package/docs/internal/THESIS-GAP-ANALYSIS-20260918.md +89 -0
  49. package/docs/internal/THESIS-OUTLINE-20260918.md +147 -0
  50. package/docs/internal/THREE-LAYER-CONTRACT.md +10 -1
  51. package/docs/internal/UPSTREAM-ISSUE-PR-TRIAGE-20260919.md +297 -0
  52. package/docs/internal/UPSTREAM-ISSUES-3RD-AUDIT-20260920.md +104 -0
  53. package/docs/screenshots/promo/promo-0-banner-v3.png +0 -0
  54. package/lib/activation-host.js +63 -9
  55. package/lib/board-mode.js +1 -1
  56. package/lib/client.js +892 -27
  57. package/lib/config-io.js +156 -0
  58. package/lib/context-bridge.js +3 -0
  59. package/lib/context-host.js +16 -9
  60. package/lib/degrade.js +385 -0
  61. package/lib/dsh-home.js +143 -0
  62. package/lib/episodic-store.js +52 -2
  63. package/lib/evidence-store.js +8 -1
  64. package/lib/fact-store.js +21 -2
  65. package/lib/index-sync.js +13 -1
  66. package/lib/index.js +1507 -158
  67. package/lib/intent-clean-safe.js +258 -40
  68. package/lib/l0-extract.js +231 -16
  69. package/lib/m4-corpus.js +8 -2
  70. package/lib/m7-index-sync-host.js +8 -1
  71. package/lib/memory-envelope.js +6 -1
  72. package/lib/memory-hub.js +127 -12
  73. package/lib/memory-index.js +4 -2
  74. package/lib/note-status-apply.js +118 -0
  75. package/lib/note-status.js +196 -0
  76. package/lib/procedure-store.js +84 -3
  77. package/lib/python-sidecar-client.js +29 -3
  78. package/lib/recall-fusion.js +83 -12
  79. package/lib/rules-edit.js +159 -0
  80. package/lib/semantic-decide.js +41 -8
  81. package/lib/semantic-js.js +51 -6
  82. package/lib/shadow-host.js +3 -5
  83. package/lib/skill-export-host.js +153 -0
  84. package/lib/skill-export.js +239 -0
  85. package/lib/storage-manage.js +6 -0
  86. package/lib/temporal-parse.js +191 -159
  87. package/lib/tier0-catalog.js +45 -3
  88. package/lib/wb-contract.js +198 -2
  89. package/lib/wb-sidecar.js +54 -3
  90. package/package.json +1 -1
@@ -0,0 +1,175 @@
1
+ # 外部 issue #55–#58 核验报告(2026-09-18)
2
+
3
+ > **核验人**:执行 Agent **核验方式**:逐条对照 pre 线源码,**只采信代码证据,不采信 issue 自述**
4
+ > **基线声明**:issue 自述基线 **v2.5.2 @ a972ebf**(本机确认该提交存在,`git describe` = `v2.5.2-14-ga972ebf`)
5
+ > **报告对象**:`Minervaowl7` 于 2026-09-18 21:05–21:08 提交的 4 个 issue,来源标注「bug-hunter 自动审计(scan-only)」
6
+
7
+ ---
8
+
9
+ ## 0. 一句话结论
10
+
11
+ **4 条全部为真,且全部存在于 pre 线。** 无一误报,无一因 3.0 重建而失效。
12
+ 前 3 条(#55/#56/#57)是**同一族缺陷**:fail-soft 的 catch 吞掉了一个**本不该发生**的错误
13
+ (未定义标识符 / 缓存登记时机错 / 缺 schema 校验),把"代码写错了"伪装成"环境问题"。
14
+ 第 4 条(#58)是**纯卫生问题**(慢性泄漏),性质不同。
15
+
16
+ **但有一个关键前提必须说明**:这 4 条的**触发条件都较窄**,作者的 Low 定级是合理的(见 §3)。
17
+
18
+ ---
19
+
20
+ ## 1. 逐条核验
21
+
22
+ ### #55 · `storage-manage-pre.js` `readSidecarPrev` 引用未定义标识符 ✅ **确认为真**
23
+
24
+ | 项 | 证据 |
25
+ |---|---|
26
+ | 声明 | `readSidecarPrev` 引用不在其作用域的 `docStore` |
27
+ | 实测 | `:117 function readSidecarPrev(file) {` → `:118 try {` → `:119 if (!docStore \|\| ...) return null` |
28
+ | 作用域 | `docStore` **只在** `:135 repair()` 与 `:169 deleteMemory()` 内 `const docStore = docStoreOf()` 定义 |
29
+ | 工厂层 | `:31 const docStoreOf = ...`(只有函数,没有变量) |
30
+ | 后果 | ESM 下必抛 `ReferenceError`,被 `:127 catch (_) { return null }` 吞掉 ⇒ **此函数结构上不可能返回非 null** |
31
+ | 调用点 | `:148 docStore.rebuildSidecar(file, readSidecarPrev(file) \|\| undefined)` ⇒ 恒传 `undefined` |
32
+
33
+ **作者额外发现的一个后果,我独立复核成立**:`rebuildSidecar` 拿不到 prev ⇒ 每次重建都产生新 epoch。
34
+ 而 evidence 的 fresh/stale 判定按 `recordDigest + sourceVersion` 匹配(`evidence-store-pre.js:237`)
35
+ ⇒ **「修复」功能反而把原本 fresh 的证据全部翻成 stale**。这是比"继承失效"更严重的实际影响。
36
+
37
+ > 作者的置信 96/100、定级 **Medium** 是全部四条里最高的,合理。
38
+
39
+ ---
40
+
41
+ ### #56 · `evidence-store-pre.js` 幂等缓存在落盘前登记 ✅ **确认为真**
42
+
43
+ | 项 | 证据 |
44
+ |---|---|
45
+ | 声明 | `_appended.add(id)` 在写盘**之前**执行 |
46
+ | 实测 | `:112 if (id && this._appended.has(id)) { ...duplicate-evidence... }` → `:119 if (id) this._appended.add(id)` → `:120 this._chain = this._chain.then(() => this._writeLine(...))` |
47
+ | 失败路径 | `:134 this.stats.writeFailed++` / `:135 this.stats.lastWriteError = ...` —— **无任何 `delete(id)`** |
48
+ | 唯一清理 | `:187 this._appended.clear()`(在 `dispose()` 内) |
49
+ | 后果 | 一次瞬时写失败 ⇒ 同 id 进程内重试**恒被拒**,证据静默丢失;除 `stats.writeFailed` 外无信号 |
50
+
51
+ **修复建议复核**:作者说「`BoundedIdSet` 需补 `delete()`」——**我未验证该内部类是否有 delete**,
52
+ 这条属下修实现细节,落地时须先确认(可能是已有等价操作)。
53
+
54
+ ---
55
+
56
+ ### #57 · `episodic-store-pre.js` `restore` 不校验 `current` 形状 ✅ **确认为真**
57
+
58
+ | 项 | 证据 |
59
+ |---|---|
60
+ | 声明 | `restore()` 对 `current` 零校验 |
61
+ | 实测 | `:292 current = data.current \|\| null` —— **孤零零一行,无任何形状检查** |
62
+ | 崩点 | `:202 if (current.segments.length < cfg.minSegments)` —— `current` 若为 `{}` ⇒ `TypeError` |
63
+ | 对比 | `episodes` 走 `:222 validateEpisodePre(ep)`,`current` 完全没有对应校验 |
64
+ | 后果 | `current` 损坏(手改/半损坏但可解析)⇒ restore "成功" ⇒ 之后每次 consolidate 都 TypeError ⇒ **巩固链路静默停摆直到重启** |
65
+
66
+ **这条最符合本仓已记录的病害模式**:`restore` 声称成功(`{ ok: true, restored: N }`),
67
+ 但恢复出来的状态**结构上不可用** —— 与 M8/M9「结构性恒假」同族:**成功信号与真实可用性脱钩**。
68
+
69
+ ---
70
+
71
+ ### #58 · `activation-host-pre.js` 清理为零调用 + 键格式错配 ✅ **确认为真(两处均成立)**
72
+
73
+ **根因 1 — 清理函数零调用方**:
74
+ - 全仓 grep `activationHost.disposeRuntime|disposeSession|disposeAll` 只命中 **1 处**:
75
+ `index.js:10869 disposeAll('plugin disposed')`(仅插件整体卸载)
76
+ - `index.js:1083` 的 runtime 释放路径只有 `this._shadowHost.disposeRuntime(runtime)`,
77
+ **没有** `this._activationHost.disposeRuntime(...)`
78
+ - ⇒ 每会话遗留一条 `runtimeState`/`stepsByRuntime`/`pathsByKey`/inbox 记录,进程存活期内不释放
79
+
80
+ **根因 2 — 键格式错配**:
81
+ ```
82
+ :352 const key = sessionId + '|ws:' + workspaceKey ← 写入键(stepFor)
83
+ :510 stepsByRuntime.delete(String(runtimeKey)) ← 删除键(disposeRuntime)
84
+ ```
85
+ 两个键格式不同 ⇒ **即使接线,删除也是空操作**。作者这句话是准的。
86
+
87
+ **定级**:单条很小、需长时间运行 + 频繁开关会话才显现 ⇒ Low 合理。
88
+
89
+ ---
90
+
91
+ ## 2. 与 pre 线的关系(重要)
92
+
93
+ | 问题 | 结论 |
94
+ |---|---|
95
+ | 这些文件在 pre 线存在吗? | ✅ 四个都在(`*-pre.js` 变体,2752–28524 B) |
96
+ | 3.0 重建修掉了它们吗? | ❌ **没有**。四条缺陷在 pre 线**逐行可复现** |
97
+ | pre 线是否已有修复? | ❌ 未见任何对应补丁 |
98
+ | issue 里的行号能直接用吗? | ⚠️ **不能**。issue 行号是 2.5.2 的;pre 线行号已漂移(如 #55 的 `:117-128` 在 pre 线是 `:117-119`,`docStoreOf` 在 `:31` 而非 `:31` 附近需重定位) |
99
+
100
+ **⇒ 这四条是「已发布版本存量缺陷 + pre 线同样存在」,需要在 pre 线修,随下个版本发出。**
101
+
102
+ ---
103
+
104
+ ## 3. 严重性再评估(我的判断,供决策)
105
+
106
+ 作者的定级是 Low/Medium。**我同意 Low 的技术定级,但要补一条**:
107
+
108
+ **#55 应升级为 Medium-High。** 理由不是"泄漏",而是**功能反向**:
109
+ 设置页的「修复」按钮**不但没修,还把证据新鲜度全污染了**。这是一个**用户主动点击才会触发的、结果与按钮语义相反**的行为 ——
110
+ 比"缓慢泄漏"更值得优先。作者给 Medium,我认为偏保守(他大概只估了"epoch 继承失效",
111
+ 没把"fresh→stale 反向污染"算进去;但这一条**作者自己在正文里写了**,所以是定级偏保守,不是漏判)。
112
+
113
+ **四条的共同特征(本仓值得记的判据)**:
114
+
115
+ > 这四条**没有一条**会被现有 smoke 抓到,也**没有一条**会在正常使用中报错。
116
+ > 三条是 fail-soft 的 catch **吞掉了本该炸的错误**(未定义标识符 / 时机错 / 缺校验),
117
+ > 一条是"删除键写错 ⇒ 删除静默失败"。
118
+ > ⇒ **fail-soft 是本仓的铁律,但它的代价在这里集中兑现了**:
119
+ > catch 把"代码 bug"降级成"无明显症状",只有专门的静态审计才能捞出来。
120
+ > 这正好印证用户的原话:「用 GPT 找到 bug」——这类 bug 靠跑是跑不出来的。
121
+
122
+ ---
123
+
124
+ ## 4. 修复建议(按性价比排序)
125
+
126
+ | 序 | issue | 修法 | 风险 | 建议时机 |
127
+ |---|---|---|---|---|
128
+ | 1 | **#55** | `readSidecarPrev` 内加 `const docStore = docStoreOf()`(与 `repair`/`deleteMemory` 一致);补一条 smoke:repair 后 epoch/version 应继承 | 极低(一行 + 断言) | **立即** |
129
+ | 2 | **#56** | 写入成功后才 `add(id)`(在 `.then((written) => ...)` 内判 `written === true`);或失败时 `delete(id)` | 低(需确认 `BoundedIdSet` 能否 delete) | **立即** |
130
+ | 3 | **#57** | `restore` 对 `current` 补形状校验(`sessionRef` 字符串 / `startedAt` 有限 / 三个数组),不合格置 `null`(**丢弃优于卡死**) | 低 | **立即** |
131
+ | 4 | **#58** | ① `dispose(agent)` 补 `_activationHost.disposeRuntime(runtime.key)`;② `disposeRuntime` 改按 `stepFor` 实际键格式删除(或建条目时记下 key) | 中(键格式统一要**同时**核对所有读写点,否则二次错配) | 紧随 |
132
+
133
+ **共同纪律**:四条都**必须先写"能失败"的套件**(当前 4 条全都零测试覆盖),
134
+ 且 #58 修完要**用变异演示确认键格式真的对齐**(正是"改了一处、另一处还是旧键"的典型)。
135
+
136
+ ---
137
+
138
+ ## 5. 与今晚在做的 I5/R4-A 是否同一问题?——**不是,完全无关**
139
+
140
+ 用户问「这个和刚才改的东西是否针对的是同一个问题」——**明确不相干**:
141
+
142
+ | | issue #55–#58 | 今晚在做的 R4-A / I5 |
143
+ |---|---|---|
144
+ | 对象 | `storage-manage` / `evidence-store` / `episodic-store` / `activation-host` | `l0-extract-pre` 的 `status` 谓词 |
145
+ | 层面 | **存储与生命周期**(谁在何时清理、何时登记、何时校验) | **检索呈现**(作废条目该过滤还是该标记) |
146
+ | 用户可见性 | 设置页「修复」按钮失效、证据静默丢失、巩固停摆 | 检索结果里能不能看到"这条已过时" |
147
+ | 关系 | **无任何代码交叠** | — |
148
+
149
+ **唯一的间接联系**:四条 issue 所在的模块(evidence-store / episodic-store / activation-host)
150
+ 正是 R1「静默降级普查」点过名的高危区,而 #55/#56/#57 三条**又是 fail-soft 吞错**同一族。
151
+ ⇒ **可以确认:R1 普查的方向是对的,但覆盖面不全** —— 它按"降级点"枚举,
152
+ 而这次是**按"标识符作用域/缓存时机"**这类静态缺陷找出来的,是另一个证据面。
153
+ 建议把「静态审计」列为 R 系列的补充分支(见 §6)。
154
+
155
+ ---
156
+
157
+ ## 6. 建议(供用户决策,不自行执行)
158
+
159
+ 1. **立即修 #55/#56/#57**:三条都是低风险、一行到几行的修,且**用户可感知**(尤其 #55 的修复按钮反向污染)。
160
+ 2. **#58 紧随其后**,但键格式统一要**一并核对全部键读写点**再动手。
161
+ 3. **建议把「静态审计分支」正式写进作战计划**:本仓已有 R1(降级普查)与本次(外部审计),
162
+ 但**缺少常态化的静态检查**(未定义标识符、键格式一致性、缓存登记时机)。
163
+ 这次 4 条里有 3 条属于"跑不出来、只能看代码看出来"的类型,值得一条独立防线。
164
+ 4. **本次 issue 的作者质量值得肯定**:4/4 全真、行号级证据、给出建议修法、并**如实标注了 Low(没有夸大)**。
165
+ 这是外部贡献里质量很高的一批。
166
+
167
+ ---
168
+
169
+ ## 附:核验方法与可复核性
170
+
171
+ - 提交存在性:`git describe --tags a972ebf` → `v2.5.2-14-ga972ebf`
172
+ - 文件存在性:4 个 `lib/*-pre.js` 全部存在(含字节数)
173
+ - 逐条 grep + 上下文窗口读取(`Select-String -Context`),对照 issue 声明的行号与代码形态
174
+ - **未执行**:未跑复现脚本(#56/#57 需构造瞬时写失败/损坏 JSON,#58 需长时间运行观测);
175
+ 本次为**静态核验**,结论基于代码结构,不基于运行时观测 —— 如需运行时证据,应另写探针。
@@ -0,0 +1,389 @@
1
+ # ⑩-a/⑩-b 修复执行记录(T0 → T1 → T2)
2
+
3
+ > 执行日期:2026-09-19 · 执行人:主代理 · 依据:`ISSUE10B-FORENSICS-20260919.md` §8/§9
4
+ > 状态:**T0/T1/T2 全部完工**,全量回归 **PASS 133 / FAIL 0 / TIMEOUT 0**(139.6s)
5
+ > ⚠️ **待用户动作:重启宿主**(`lib/` 下三个文件已改未提交,pre 线以 `link:` 挂载 ⇒ 需要重启才生效)
6
+
7
+ ---
8
+
9
+ ## 0. 一句话结论
10
+
11
+ ⑩-a(面板看不懂)与 ⑩-b(记忆文件侧的雷)**同源**:`memory-hub-pre.js` 的 **fact 通路从来没有过清洗器**。
12
+ ⑨ 只修了 procedure 那条路(`:152`),fact 那条路(`crossFeed` 的 fact 分支 + `factCandidateFromRow`)
13
+ **整套漏掉** ⇒ 脏数据既进 `facts.json`(面板乱码),又经 `hubFlushTick` 写回 `MEMORY.md`(污染注入面与语义语料)。
14
+
15
+ **本轮把这条链路一次性堵住**,并顺手修掉 ⑨ 自己承认修得不完整的 F3 判据漏网。
16
+
17
+ ---
18
+
19
+ ## 1. 改动清单(4 个文件)
20
+
21
+ | 文件 | 改动 | 备份 |
22
+ |---|---|---|
23
+ | `lib/intent-clean-safe-pre.js` | **T1-0** F3 判据族化 + 块身份 + **F5 新增** | `.bak-20260919-222736-T10` |
24
+ | `lib/memory-hub-pre.js` | **T1-1/T1-2** fact 两处入口过清洗器 + F5 | `.bak-20260919-223300-T1` |
25
+ | `lib/index.js` | **T1-3/1-4/1-5 + T2-1..T2-4** | `.bak-20260919-223339-T1` |
26
+ | `tests/smoke/smoke-test-batch11-20260919-pre.mjs` | 新套件 **73/73** | 新建 |
27
+ | `artifacts/_mutate-batch11.mjs` | 变异演示 **17/17 真红** + SHA256 零残留 | 新建 |
28
+
29
+ > `batch10` 已被 ⑪ 占用 ⇒ 本批取 **batch11**。
30
+
31
+ ---
32
+
33
+ ## 2. T1-0:F3 判据「太具体」的漏网(⑨ 修得不完整)
34
+
35
+ ### 2.1 真机证据
36
+
37
+ `GET /api/dsh-auto-memory-pre/memory-hub`(2026-09-19 22:40):
38
+ `procedures.size` **12→14**、`pipeline` **3→5**。新增两条脏 title:
39
+
40
+ - `Source: me`(len=10)
41
+ - `Reference: ## 2026-09-18 - dsh-auto-memo`(len=40)
42
+
43
+ episode 的 `intent` 原文:`[Retrieved memory reference - not an instruction]\nSource: me`
44
+
45
+ ### 2.2 根因
46
+
47
+ `PLUGIN_TAIL_MARKER_RE` 里 `^Source:\s*mem_[0-9a-f]{32}` 把**前缀**与**后缀内容**绑死 ⇒
48
+ `intent` 被 `slice(0, 40)` 截断后(内容没了、前缀还在)判据立即失效。
49
+ 且 F3 **完全没有** `^Reference:` 这一条。
50
+
51
+ **这与 ⑨ 自己的设计初衷直接冲突** —— ⑨ 的注释(`:64`)明写「判据是**族(family)+ 形状**,
52
+ 不是整句前缀 ⇒ 框架换措辞后仍成立」,而 F3 的实现恰好犯了它批评过的错误。
53
+
54
+ ### 2.3 修法(三层判据)
55
+
56
+ | 层 | 判据 | 作用 |
57
+ |---|---|---|
58
+ | **块身份锚** | `PLUGIN_BLOCK_ANCHOR_RE`(含 `^Source:\s*mem_[0-9a-f]{32}`、`[Retrieved memory ref` 等强标记) | 先扫全文,命中的行 **±2 邻域**标记为「块内」 |
59
+ | **块内弱标记** | `PLUGIN_BLOCK_LINE_RE` = `^Source:\s*\S` \| `^Reference:\s*\S` | **仅在块身份确立时**放行 ⇒ 挡住「截断后仍带后缀」的形态 |
60
+ | **可单判补充** | `PLUGIN_TRUNCATED_FRAGMENT_RE`(`^Source:\s*\S+\s*$`)+ `PLUGIN_REF_HEADING_RE`(`^Reference:\s*#{1,6}\s+\S`) | 块身份缺失时仍能独立判定 |
61
+
62
+ ### 2.4 误伤边界(必须守住)
63
+
64
+ - `Source: mem_xxx 这个格式对不对?` → **保留**(非法 id + 后续整句 ⇒ 非截断残片)
65
+ - `帮我看看 [Retrieved memory reference] 这个标记是干嘛的` → **保留**(讨论标记本身的真人句)
66
+ - `Reference: 这段话引用自哪里?` → **保留**(Reference 后非 markdown 标题)
67
+
68
+ ---
69
+
70
+ ## 3. T1-1/T1-2:fact 通路补清洗器(⑩-a 与 ⑩-b 的**共同根因**)
71
+
72
+ | 入口 | 原实现 | 现实现 |
73
+ |---|---|---|
74
+ | `crossFeed` fact 分支 | `String(ep.intent).slice(0,30)` **未清洗** | 取 raw → F5 判定 → `stripRuntimeIntentPre` → 空值回退 `'episode'` |
75
+ | `factCandidateFromRow` | `String(row.subject)` **未清洗** | 三字段同样处理;object 命中 F5 则置 `null` |
76
+
77
+ **真机三条脏 fact 的实际字段值**(本套件 [6] 组逐字使用):
78
+
79
+ | factId | 脏形态 |
80
+ |---|---|
81
+ | `fact_pre_ac4920327df6601f25200d66e52df71f` | subject 含 **22 个 U+FFFD** |
82
+ | `fact_pre_a9380f5bca22011e33547a45542c61a3` | object 内嵌 `Current DSH file policy: …` |
83
+ | `fact_pre_dda6cc1f36176f5ea000c6f681ea6818` | object 内嵌 `Approval prompts are disabled …` |
84
+
85
+ ---
86
+
87
+ ## 4. ★ F5:行内残留(**新套件自己抓出来的缺口**)
88
+
89
+ ### 4.1 发现过程
90
+
91
+ 新套件首轮跑出 **1 条真 FAIL**:[6] 组 `真机 fact[1]` 未通过。追查发现:
92
+
93
+ ```
94
+ object = "现在是什么情况? Current DSH file policy: danger-full-access. The DS"
95
+ └─真人话─┘ └──────────── 运行时信封 ────────────┘
96
+ ```
97
+
98
+ ### 4.2 为什么清洗器处理不了它
99
+
100
+ `stripRuntimeIntentPre` 是**按行判断**的 —— 删掉这样的整行会**连带丢掉真人话**,
101
+ 所以它必须保留 ⇒ **「清洗后是否变化」这条判据对行内混合天然无效**。
102
+
103
+ ### 4.3 修法:新增 F5 判据(不做清洗,只做判定)
104
+
105
+ `RUNTIME_RESIDUE_RE` 匹配**行内任意位置**的运行时标记短语:
106
+ `Current DSH file policy` | `Current runtime context` | `Approval prompts are disabled in this session` |
107
+ `[Retrieved memory ref` | `Verify against the current user request` | `If a reference hints at what you need` |
108
+ `Reason: fv2 lane=` | `Score: N.N (rank N/N)`
109
+
110
+ `looksRuntimeResiduePre(text)` → 布尔。**命中即判脏并丢弃该字段**(不做「清洗后照写」——
111
+ 这符合本仓纪律:fail-soft 必须留痕,静默改写等于悄悄改数据并丢失诊断事实)。
112
+
113
+ **接入三处**:`crossFeed` fact 分支 · `factCandidateFromRow` · `hubFlushTick` 的 `dirty` 判定。
114
+
115
+ > **F5 与 F1/F2 的区别**:F1/F2 判「**整行是**信封」,F5 判「**行内夹带**信封」。
116
+
117
+ ---
118
+
119
+ ## 5. T1-3/T1-4/T1-5:hubFlushTick 内容卫生门
120
+
121
+ ⑩-b 取证结论:该通路 **6 道过滤全是「结构性」**(`state.flushed` / `count<8` / `subj` 非空 …),
122
+ **没有一道内容卫生** ⇒ 脏 fact 实测已写入正文并归档
123
+ (`archive/notes-archived.md:493` 的 `## DSH ������(M8 固化)`)。
124
+
125
+ | 项 | 原实现 | 现实现 |
126
+ |---|---|---|
127
+ | **T1-3** 卫生门 | 无 | `cSubj/cObj/cPred` 过清洗器 **+ F5 三项** ⇒ `dirty` 则 `mark flushed` + `diag` + `_degradePre.record('hub-flush',…)` + `continue` |
128
+ | **T1-4** 换行归一化 | 无 | `subj1/pred1/obj1 = ….replace(/\s*[\r\n]+\s*/g, ' ').trim()`,且**拼接确实用归一化变量** |
129
+ | **T1-5** 失败留痕 | `catch (_) {}` **全吞** | `catch (eFlush)` + `diag('hub flush FAIL: …')` + `_degradePre.record('hub-flush','append-failed:…')` |
130
+
131
+ > **为什么脏 fact 处理成 skip 而不是「清洗后照写」**:本仓纪律是 fail-soft 必须留痕、
132
+ > 不得静默改写。清洗后照写等于悄悄改数据,并丢失「曾出现脏 fact」这一诊断事实。
133
+
134
+ ---
135
+
136
+ ## 6. T2:四项结构加固
137
+
138
+ | 项 | 问题 | 修法 |
139
+ |---|---|---|
140
+ | **T2-1** | 「已在正文」分支只 `flushed=true` **从不 `count++`**,且完全静默 | 补 `diag('hub flush skip(already-in-body): …')`;**保留「不写正文不计额度」的语义**(这是正确行为,问题只在于原先完全静默) |
141
+ | **T2-2** | disposer 只 clear 两个 interval,**漏 `hubBootTimer`** | 补 `clearTimeout(hubBootTimer)` ⇒ dispose 后不再有回调 |
142
+ | **T2-3** | `flush-state.json` 裸 `writeFileSync`,无锁无 tmp+rename | 改 **tmp + `renameSync`** 原子替换 |
143
+ | **T2-4** | `setInterval(() => { void hubFlushTick() })` **无重入保护** | 加 `hubFlushInFlight` 标志 + `try/finally` 复位;定时器改走 `hubFlushTickGuarded` |
144
+
145
+ > **T2-3 的真实危害**:写坏后 `hubFlushLoad` 静默吞异常 ⇒ `flushed` 回退到旧值
146
+ > ⇒ **已写过的 fact 会被再写一遍**(正文出现两个同名 `(M8 固化)` 段落)。
147
+
148
+ ---
149
+
150
+ ## 7. ★ 变异演示:从「假绿 8 条」到「17/17 全真红」
151
+
152
+ **这是本轮最有价值的方法论发现。**
153
+
154
+ ### 7.1 首轮结果:**8 条假绿**
155
+
156
+ | 假绿项 | 根因 | 处置 |
157
+ |---|---|---|
158
+ | T1-0c/0d 块身份 | **断言太弱**:原用例的每行都被其他判据命中 ⇒ 该路径实际未被覆盖 | **造真正需要块身份的用例**:`Source: mem_<截断> / Wo`(F3 与残片判据都不匹配) |
159
+ | T1-1 crossFeed | **漏测**:原套件只测了 T1-2 | 补源码级断言(完整表达式) |
160
+ | T1-4/T1-5/T2-3/T2-4 | **断言的是字面量存在,不是完整表达式** | 全部改为断言完整表达式 |
161
+ | T2-3 锚点不唯一 | 全仓 `renameSync` **有两处** ⇒ 锚点命中邻近代码 | **限定在 `hubFlushSave` 函数体内**断言 |
162
+ | F5a | 锚点串与实际代码不符 | 读实际代码取准锚点 |
163
+
164
+ ### 7.2 过程中发现的两个次生问题
165
+
166
+ 1. **`looksRuntimeResiduePre` 漏写 `export`** ⇒ `SyntaxError: does not provide an export named …`。
167
+ 教训:新增导出必须跑一次真实 import 验证,`node --check` **查不出**这个。
168
+ 2. **T1-5 的首个变异体是无效变异**:写成 `catch (eFlush) { if (true) {} else {` ——
169
+ `if (true)` 之后仍执行原分支,**语义没变** ⇒ 假绿是**变异体写错**,不是断言太弱。
170
+ 处置:改用 `lineContains/lineTo` **整行替换**(新增能力),把留痕真正删掉。
171
+
172
+ ### 7.3 最终结果
173
+
174
+ ```
175
+ 真红 17 / 假绿或锚点丢失 0 (共 17)
176
+ SHA256 还原校验:clean / hub / idx 三文件**逐字节一致**(零残留)
177
+ ```
178
+
179
+ ---
180
+
181
+ ## 8. 验收证据
182
+
183
+ | 项 | 结果 |
184
+ |---|---|
185
+ | `node --check` 三文件 | OK |
186
+ | 行尾 | 全部 **CRLF**(`index.js` 11115/11115 · `memory-hub-pre.js` 349/349 · `intent-clean-safe-pre.js` 230/230) |
187
+ | 新套件 `smoke-test-batch11` | **PASS 73 / FAIL 0** |
188
+ | 既有契约守卫 `batch9`(⑨) | **33/33** |
189
+ | 既有契约守卫 `batch10`(⑪) | **57/57** |
190
+ | 既有契约守卫 `r5-envelope-structural` | **36/36** |
191
+ | 变异演示 | **17/17 真红** + SHA256 零残留 |
192
+ | **全量回归** | **PASS 133 / FAIL 0 / TIMEOUT 0(139.6s)** |
193
+
194
+ > 基线为 132;+1 = 本批新增的 `batch11`。
195
+
196
+ ---
197
+
198
+ ## 9. 本轮没有做的事(明确边界)
199
+
200
+ 1. **没有碰 `MEMORY.md` 任何字节** —— 存量 3 条脏 fact 按用户拍板走**路 C(靠卫生门堵住,不改数据)**。
201
+ 2. **没有直接改 `hub-pre/*.json`** —— ⑨ 的教训:宿主内存持有副本,`dispose()` 会无条件回写 ⇒ 改盘会被回滚。
202
+ 3. **没有动 T3** —— 用户已拍板「T3 系统性收口单独立项,前端之前做」。
203
+ 本轮只在**三个已知入口**设防(fact 两处 + hubFlushTick);T3 要解决的是
204
+ 「把 `sanitizeForWrite` 下沉到写原语 ⇒ 20 条写入路径自动受保护」,那是另一个战场。
205
+ 4. **没有重启宿主**(用户硬性规则)。
206
+
207
+ ---
208
+
209
+ ## 10. 遗留与本轮新增的待办
210
+
211
+ | 项 | 说明 |
212
+ |---|---|
213
+ | **待用户重启宿主** | `lib/` 三文件已改未生效 |
214
+ | **T3 立项** | 20 条写入路径 × 3 个消费方,无联合门禁 |
215
+ | **前端可读性** | 用户追加:「procedure memory / skill 的内容、**晋升的原因**都要在前端更好地表示」⇒ 已登记进 `PRE-FRONTEND-CHECKLIST-20260919.md` §4(R2 需 host 侧改动:`overview()` 目前只投影 `procedureId/title/stage/riskLevel/evidence/pinned/observationOnly`) |
216
+
217
+ ---
218
+
219
+ ## 11. T3 执行记录(2026-09-19 · 第 2 批)
220
+
221
+ ### 11.1 ★ T3-1 原方案被实测推翻(两处数据破坏风险)
222
+
223
+ ⑩-b 报告的 T3-1 写的是「把 `sanitizeForWrite` 下沉到 `appendText`/`writeFull` ⇒ 20 条通路全受保护」。**取证发现不能照做**:
224
+
225
+ | 风险 | 证据 |
226
+ |---|---|
227
+ | **① 体量截断** | `sanitizeForWrite` 超 `maxEntryChars`(默认 **8000**)时**不拒绝而是 `slice(0,8000)` 静默截断**(`:8327-8329`)。而 `writeFull` 承担「原文保底归档」(`:7723`,注释即写「绝不丢信息」)与「归档日志原文内联」(`:7733`);实测真实日志 **77805 / 52394 / 43224** 字符 ⇒ 下沉后这些会被砍到 8000。 |
228
+ | **② `RAW_JSON_MARK` 大面积误伤** | 该正则含**裸词 `updatedAt`**。实测扫描 **541 个真实记忆文件:124 个命中**(多数是正常提到 `updatedAt` 的正文)。它是**入口级**判据(针对「AI 调写入工具时传外部画像 raw JSON」),不适合审「整篇文档 / 任意追加」。 |
229
+
230
+ ### 11.2 T3-1 实际落地(用户拍板:**新开一个,不拆既有函数**)
231
+
232
+ **新增 `hygieneGateForPrimitive(text)`**(`index.js`,独立于 `sanitizeForWrite`):
233
+ - **只做卫生**:`mojibake` / `stutter` / `base64` / `duplicate-lines` —— **无体量上限、不截断、不改写正文**(返回体只有 `{ok, reason?}`,无 `clean`/`truncated`)。
234
+ - **刻意不含 `RAW_JSON_MARK`**(理由见 11.1②)。
235
+ - **只挂 `appendText` 一处**,**不挂 `writeFull`**(它写整篇文档,命中面过大;且 `:7723` 是保底归档,绝不能因误伤而失败)。
236
+ - **fail-soft**:内部异常一律视为放行 —— 新守卫绝不成为新失败源。
237
+ - **拒绝时抛出** `memoryWriteError('hygiene', {reason})`,与写入原语既有契约一致(调用方已有 try/catch 兜底)。
238
+
239
+ **`sanitizeForWrite` 一个字节未改**,现有 6 个入口继续用它(`WRITE_GATE_REASON` 的 `raw-json` 文案也保留)。
240
+
241
+ ### 11.3 ★ 顺带修复一个既有数据丢失 bug(必须修,否则新门会引爆它)
242
+
243
+ `maintain()` 第 4 步的删除循环**遍历的是 `oldLogs` 而不是 `archived`**:
244
+
245
+ ```
246
+ [归档] catch (e) { console.error(...) } ← 失败只记日志,archived 里没有它
247
+ [删除] for (const log of oldLogs) rm(...) ← ★ 归档失败的也照样删
248
+ ```
249
+
250
+ 上方注释虽写「原文已在 archive/ 保底」,但那两段代码并不保证这件事。**加了卫生门之后,脏日志会让归档失败并被删 ⇒ 永久丢失。**
251
+ **修法**:删除以 `archived` 为准(`if (archived.indexOf(log.name) === -1) { kept.push(...); continue }`)。
252
+
253
+ ### 11.4 验收证据(T3-1)
254
+
255
+ | 项 | 结果 |
256
+ |---|---|
257
+ | `node --check` | OK · 全 CRLF |
258
+ | **真实 `import` 验证** | OK(`node --check` 查不出 export 缺失) |
259
+ | **探针 `artifacts/_probe-t3.mjs`** | **13/13 全绿**(含 3 条反向保证:大文本放行 · `sanitizeForWrite` 仍截断 · 仍拦 raw-json) |
260
+ | **误伤扫描 `artifacts/_scan-t3-falsepos.mjs`** | 541 文件:判据收窄前「整文件被拒 **129**」→ 收窄后 **5**(1 mojibake + 4 stutter,**均为真脏**) |
261
+
262
+ ### 11.5 ★ T3-2 判定为「不做」(原报告判断有误)
263
+
264
+ ⑩-b 报告称 `m4-corpus-pre.js` 的 `dropped[]` 需「补 degrade 台账,让静默丢弃可见」。**取证推翻了「静默」**:
265
+
266
+ | 消费者 | 位置 | 实际行为 |
267
+ |---|---|---|
268
+ | **审计持久化** | `shadow-host-pre.js:346-351` | 映射进 `appendAuditDurable(ev)`(`stage/reason/memoryId/sourceRef` + counts) |
269
+ | **自动自愈** | `storage-manage-pre.js:82-95` | 按 `reason` 分类为 `stale`/`unrepairable`/`ok`,`REPAIRABLE_REASONS_PRE_V1` 命中即 rebuild sidecar |
270
+
271
+ ⇒ `dropped[]` **已被两处真实消费**,不是静默丢弃。且这两处**都没走 degrade 台账**,因为 `dropped` 属**常规观测**而非预期外失败 —— 补 `degrade.record` 会**违反** R4 批确立的纪律(「常规观测走并列通道,绝不 record,否则『有没有降级』永远非空」)。
272
+
273
+ **结论:T3-2 不是遗漏,是设计。不做。**
274
+
275
+ ### 11.6 探针两次「假失败」的教训(可复用)
276
+
277
+ 初版探针两条 FAIL,**都是夹具写错,不是代码错**:
278
+
279
+ 1. `'abc'.repeat(12)`(无分隔符连写)**不该**命中 `hasStutter` —— 其词规则是 `(\w{2,})(?:[^\w]+\1){3,}`,**要求重复之间有分隔符**(覆盖 `"Run. Run. Run. Run."`)。
280
+ 2. `'x'.repeat(200)` 想测「大文本不被截断」,却**先命中 `BASE64_LINE`**(`^[A-Za-z0-9+/]{200,}={0,2}$`)⇒ 在长度检查**之前**就返回 `base64`。换含空格/中文的长文本才走对路径。
281
+
282
+ **通用教训**:写判据探针时,**夹具必须避开其他判据的命中面**,否则测的不是你想测的分支(这是「假红」的常见来源,与 `⑩` 批的「假绿」互为镜像)。
283
+
284
+ ### 11.7 ★ T3-3 撞用户硬规则 · 已停手待拍板
285
+
286
+ **目标**:`overview()` 暴露 flush 状态 + `reasonCodes` 投影(前端 R2「让人读懂晋升原因」的前置依赖)。
287
+
288
+ **取证发现的雷**:`procedure-store-pre.js` 的 `promote()` **不是纯函数,有真实副作用**:
289
+
290
+ | 行 | 副作用 |
291
+ |---|---|
292
+ | `:295` / `:297` | `stats.approvalAsked++` |
293
+ | `:303` | **`p.stage = 'validated'`**(改状态) |
294
+ | `:305` | `stats.validated++` |
295
+ | `:306` | **`void persist()`**(写盘) |
296
+
297
+ ⇒ 若在 `overview()` 里对每条 procedure 跑 `promote()` 来取 `reasonCodes`,**「打开记忆中枢面板」这个只读动作会让所有够格的技能真的晋升并写盘**。
298
+
299
+ **正解**:另写一个**纯只读的判定投影**(复算门限 → 返回 `reasonCodes`,不改状态、不写盘、不动 stats),供 `overview()` 调用。
300
+
301
+ **为什么停手**:这要动 `lib/procedure-store-pre.js` —— 命中用户硬规则
302
+ > 「涉及 procedure 记忆引擎(含清洗器)的改动,不得自行实施,必须等用户就整体方案拍板」。
303
+
304
+ **★ 新增可复用纪律**:给只读投影(面板/概览)复用会改状态的判定函数 = **点一下面板就等于执行一次晋升**。复用任何判定函数前,必须先查它有没有副作用(改状态 / 写盘 / 改统计)。
305
+
306
+ ### 11.8 ★ 本条日志的端到端实证(`RAW_JSON_MARK` 误伤,非纸面推断)
307
+
308
+ 写 `11.1②` 这条记忆时,**日志写入被入口闸门以 `raw-json` 拒绝了一次** —— 原因仅仅是正文里**提到了那个字段名**(`u`…`A`,即 `updatedAt`)。
309
+
310
+ ⇒ `11.1②` 那个「541 文件 / 124 命中」的静态扫描结论,由此获得了**端到端**验证:该判据确实会把「正常提及该词」的内容误判为外部画像 raw JSON。**这也是 `hygieneGateForPrimitive` 刻意不含它的直接理由。**
311
+
312
+
313
+ ---
314
+
315
+ ## §12 T4 完工 —— procedure memory 的**模型直写通路**(2026-09-19 用户拍板)
316
+
317
+ ### 12.1 用户原话与根因
318
+
319
+ 用户:「原来之前一直都没做模型生成的过程吗?这太恐怖了。赶紧把这个模型生成的过程做了。另外,这个 semantic 和 episodic memory 的部分是不是也没有接入模型啊?」
320
+
321
+ **取证结论(三条线全部无模型入口,附代码证据)**:
322
+ - `defineTool` 全仓仅 16 个,**零** `memory_fact*` / `memory_episode*` / `memory_procedure*`。
323
+ - procedural 线唯一来源 = `memory-hub-pre.js:142-189` `crossFeed()`,把每个"成功 episode"机械切成候选;
324
+ 而 episode 的 `actions` 恒为 `['user','user','user']`(`episodes.json` 实测行)⇒
325
+ 产出 14 条里 **13 条 evidence 全 0 / successCriteria 全 0 / steps 是 "步骤1: user" 占位符**。
326
+ - **清洗器无法解决**:`['user','user','user']` 里本就不含流程信息 —— 这是**结构性天花板**,不是清洗不足。
327
+ - episodic:`index.js:7382` `hub.stores.episodic.append({...})` 在自动沉淀路径内,**100% 宿主自动**,无工具。
328
+ - fact:仅 `upsert(cand)`(`fact-store-pre.js:169`) + `ingestJudgementRows`(`:456`),模型无直接路径。
329
+
330
+ ### 12.2 改动(两文件)
331
+
332
+ **`lib/procedure-store-pre.js`** —— `promote(procedureId, extraEvidence, opts)` 新增第三参:
333
+ - `opts.authorizedBy === 'model'` ⇒ **跳过两条统计证据门**(`minSessionDiversity` / `minSuccessCount`)。
334
+ - **为什么必须跳**:这两条门是给机械生成的观察行用的防污染护栏(靠"反复出现"累积证据);
335
+ 而模型主动写出的真技能(带 steps + successCriteria)**天生零历史证据**,不放行则永远卡 `diversity-below-3`。
336
+ - **授权仍不能突破的结构门**(护栏保留):`deprecated` 短路 / `isObservationOnlyPre` 短路 /
337
+ `no-success-criteria` / `correction-rate` / `has-correction`。
338
+ - **留痕**:晋升成功写 `p.authorizedBy`,`reasonCodes` 追加 `model-authorized`(可审计、前端可展示)。
339
+ - **向后兼容**:`opts` 缺省 `{}` ⇒ `authorizedBy` 为空串 ⇒ **未授权时行为逐字节不变**(T4-1 锁定)。
340
+
341
+ **`lib/index.js`** —— 两处:
342
+ 1. 新增 `defineTool('memory_procedure_pre', ...)`(**工具数 16→17**):
343
+ - `action: 'write' | 'activate'`;`write` → `observe()` 进审批列表;
344
+ `activate` → `observe` → `promote(授权=model)` → `activate` → **自动导出 SKILL.md**。
345
+ - `steps`/`successCriteria`/… 用**字符串分行**接收(`defineTool` 不支持 array schema);
346
+ 行首 `1. `/`- `/`* ` 自动剥离(模型输出格式不稳定的兜底)。
347
+ - 无 `successCriteria` 时**显式警告**(该条目结构上无法晋升)。
348
+ 2. `renderMemoryStatic()` 内新增注入语:技能库直写引导(用户原话「如果有值得介入 procedure memory 的东西,
349
+ 那就接入写进审批列表」),含判据「下次我遇到类似场景,会不会想照做」。
350
+
351
+ ### 12.3 四处工具数硬锁联动(16→17 / legacy 14→15)
352
+
353
+ 加工具**必然**打破数量断言,逐处更新并注明原因:
354
+ `tests/smoke/smoke-test.mjs:67` · `smoke-test-m3b3-pre.mjs:43` · `smoke-test-context-observer.mjs:107` ·
355
+ `smoke-test-graph-mode-pre.mjs`(A1 graph 17 / **D1 legacy 15** / D2 非法档 15)·
356
+ `smoke-test-p23-wb-sidecar-pre.mjs:127-138`(M9 正则 `!== 16` → `!== 17`)。
357
+ ★ 另在 `index.js` 工具定义处补「工具数 16→17」联动注释(M9 会检查该注释在场)。
358
+ ★ D1 新增反向断言:`memory_procedure_pre` **不受 boardMode 闸门管** ⇒ legacy 档也必须在场。
359
+
360
+ ### 12.4 验收(全部实跑,非纸面)
361
+
362
+ | 项目 | 结果 |
363
+ |---|---|
364
+ | `node tests/smoke/smoke-test-t4-procedure-model-write-pre.mjs` | **36/36 通过** |
365
+ | 变异①`if (!authorizedBy)` → `if (true)` | **27 通过 / 9 失败**(真红:授权失效被抓) |
366
+ | 变异②删除 `observationOnly` 短路 | **34 通过 / 2 失败**(真红:授权越结构门被抓) |
367
+ | 变异后恢复 | SHA256 `F140D7E5AE681EDE14F382D416B930C6A4E1D5389BDCE0B8B9AA36E9122C589` **逐字节一致,零残留** |
368
+ | 全量回归 | **PASS 134 / FAIL 0 / TIMEOUT 0(138.1s)**(基线 133 → 134,+T4 套件) |
369
+
370
+ ### 12.5 三条踩坑(新增,可复用)
371
+
372
+ 1. **`addEvidence(pid, ev)` 的签名是 `ev.kind`**,不是 `{correction:1}` / `{success:true}` ——
373
+ 传错**静默返回 `bad-evidence` 且不生效**(不是抛错)⇒ 夹具造出的"有纠正记录"根本不存在 ⇒ **假红**。
374
+ 本套件**连续踩了三次同一坑**(correction / sessions / success 三处)。教训:造证据类夹具必须**加自检断言**
375
+ (`ae.ok === true` / `evidence.sessions >= 3`),否则测试在测空气。
376
+ 2. **加工具会连带打破多处"数量硬锁"** —— 本次共 5 个文件 6 处(含一处正则 `!==\s*16`)。
377
+ 纪律:新增/删除工具后必须全仓 grep 工具数断言,逐处更新并**写明原因**,不能只改数字。
378
+ 3. **`memory_procedure_pre` 必须无条件注册** —— 它不属白板 P3 闸门;若误放进 `graphEnabled` 分支内,
379
+ legacy 档就没有模型写入口(本项目 BUG-15 正是"defineTool 写在数组外"的同型缺陷)。
380
+ D1 已加反向断言守住这一点。
381
+
382
+ ### 12.6 边界与未做
383
+
384
+ - **未禁用** `crossFeed()` 的机械 procedure 分支 —— 那会改 `memory-hub-pre.js`,
385
+ 触及用户「procedure 引擎改动须先拍板」硬规则,**待用户裁定**。
386
+ - `T3-3`(`overview()` 补投影 / 只读 `evaluatePromotion`)仍待用户 A/B/C 选择。
387
+ - 观察线信封残留(`memory-hub-pre.js:152` 只用 `stripRuntimeIntentPre`,缺 F5 同款 `looksRuntimeResiduePre`)
388
+ 未修,属同一批 procedure 引擎议题。
389
+ - **⚠️ 待用户重启宿主**:`lib/index.js` + `lib/procedure-store-pre.js` 已改未生效。