@a9i5k4/dsh-auto-memory 3.0.1 → 3.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -8
- package/README.zh-CN.md +13 -8
- package/docs/HANDBOOK.md +88 -52
- package/docs/USER-GUIDE.en.md +9 -9
- package/docs/USER-GUIDE.zh-CN.md +9 -9
- package/docs/screenshots/promo/promo-0-banner-v4.png +0 -0
- package/docs/screenshots/promo/promo-1b-auto-recall.png +0 -0
- package/lib/activation-host.js +6 -1
- package/lib/client.js +819 -272
- package/lib/context-host.js +7 -1
- package/lib/episodic-store.js +90 -16
- package/lib/fact-store.js +463 -41
- package/lib/hub-io.js +217 -0
- package/lib/index.js +224 -45
- package/lib/intent-clean-safe.js +1 -1
- package/lib/memory-hub.js +37 -5
- package/lib/note-status.js +9 -1
- package/lib/procedure-store.js +252 -31
- package/lib/procedure-switch.js +38 -0
- package/lib/python-sidecar-client.js +285 -8
- package/package.json +6 -2
- package/docs/internal/ACCEPT-35-LIVE.md +0 -143
- package/docs/internal/ACCEPTANCE-20260914.md +0 -90
- package/docs/internal/ARCH-REVIEW-BRIEF.md +0 -411
- package/docs/internal/ARCH-REVIEW-REQUEST.md +0 -201
- package/docs/internal/ARCH-REVIEW-ROUND2.md +0 -169
- package/docs/internal/ARCH-REVIEW-ROUND3.md +0 -206
- package/docs/internal/ARCHITECTURE-FOR-ZCODE-20260920.md +0 -397
- package/docs/internal/ART-DIRECTION-DEEPSEEK-20260920.md +0 -351
- package/docs/internal/ART-DIRECTION-WIREFRAME.md +0 -191
- package/docs/internal/ART-DIRECTION-WIREFRAME.md.bak-superseded +0 -181
- package/docs/internal/AUDIT-WB-GRAPH-FULL-20260916.md +0 -314
- package/docs/internal/BATTLE-PLAN-20260917.md +0 -871
- package/docs/internal/CONCURRENCY-INVESTIGATION-20260917.md +0 -192
- package/docs/internal/CROSS-SESSION-SEARCH-PATH-DECISION.md +0 -72
- package/docs/internal/CROSS-SESSION-SEARCH-RESEARCH.md +0 -131
- package/docs/internal/CUA-VISION-FIX-NOTES.md +0 -78
- package/docs/internal/DECISIONS-20260914-SESSION.md +0 -269
- package/docs/internal/DESIGN-OVERHAUL-PRE-RESEARCH.md +0 -292
- package/docs/internal/DESIGN-P1-STATE-COMMIT-20260915.md +0 -219
- package/docs/internal/DIRECTION-CHECK-WB-GRAPH-20260916.md +0 -132
- package/docs/internal/FEATURE-INVENTORY.md +0 -531
- package/docs/internal/FEEDBACK-TO-DSHAPI-RELAY.md +0 -13
- package/docs/internal/G-SERIES-EXECUTION-20260917.md +0 -248
- package/docs/internal/G3-DESIGN-20260918.md +0 -82
- package/docs/internal/G3-DISK-FORMAT-GAP-20260919.md +0 -92
- package/docs/internal/GH-DISCUSSION-5732-COMMENT.md +0 -74
- package/docs/internal/GPT-ACCEPTANCE-PROMPT-20260916.md +0 -352
- package/docs/internal/GPT-REVIEW-PROMPT.md +0 -216
- package/docs/internal/GROUP-DIGEST-SETUP.md +0 -62
- package/docs/internal/GROUP-LISTENER-SETUP.md +0 -49
- package/docs/internal/GROUP-WEBHOOK-SETUP.md +0 -93
- package/docs/internal/HANDOFF-TO-ZCODE-20260920.md +0 -309
- package/docs/internal/HANDOFF-TO-ZCODE.md +0 -168
- package/docs/internal/HERMES-DATA-VERIFICATION-20260919.md +0 -120
- package/docs/internal/HERMES-LEGACY-STATUS-20260919.md +0 -74
- package/docs/internal/ISSUE-55-58-VERIFICATION-20260918.md +0 -175
- package/docs/internal/ISSUE10-FIX-EXECUTION-20260919.md +0 -389
- package/docs/internal/ISSUE10-PLAN-20260919.md +0 -254
- package/docs/internal/ISSUE10B-FORENSICS-20260919.md +0 -468
- package/docs/internal/ISSUE9-PURGE-AND-R1-PLAIN-20260919.md +0 -150
- package/docs/internal/ISSUE9-RESIDUAL-FORENSICS-20260919.md +0 -114
- package/docs/internal/KICKOFF-P0.md +0 -254
- package/docs/internal/LESSON-TO-CANDIDATE-STATUS-20260919.md +0 -79
- package/docs/internal/MASTER-PLAN-3.0.md +0 -411
- package/docs/internal/MEMORY-GOVERNANCE-20260917.md +0 -309
- package/docs/internal/MEMORY-MUTATION-AND-INDEX-DESIGN.md +0 -85
- package/docs/internal/MERGE-CONFLICT-SCAN-20260914.md +0 -222
- package/docs/internal/NEXT-VERSION-TODO.md +0 -95
- package/docs/internal/OFFICIAL-DISCUSSION-DRAFT.md +0 -80
- package/docs/internal/PENDING-FIXES-20260916.md +0 -289
- package/docs/internal/PRE-FRONTEND-CHECKLIST-20260919.md +0 -705
- package/docs/internal/PRE-FRONTEND-CHECKLIST-20260919.md.bak-s10 +0 -649
- package/docs/internal/PROCEDURAL-MEMORY-AND-APPROVAL-DESIGN-20260918.md +0 -225
- package/docs/internal/PROGRESS-20260917.md +0 -93
- package/docs/internal/PROMPT-GAP-AUDIT-20260920.md +0 -128
- package/docs/internal/R1-DEGRADE-AUDIT-20260918.md +0 -163
- package/docs/internal/R1-READABILITY-FORENSICS-20260919.md +0 -127
- package/docs/internal/R2-EVIDENCE-DEEP-AUDIT-20260918.md +0 -140
- package/docs/internal/R3-DEGRADE-LEDGER-DESIGN-20260918.md +0 -138
- package/docs/internal/R4-RECALL-QUOTA-PLAN-20260918.md +0 -218
- package/docs/internal/RAG-KARPATHY-PROGRAM.md +0 -229
- package/docs/internal/RELEASE-PROCESS.md +0 -99
- package/docs/internal/REPORT-P0-NIGHTLY.md +0 -212
- package/docs/internal/REPORT-P5-ACCEPTANCE.md +0 -31
- package/docs/internal/REPORT-WB-GRAPH-NIGHTLY.md +0 -153
- package/docs/internal/RESUME-20260918.md +0 -171
- package/docs/internal/RESUME-20260919.md +0 -104
- package/docs/internal/REVIEW-WB-GRAPH-SELF.md +0 -81
- package/docs/internal/RHINELAB-TO-DEEPSEEK-FEASIBILITY.md +0 -198
- package/docs/internal/ROADMAP-20260917-WEEK.md +0 -439
- package/docs/internal/ROADMAP.md +0 -106
- package/docs/internal/RUN-P0-NIGHTLY.md +0 -227
- package/docs/internal/S10-CONSTRUCTION-HANDOFF-20260917.md +0 -185
- package/docs/internal/S10-GAP-INVENTORY-20260917.md +0 -239
- package/docs/internal/S10-GAPS-PLAIN-20260917.md +0 -125
- package/docs/internal/SEMANTIC-ARCHITECTURE-SPEC.md +0 -360
- package/docs/internal/SESSION-FILE-REPAIR-PROTOCOL.md +0 -90
- package/docs/internal/SUBAGENT-REPORT-ROUTING-PRE-RESEARCH.md +0 -261
- package/docs/internal/T6-EXECUTION-20260920.md +0 -130
- package/docs/internal/TELEMETRY-EFFECT-REPORT-DESIGN-20260918.md +0 -146
- package/docs/internal/THESIS-GAP-ANALYSIS-20260918.md +0 -89
- package/docs/internal/THESIS-OUTLINE-20260918.md +0 -147
- package/docs/internal/THREE-LAYER-CONTRACT.md +0 -219
- package/docs/internal/TODO-BACKLOG.md +0 -263
- package/docs/internal/TODO-GRAPH.html +0 -715
- package/docs/internal/TODO-GRAPH.html.bak-20260914-v2 +0 -493
- package/docs/internal/TODO-GRAPH.html.bak-20260915-alsfix +0 -710
- package/docs/internal/TODO-GRAPH.html.bak-20260915-p1 +0 -710
- package/docs/internal/TODO-GRAPH.html.bak-20260915-p6a-rev +0 -703
- package/docs/internal/TODO-GRAPH.html.bak-20260915-wshint +0 -710
- package/docs/internal/TODO-GRAPH.html.bak-20260916-batch +0 -715
- package/docs/internal/UPSTREAM-ISSUE-PR-TRIAGE-20260919.md +0 -297
- package/docs/internal/UPSTREAM-ISSUES-3RD-AUDIT-20260920.md +0 -104
- package/docs/internal/WB-FORMAT-CONVENTION.md +0 -112
- package/docs/internal/WB-GRAPH-DECISIONS-20260914.md +0 -71
- package/docs/internal/WB-GRAPH-INTEGRATION-PLAN.md +0 -386
- package/docs/internal/WB-GRAPH-RESEARCH-BRIEF.md +0 -118
- package/docs/internal/WB-GRAPH-RESEARCH-EXTERNAL.md +0 -228
- package/docs/internal/WB-GRAPH-RESEARCH-LOCAL.md +0 -190
- package/docs/internal/reviews/CLAIM-VERIFICATION-20260914.md +0 -56
- package/docs/internal/reviews/PLAN-gpt6astra-round2-20260914.md +0 -787
- package/docs/internal/reviews/REVIEW-gpt6astra-20260914.md +0 -112
- package/docs/internal/reviews/ROUND3-REVIEW-INTEGRATION-20260914.md +0 -230
|
@@ -1,389 +0,0 @@
|
|
|
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` 已改未生效。
|
|
@@ -1,254 +0,0 @@
|
|
|
1
|
-
# ⑩-a / ⑩-b 规划(2026-09-19)
|
|
2
|
-
|
|
3
|
-
> **本文件是上下文压缩后的唯一恢复入口**——读完它即可恢复 ⑩ 的全部上下文,无需回溯对话。
|
|
4
|
-
> **术语校正**:用户口述「石壁」=**⑩-b**(「十B」的语音转写)。
|
|
5
|
-
> **⑩-b 的定义**=**记忆文件(`MEMORY.md`)侧的隐患**,含两件事:(i) 尚未引爆的 **M8 回写雷**;(ii) 排版与语义的耦合。
|
|
6
|
-
|
|
7
|
-
---
|
|
8
|
-
|
|
9
|
-
## 0. 一句话定位(先看这张表)
|
|
10
|
-
|
|
11
|
-
| | **⑩-a** | **⑩-b** |
|
|
12
|
-
|---|---|---|
|
|
13
|
-
| **是什么** | **前端「记忆中枢」面板里看不懂** | **记忆文件侧的隐患**(回写雷 + 排版/语义耦合) |
|
|
14
|
-
| **用户本意** | ✅ **这就是 ⑩ 的原意**(2026-09-19 用户以面板截图澄清) | 后续深挖出来的 |
|
|
15
|
-
| **涉及语义引擎吗** | **不涉及**(已代码核实) | **涉及** |
|
|
16
|
-
| **涉及 `MEMORY.md` 吗** | **不涉及** | **涉及** |
|
|
17
|
-
| **今天的状态** | **待修**(用户指示先做) | **取证中**(两个只读子代理并行) |
|
|
18
|
-
|
|
19
|
-
**⑩ 的文档链**:
|
|
20
|
-
- 取证数据 → `R1-READABILITY-FORENSICS-20260919.md`
|
|
21
|
-
- 人话版 + 四选修法 → `ISSUE9-PURGE-AND-R1-PLAIN-20260919.md`
|
|
22
|
-
- **本文件 = 拆分为 ⑩-a / ⑩-b 后的执行规划**
|
|
23
|
-
|
|
24
|
-
---
|
|
25
|
-
|
|
26
|
-
## 1. ⑩-a · 前端面板可读性(**本项要做**)
|
|
27
|
-
|
|
28
|
-
### 1.1 现象(用户截图,2026-09-19)
|
|
29
|
-
前端「记忆中枢」→ 事实(Semantic)区,条目显示为乱码与噪声,**人看不懂**。
|
|
30
|
-
|
|
31
|
-
### 1.2 根因 = **数据脏,不是渲染差**(已实测)
|
|
32
|
-
|
|
33
|
-
喂该面板的是 `~/.dsh/memory/hub-pre/facts.json`。探针实测 **4 条中 3 条是坏的**:
|
|
34
|
-
|
|
35
|
-
| # | factId | 坏在哪 |
|
|
36
|
-
|---|---|---|
|
|
37
|
-
| 0 | `fact_pre_ac4920327df6601f25200d66e52df71f` | **22 个 U+FFFD**:`DSH ������ \| has three modes \| shadow …` |
|
|
38
|
-
| 1 | `fact_pre_a9380f5bca22011e33547a45542c61a3` | object 内嵌运行时信封:`Current DSH file policy: danger-full-access…` |
|
|
39
|
-
| 3 | `fact_pre_dda6cc1f36176f5ea000c6f681ea6818` | object 内嵌运行时信封:`Approval prompts are disabled in this session…` |
|
|
40
|
-
|
|
41
|
-
### 1.3 为什么脏 = **⑨ 只修了一半**(关键发现)
|
|
42
|
-
|
|
43
|
-
`lib/memory-hub-pre.js` 的 `crossFeed()` **两条通路不对称**:
|
|
44
|
-
|
|
45
|
-
```
|
|
46
|
-
走 procedure(约 :152)→ stripRuntimeIntentPre(ep.intent) ✅ ⑨ 已修
|
|
47
|
-
走 fact (约 :166)→ subject: ep.intent.slice(0, 30) ❌ 完全没清洗
|
|
48
|
-
object: ep.unresolved[0].slice(0, 60) ❌ 直接切片
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
同理 `factCandidateFromRow()`(约 `:249-265`)**也不调**清洗器,只做 `String(row.subject || sourceIds[0])`。
|
|
52
|
-
⇒ **⑨ 修了「技能」那条路,「事实」这条路整套漏掉** —— 这是 `facts.json` 变脏的直接成因。
|
|
53
|
-
|
|
54
|
-
### 1.4 修复方案
|
|
55
|
-
|
|
56
|
-
| 步 | 动作 | 位置 |
|
|
57
|
-
|---|---|---|
|
|
58
|
-
| **a-1** | **清存量脏数据**(3 条) | `~/.dsh/memory/hub-pre/facts.json` |
|
|
59
|
-
| **a-2** | `crossFeed()` 的 **fact 分支**补清洗:`subject`/`object` 先过 `stripRuntimeIntentPre` | `lib/memory-hub-pre.js` 约 `:166-174` |
|
|
60
|
-
| **a-3** | `factCandidateFromRow()` 补清洗(`subject`/`predicate`/`object`) | 同文件约 `:249-265` |
|
|
61
|
-
| **a-4** | 新增 smoke 套件 + 变异演示 + 全量回归 | `tests/smoke/` · `artifacts/` |
|
|
62
|
-
|
|
63
|
-
**清洗器的选择**:复用 ⑨ 修好的 `stripRuntimeIntentPre()`(`lib/intent-clean-safe-pre.js`)。
|
|
64
|
-
它同时覆盖两类脏数据——**F3**(召回块/信封尾部标记)与 **F4**(`looksEncodingCorruptedPre`,U+FFFD ≥3)
|
|
65
|
-
⇒ 一条调用即可同时解决「信封污染」与「乱码」两种症状。
|
|
66
|
-
|
|
67
|
-
**⚠️ 清存量数据的坑(⑨ 已踩过,务必复用)**:
|
|
68
|
-
宿主**内存里持有副本**,`dispose()` 会**整份写回盘** ⇒ **直接编辑 json 会被回滚**。
|
|
69
|
-
⑨ 的解法是走宿主自己的 loopback 通路(`POST /api/dsh-auto-memory-pre/memory-hub`)。
|
|
70
|
-
|
|
71
|
-
### 1.4b ★ a-1 调研结论(2026-09-19 20:45 已查,**未动手**)
|
|
72
|
-
|
|
73
|
-
**事实一:fact store 没有「按 id 删除」的方法。**
|
|
74
|
-
`fact-store-pre.js:409-410` 导出的全集 = `upsert · supersede · get · query · conflictsList · pendingConflicts · resolveConflict · evidenceFor · revokeBySource · snapshot · restore · clear · dispose`
|
|
75
|
-
—— **没有 `remove` / `delete` / `revokeById`。**
|
|
76
|
-
|
|
77
|
-
**事实二:唯一的撤销通路是 `revokeBySource(sourceId)`(`:283-300`),且它按 provenance 反查**:
|
|
78
|
-
|
|
79
|
-
```js
|
|
80
|
-
if (!Array.isArray(f.provenance) || !f.provenance.includes(sid)) continue
|
|
81
|
-
f.revoked = true; f.revokedAt = now; f.revokeReason = 'source-deleted'
|
|
82
|
-
```
|
|
83
|
-
|
|
84
|
-
⇒ **只读不删**(注释明写「保留 provenance 与 revokedAt,只读不删(可审计、可追溯)」)。
|
|
85
|
-
|
|
86
|
-
**事实三:三条脏数据的 provenance 分别是** `mem_af41b679…`(记忆 id)、`epi_pre_66d51268…` / `epi_pre_010a543e…`(episode id)
|
|
87
|
-
⇒ 用 `revokeBySource` 清它们,**前提是先删掉那两个来源** —— 代价远大于清三条 fact。
|
|
88
|
-
|
|
89
|
-
**⇒ a-1 的三条候选路线(待用户拍板,勿自行选)**:
|
|
90
|
-
|
|
91
|
-
| | 做法 | 代价 | 评价 |
|
|
92
|
-
|---|---|---|---|
|
|
93
|
-
| **路 A** | 走 `storage-manage` 的 `delete`(`index.js:10476-10487`,级联 `factStore.revokeBySource`) | **要连带删掉那条记忆 / episode** | ❌ 副作用太大 |
|
|
94
|
-
| **路 B** | 新增 `revokeById(factId)`(按 id 直接撤销) | 动源码(新增方法 + 套件),但最小精准,**仍符合既有语义**(只读不删 + 留痕) | ⭕ 可选 |
|
|
95
|
-
| **路 C** | **不改数据,改行为**:让 `hubFlushTick` 的过滤条件**主动跳过脏 fact**(`subject`/`object` 命中清洗器判据即 skip) | **零数据改动**,脏条目留在盘上但**永远写不出去** | ✅ **倾向** |
|
|
96
|
-
|
|
97
|
-
**★ 更根本的判断**:⑩-a 的根因是「**fact 通路没清洗**」⇒ **先修 a-2/a-3,新脏数据即断源**。
|
|
98
|
-
**存量 3 条怎么处置,是独立的小决策**,不影响主线;且与 ⑨ 的取舍同源(**⑨ 时用户选的是「弃用不删」**)。
|
|
99
|
-
|
|
100
|
-
### 1.5 验收标准(可自动化)
|
|
101
|
-
1. `facts.json` 中 U+FFFD 计数 = 0
|
|
102
|
-
2. `facts.json` 中命中 `/Current DSH file policy|Approval prompts are disabled|Current runtime context|Retrieved memory ref/i` 的条数 = 0
|
|
103
|
-
3. 新套件断言:`crossFeed` 走 fact 时脏 intent **不落库**
|
|
104
|
-
4. 变异演示真红(把新加的清洗调用去掉 ⇒ 断言必须变红)
|
|
105
|
-
5. 全量回归 **0 失败**
|
|
106
|
-
|
|
107
|
-
### 1.6 风险与回退
|
|
108
|
-
- **风险:低**。不改注入路径、不改语义语料、不碰 `MEMORY.md`。
|
|
109
|
-
- **回退**:清洗器调用是**纯函数前置**,去掉即恢复原行为;数据侧有备份。
|
|
110
|
-
|
|
111
|
-
### 1.7 顺带记录(不在本次范围)
|
|
112
|
-
清洗后仍会看到 `XX | 有未决事项 | YY` 这类由 episode 自动派生的事实——**内容质量本身偏低**(它是「有未决事项」的机械模板,不是真的知识)。
|
|
113
|
-
**这是 ⑩-a 之后的独立话题**(可称 ⑩-a-2 「fact 质量」),需另立规划,不在本轮。
|
|
114
|
-
|
|
115
|
-
---
|
|
116
|
-
|
|
117
|
-
## 2. ⑩-b · 记忆文件侧的隐患(**本项只取证,不修**)
|
|
118
|
-
|
|
119
|
-
### 2.1 为什么「涉及语义」——三条管线的耦合(已代码核实)
|
|
120
|
-
|
|
121
|
-
`MEMORY.md` **不是纯文本**,它同时喂三条管线:
|
|
122
|
-
|
|
123
|
-
| # | 消费方 | 依据 |
|
|
124
|
-
|---|---|---|
|
|
125
|
-
| ① | **注入上下文**(整篇文本) | `lib/index.js:5498-5499` |
|
|
126
|
-
| ② | **L0 摘要抽取**(按 `<!-- memory:mem_<32hex> -->` 切条) | `lib/l0-extract-pre.js:34` |
|
|
127
|
-
| ③ | **语义语料**(锚点划字节区间 → `recordDigest`) | `lib/memory-anchor-pre.js:168/248`;复核在 `lib/m4-corpus-pre.js:107` |
|
|
128
|
-
|
|
129
|
-
**两条硬约束**:
|
|
130
|
-
- 文件级先比 `fileDigest`,不等则**跳过整个文件**(`m4-corpus-pre.js:96`,连逐条比对都不做);
|
|
131
|
-
- `sourceVersion` 随 `fileDigest` 变化 **+1**(`memory-anchor-pre.js:267`)⇒ 该文件**向量全部失效需重编码**。
|
|
132
|
-
|
|
133
|
-
**`memoryId` 是随机的**(`memory-anchor-pre.js:40` `newMemoryId()`)⇒ **就地改文件会换一批新 id**,
|
|
134
|
-
已积累的 `seen/read/cite` 证据、`supersededBy` 指针、白板锚点**全部对不上号**。
|
|
135
|
-
|
|
136
|
-
**正路**:sidecar 是**可重建的派生数据**(`note-status-pre.js:16` 明示)⇒ 改正文须配套走
|
|
137
|
-
`storage-manage-pre.js` 的 `repair` 重建 sidecar(**只重建 sidecar,不动正文**)。代价=向量重编码 + id 漂移。
|
|
138
|
-
**真正安全**:**追加式写入**(既有 `memory-writer` 事务路径),不改已有条目的字节布局。
|
|
139
|
-
**绝不可做**:改/删文件里的锚点标记——它是**切条唯一依据**,去掉后整篇退化为一个 legacy 块,`memoryId` 全丢。
|
|
140
|
-
|
|
141
|
-
### 2.2 ★★★ 尚未引爆的雷:**判断已被推翻 —— 它已经引爆过**(2026-09-19 子代理 A 取证 + 主代理独立复核)
|
|
142
|
-
|
|
143
|
-
> **⚠️ 本节原先写的是「实测尚未引爆」。该结论错误,已作废。**
|
|
144
|
-
> 错误根源:把「两份 `MEMORY.md` 里 `(M8 固化)` 计数为 0」当成了「没写过」的证据。
|
|
145
|
-
> **真相:写过了、而且是成功写入;后来被容量整理(compaction)移出正文、落进归档。**
|
|
146
|
-
|
|
147
|
-
**★ 铁证(主代理独立复核,非仅采信子代理)**:
|
|
148
|
-
|
|
149
|
-
| 证据 | 内容 |
|
|
150
|
-
|---|---|
|
|
151
|
-
| `archive/notes-archived.md:493-495` | `## DSH ������(M8 固化)` / `- has three modes:shadow ֻ��¼ …` / `- 来源:记忆中枢治理固化(confidence=0.92)` |
|
|
152
|
-
| 同文件 `:519-521` / `:684-686` / `:3168-3170` | 另 3 段(`让我自己去试吧…` / `是这样的,我后台在改语义模型…` / `Approval prompts are disabled…`),**均带 `(M8 固化)`** |
|
|
153
|
-
| `MEMORY.md.bak-20260917-G2` | 正文里**曾有** `## Approval prompts are disabled(M8 固化)` ⇒ 证明**当时确实在正文** |
|
|
154
|
-
| 格式唯一性 | `(confidence=` 这一写法全仓只出现在 `lib/index.js:8981` ⇒ 这些段落**只能由本通路产生** |
|
|
155
|
-
| 锚点存在 | 每段都带 `<!-- memory:mem_<32hex> -->` ⇒ 走的是 `appendText` **正经事务**,不是事后手工拼 |
|
|
156
|
-
|
|
157
|
-
**⇒ 结论:`count: 0` 不是「没写」的证据。**
|
|
158
|
-
`index.js:8962` `if (hubFlushState.date !== today) { …count = 0 }` —— `count` 按**记忆日**清零,
|
|
159
|
-
当前 `date:2026-09-19 / count:0` 只表示「**今天还没写**」。
|
|
160
|
-
|
|
161
|
-
**危险本质(现已确认,不再是假设)**:**facts 侧是脏的(见 §1.2),而这条通路会把脏数据写进 `MEMORY.md`** ——
|
|
162
|
-
**从「面板难看」升级为「污染注入 + 污染语义语料」,且已实际发生。**
|
|
163
|
-
归档里那段 `## DSH ������(M8 固化)` 就是活证据。
|
|
164
|
-
|
|
165
|
-
### 2.3 ★ 子代理 A 的其余关键发现(含行号,可复核)
|
|
166
|
-
|
|
167
|
-
| # | 发现 | 依据 |
|
|
168
|
-
|---|---|---|
|
|
169
|
-
| 1 | **两条 tick 的开关门控是彻底的**(关掉不写),**但定时器无条件创建**、开关关掉后仍每 30min 空转;**`hubBootTimer` 未纳入 disposer** | `index.js:8918/8958`(门控)· `8992-8993`(无条件创建)· `8999`(只 clear 两个 interval) |
|
|
170
|
-
| 2 | **6 道过滤全是「结构性」,没有一道是「内容卫生」** —— 不查脏数据、不查长度、不查换行、不查信封 | `index.js:8970-8974` |
|
|
171
|
-
| 3 | **`confidence: null` 直接放行**(`typeof null === 'object'` 判假)⇒ facts.json 里 3 条 null **全部放行** | `index.js:8972` |
|
|
172
|
-
| 4 | **`ttl: 0` = 永不过期** ⇒ 4 条全放行 | `index.js:8971` + `fact-store-pre.js:60` |
|
|
173
|
-
| 5 | **换行未归一化** ⇒ 一条 fact 可**伪造 `## ` 标题**,并被 `compactLegacyLayer` 的分段正则当成独立段落搬运 | `index.js:8981` · `4796` `if (m = ln.match(/^##\s+(.+)$/))` |
|
|
174
|
-
| 6 | **失败静默**:`catch (_) {}` 吞异常(`:8987`),而 `hubFlushSave()`(`:8989`)**在 try 外无条件执行** ⇒ 失败也照写 flush-state | `index.js:8987` / `8989` |
|
|
175
|
-
| 7 | **`flushed` 与 `count` 不自洽**:`cur.includes(subj)` 分支只置标记、不 `count++`;且该标记**永久生效**;且判定用**子串**(短主语易误命中) | `index.js:8980` vs `8984-8985`;`8962` 只清 count 不清 flushed |
|
|
176
|
-
| 8 | **`flushed` 永不清理** ⇒ 实际语义是「**生命周期总量 8 条**」而非「每日 8 条」;**归档后永不重写**(单向) | 全仓无 `flushed = {}` / `delete` |
|
|
177
|
-
| 9 | **无重入保护**(`void hubFlushTick()` 无 in-flight 标志);文件不撕裂靠 docStore `_queue` 串行,但 **flush-state.json 是裸 `writeFileSync`,无锁无 tmp+rename** | `index.js:8993` · `8895` · `memory-writer-pre.js:345-352/506` |
|
|
178
|
-
| 10 | **`hubFlushSave` 写失败被静默吞** ⇒ 重启后 `flushed` 回退 ⇒ **已写过的 fact 会被再写一遍**(正文出现两个同名 `(M8 固化)` 段落) | `index.js:8895` `catch (_) {}` |
|
|
179
|
-
| 11 | **可观察性缺失**:成功有 `diag`(`:8986`),**失败零留痕**(无 diag、无 degrade 台账,对照 `:5853` note-status 路径**有** `_degradePre.record`);`overview()` **不含 flush 字段** ⇒ 面板看不到「写了但没成功」 | `index.js:8986/8987` · `memory-hub-pre.js:192-220` |
|
|
180
|
-
|
|
181
|
-
### 2.4 待查清单(子代理 B 仍在运行)
|
|
182
|
-
|
|
183
|
-
**子代理 B —— `MEMORY.md` 全部写入者普查**(`88b6427e-5393-42b7-bed3-ee46c41e8ce6`):完整清单 → 是否走事务 → `memoryId` 命运 → sidecar 与 digest → 风险面 → 现有防线。
|
|
184
|
-
|
|
185
|
-
---
|
|
186
|
-
|
|
187
|
-
### 2.5 ★★★ ⑩-b 完整取证报告(2026-09-19 已完工)
|
|
188
|
-
|
|
189
|
-
> **`docs/internal/ISSUE10B-FORENSICS-20260919.md`** ← **⑩-b 的唯一权威文档,压缩后读它**
|
|
190
|
-
|
|
191
|
-
**两份只读子代理普查 + 主代理逐条复核的合并成果**,含:
|
|
192
|
-
|
|
193
|
-
| 节 | 内容 |
|
|
194
|
-
|---|---|
|
|
195
|
-
| §0 | 一句话结论:**20 条写入通路 × 3 条消费管线,中间没有任何联合把关** |
|
|
196
|
-
| §1 | 三条消费管线(注入 / L0 / 语料)+ 语料的两级静默丢弃 |
|
|
197
|
-
| §2 | **20 条写入者全景表** + 清洗门只接 5 处 + **全自动无人值守的只有 3 条** |
|
|
198
|
-
| §3 | 三个致命结构问题:`stripAnchorLines` 承诺不成立 · `reuse` 是死代码 · `sanitizeReservedSyntax` 去功能化 |
|
|
199
|
-
| §4 | M8 回写通路:**已引爆的物证** + 六道过滤 + 五个失败模式 |
|
|
200
|
-
| §5 | **主代理对子代理的三处纠正**(含「默认裸写」对用户本机不成立) |
|
|
201
|
-
| §6 | 风险排序 + **「自我放大回路」** |
|
|
202
|
-
| **§8** | **★ 修复方案 T0/T1/T2/T3 四层 + 执行顺序 + 验收标准 + 风险回退** |
|
|
203
|
-
|
|
204
|
-
**★ 最重要的一条新增认知(§6)**:
|
|
205
|
-
```
|
|
206
|
-
记忆文件字节 → m4-corpus-pre.js:118 取 text → index.js:8943 作 fact.subject
|
|
207
|
-
↓
|
|
208
|
-
hubFlushTick:8982 写回 MEMORY.md
|
|
209
|
-
↓
|
|
210
|
-
(若脏)再次成为语料来源 → 再固化 → 再写回
|
|
211
|
-
```
|
|
212
|
-
⇒ **脏数据不只是「落盘」,它会自己繁殖。**
|
|
213
|
-
|
|
214
|
-
**★ 关键洞察(§8.1)**:**⑩-a 的修法就是 ⑩-b 的主要止血手段** ——
|
|
215
|
-
`crossFeed` 的 fact 分支未清洗,既是 §1.2(面板乱码)的成因,也是 M8 回写脏数据的源头。
|
|
216
|
-
**给 fact 通路补清洗器,同时解决 ⑩-a 与 ⑩-b 的脏源。**
|
|
217
|
-
|
|
218
|
-
**★ 诚实评估紧急度(§8.0)**:急性事故已发生完毕(4 条已写入并归档),**没有持续流血**;
|
|
219
|
-
但 `count` 每日清零、`flushed` 永不清零 ⇒ **每个记忆日仍有最多 8 次脏写入机会**,且脏源持续产生。
|
|
220
|
-
⇒ **中高优先,应在进入前端前修完**(与用户既定节奏一致)。
|
|
221
|
-
|
|
222
|
-
### 2.4 取证完成后的流程(用户指定)
|
|
223
|
-
1. 把「它是什么、怎么干、会造成什么隐患」讲清楚 → **用户看**
|
|
224
|
-
2. 用户与 agent 一起定修法 → **修复方案再落一次盘**
|
|
225
|
-
3. 用户压缩上下文 → **agent 一次修完**
|
|
226
|
-
|
|
227
|
-
---
|
|
228
|
-
|
|
229
|
-
## 3. 执行序(用户 2026-09-19 指定)
|
|
230
|
-
|
|
231
|
-
```
|
|
232
|
-
[1] 本规划落盘 ← 本文件
|
|
233
|
-
[2] ⑩-a 修复(面板清晰可懂) ← 先做
|
|
234
|
-
[3] ⑩-b 全量取证(子代理,只读) ← 已并行启动
|
|
235
|
-
[4] 向用户讲清 ⑩-b 的隐患 → 用户拍板修法 → 修法落盘
|
|
236
|
-
[5] 用户压缩上下文 → agent 修完 ⑩-b
|
|
237
|
-
[6] 走到前端方向
|
|
238
|
-
[7] 前端做完 → GLM 全量审核 + 白皮书
|
|
239
|
-
```
|
|
240
|
-
|
|
241
|
-
**注意**:⑩-a 与 ⑩-b **不互相阻塞**:⑩-a 不碰 `MEMORY.md`、不碰语义语料。
|
|
242
|
-
但 ⑩-a 的清洗器修复**会降低 ⑩-b 的风险**(脏 fact 不再新增)。
|
|
243
|
-
|
|
244
|
-
---
|
|
245
|
-
|
|
246
|
-
## 4. 与最终方向的关系(用户 2026-09-19 补充)
|
|
247
|
-
|
|
248
|
-
⑩-b 不是孤例,而是**一类系统性风险的一次样本**:本插件的「记忆」不是普通文件,
|
|
249
|
-
而是**注入面 + 检索面 + 语义面共用的同一份字节**。任何一处写入都要同时满足三套约束,
|
|
250
|
-
而**历史实现是分批长出来的**——于是「一处漏清洗」「一处绕过事务」这类雷会零散地留着。
|
|
251
|
-
|
|
252
|
-
⇒ 这正是**前端做完后必须做「GLM 全量审核 + 白皮书」**的原因:
|
|
253
|
-
**逐条修只能清掉已知的雷,全量审核才能把「还有多少雷、都分布在哪一类通路」讲清楚**,
|
|
254
|
-
白皮书则是把这套「三面共用一份字节」的约束**显式成文**,让后续任何写入者都不再靠口口相传。
|