dsh-engram 0.7.2 → 0.8.6
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 +104 -7
- package/README.zh.md +13 -3
- package/docs/CONTEXT-GC-GUIDE.zh.md +15 -1
- package/docs/G6-quality-ab.zh.md +540 -0
- package/docs/GATES.zh.md +41 -0
- package/docs/PROPOSAL-evolution.zh.md +12 -0
- package/docs/RELEASE-NOTES-v0.7.10.md +49 -0
- package/docs/RELEASE-NOTES-v0.7.11.md +31 -0
- package/docs/RELEASE-NOTES-v0.7.12.md +22 -0
- package/docs/RELEASE-NOTES-v0.7.13.md +41 -0
- package/docs/RELEASE-NOTES-v0.7.14.md +37 -0
- package/docs/RELEASE-NOTES-v0.7.15.md +36 -0
- package/docs/RELEASE-NOTES-v0.7.16.md +42 -0
- package/docs/RELEASE-NOTES-v0.7.3.md +30 -0
- package/docs/RELEASE-NOTES-v0.7.4.md +71 -0
- package/docs/RELEASE-NOTES-v0.7.5.md +134 -0
- package/docs/RELEASE-NOTES-v0.7.6.md +74 -0
- package/docs/RELEASE-NOTES-v0.7.7.md +60 -0
- package/docs/RELEASE-NOTES-v0.7.8.md +56 -0
- package/docs/RELEASE-NOTES-v0.7.9.md +46 -0
- package/docs/RELEASE-NOTES-v0.8.0.md +59 -0
- package/docs/RELEASE-NOTES-v0.8.1.md +70 -0
- package/docs/RELEASE-NOTES-v0.8.2.md +62 -0
- package/docs/RELEASE-NOTES-v0.8.3.md +65 -0
- package/docs/RELEASE-NOTES-v0.8.4.md +56 -0
- package/docs/RELEASE-NOTES-v0.8.5.md +60 -0
- package/docs/RELEASE-NOTES-v0.8.6.md +71 -0
- package/eval/gate-audit.mjs +538 -0
- package/lib/api.js +70 -0
- package/lib/approvals.js +84 -0
- package/lib/boundary-prune.js +71 -5
- package/lib/capsule.js +134 -0
- package/lib/capture.js +85 -6
- package/lib/client.js +1813 -1745
- package/lib/compaction.js +4 -11
- package/lib/context-gc.js +63 -2
- package/lib/episode.js +10 -5
- package/lib/index-block.js +89 -8
- package/lib/index.js +108 -21
- package/lib/ledger.js +200 -10
- package/lib/pack-telemetry.js +10 -3
- package/lib/preference.js +2 -1
- package/lib/prune.js +89 -0
- package/lib/session-events.js +168 -0
- package/lib/skill-export.js +289 -0
- package/lib/status-insights.js +5 -0
- package/lib/status.js +138 -0
- package/lib/store.js +34 -0
- package/lib/todo-sink.js +3 -2
- package/lib/tools.js +2 -1
- package/lib/usage.js +32 -3
- package/lib/web-provision.js +59 -14
- package/package.json +15 -8
- package/scripts/dsh-engram.mjs +229 -4
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# dsh-engram v0.7.10
|
|
2
|
+
|
|
3
|
+
**归因对称性**:宿主自己剪的枝,我们既不认领它的节省,也不替它付前缀失效的钱。
|
|
4
|
+
|
|
5
|
+
## 1. 15 小时后的账本(5 条会话行)
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
session-775f5173 (220 req) avoid=0 inj=609,289 rw=1,224,725 engram=0 host=51,168 net=-1,711,542
|
|
9
|
+
session-746c0362 (383 req) avoid=7,795,950 inj=749,802 rw=316,516 engram=7,841 host=13,032 net=+6,761,284
|
|
10
|
+
其余 3 行 avoided=0(会话内从未剪枝/压缩)→ 结构性为负
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
两个信号:
|
|
14
|
+
|
|
15
|
+
1. **host 腿在真实使用中长到了 64,200 字符**(两个会话,我们全程没参与);
|
|
16
|
+
2. **engram 腿停在 7,841**——15 小时内只剪到过那一次。
|
|
17
|
+
|
|
18
|
+
## 2. 这次修的是记账不对称
|
|
19
|
+
|
|
20
|
+
v0.7.9 的观察者对宿主剪枝做了两件事:计入 `hostPrunedChars`(对),**并且**按
|
|
21
|
+
`estimatePrefixChars + removed` 记 `rewriteCount/rewriteChars`(错)。后果:
|
|
22
|
+
|
|
23
|
+
- 宿主剪 51,168 字符 → 我们账上多出 1,224,725 字符的「前缀失效成本」(每次约 5 万 ×24 次);
|
|
24
|
+
- 而宿主省下的东西我们不认领 → `avoidedChars` 仍是 0;
|
|
25
|
+
- 于是一个**我们全程没动过**的会话,净值 −1.71M。
|
|
26
|
+
|
|
27
|
+
这是双重记账的单边化:要么两侧都认,要么两侧都不认。选择后者——engram 的净收益只问
|
|
28
|
+
「engram 造成的成本与节省」,宿主的行为对我们既非收入也非支出。
|
|
29
|
+
|
|
30
|
+
- `notePrune(source: "host")` 只加 `hostPrunedChars`,不碰 `rewriteCount/rewriteChars/avoidedChars`。
|
|
31
|
+
- 测试:宿主剪枝不再产生 rewrite 计数(19/19 账本测试,441/441 全量)。
|
|
32
|
+
|
|
33
|
+
## 3. 对 L1 的判断(判据提前触发)
|
|
34
|
+
|
|
35
|
+
交接时定的判据是「host 腿长期远大于 engram 腿 → L1 该退场或只留阈值覆盖」。15 小时的数据已经
|
|
36
|
+
很难看:
|
|
37
|
+
|
|
38
|
+
- 一个 220 请求的真实会话,宿主自剪 4 次、共 51,168 字符;
|
|
39
|
+
- engram 的边界剪枝在那个会话里**一次都没触发**(只有 1 次 todo 写入,且没有完成项跨过水位线);
|
|
40
|
+
- 而在有 todo 的会话里,我们剪到的那一次(7,841 字符)**确实抢在了宿主下一次装配之前**。
|
|
41
|
+
|
|
42
|
+
结论:L1 在「用 todo/goal 的长会话」里还有价值,在普通会话里完全空转。这不是 bug,是
|
|
43
|
+
边界源的覆盖率问题(L0 语料早就显示 ~62% 的会话没有任何 todo 进度)。默认开关要不要关,
|
|
44
|
+
取决于是否接受「只有规划型会话受益」。
|
|
45
|
+
|
|
46
|
+
## 已知边界
|
|
47
|
+
|
|
48
|
+
- 已经写进 `ledger.json` 的历史 rewrite 数字不会被追溯修改(那是一段有偏的记录,留着当证据)。
|
|
49
|
+
- host 腿仍用 `shadowedTokenCount × 4` 估算字符。
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# dsh-engram v0.7.11
|
|
2
|
+
|
|
3
|
+
**L1 默认关闭,改为 opt-in。** 这不是修 bug,是承认结论。
|
|
4
|
+
|
|
5
|
+
## 1. 判决依据(两天的真机账本)
|
|
6
|
+
|
|
7
|
+
| 会话 | 请求 | 宿主自剪 | engram 自剪 | 备注 |
|
|
8
|
+
|---|---|---|---|---|
|
|
9
|
+
| `775f5173…` | 220 | 51,168 字符(4 次) | **0** | 只写过 1 次 todo,无完成项跨水位线 |
|
|
10
|
+
| `746c0362…` | 383 | 13,032 字符 | 7,841 字符(1 次) | 有 todo 进度,边界真的 fire 了 |
|
|
11
|
+
|
|
12
|
+
链条本身是通的——v0.7.7 修好工作区键(闸门),v0.7.8 修好 `session.append`
|
|
13
|
+
重入(执行),v0.7.9 修好两条腿归因(记账)。通的链条 + 空转的覆盖率:
|
|
14
|
+
|
|
15
|
+
- 宿主在**没有 engram 的情况下**就会剪(0.2.x 内置行为),普通会话里它全干完了;
|
|
16
|
+
- 我们的边界源只认 todo 完成 / goal 终态,而 ~62% 的会话根本没有 todo 进度;
|
|
17
|
+
- 有 todo 的会话里,我们确实**抢在宿主下一次装配之前**剪到了东西(7,841 字符)。
|
|
18
|
+
|
|
19
|
+
## 2. 改了什么
|
|
20
|
+
|
|
21
|
+
- `boundaryPrune` 默认 `false`(`resolveConfig` 与设置卡文案同步:需手动打开);
|
|
22
|
+
- README 的 L1 小节改标题为 opt-in,并写入两天的实测对照;
|
|
23
|
+
- `test/settings.test.mjs` 的默认断言反转,并写明依据(不再是一句「v0.6 决定」);
|
|
24
|
+
- `resultPack` 仍默认开——没有 `boundaryPrune` 时它自己退场(已有逻辑,无行为变化)。
|
|
25
|
+
|
|
26
|
+
## 3. 为什么不是「再调参看看」
|
|
27
|
+
|
|
28
|
+
idle cadence 那条路(每 N 请求触发)也能让普通会话开火,但它剪的是宿主**下一步就会自己剪**的
|
|
29
|
+
东西——重复劳动,只多付一次前缀失效。真正的问题不是触发条件太窄,是**宿主已经把这个活干了**。
|
|
30
|
+
|
|
31
|
+
留 opt-in 的理由只有一个:规划型长会话里,早剪确实比宿主压力驱动的晚剪省得多。
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# dsh-engram v0.7.12
|
|
2
|
+
|
|
3
|
+
**把 v0.7.11 的结论做成一行可读的证据**,省得下一个人(或三个月后的我)重走一遍。
|
|
4
|
+
|
|
5
|
+
## 改了什么
|
|
6
|
+
|
|
7
|
+
- `npx dsh-engram status` 新增一行:
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
账本两条腿:engram 剪枝 7841 字符 · 宿主自剪 64200 字符(宿主腿远大于 engram 腿 → 先确认边界源有没有覆盖到你的用法)
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
三种判词:宿主全干 / 宿主腿远大于 engram 腿 / 持平。这行**不依赖状态快照**(读的是账本,
|
|
14
|
+
不是插件启动状态),所以没跑过 web 也能看。
|
|
15
|
+
- 设置卡 `boundaryPrune` 的提示补一句:打开前先看这行,「宿主腿远超 engram 腿时,打开只是重复劳动」。
|
|
16
|
+
- 测试:CLI 集成测试固定这条输出(442/442)。
|
|
17
|
+
|
|
18
|
+
## 为什么值得单独发一版
|
|
19
|
+
|
|
20
|
+
v0.7.11 关掉 L1 是个**判断**,判断会过期——宿主换版本、边界源改覆盖率、idle cadence 有了新证据,
|
|
21
|
+
结论都可能翻。把判断的依据做成一条随时可读的输出,比写在发布说明里更可靠:发布说明没人重读,
|
|
22
|
+
`status` 会被顺手跑。
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# dsh-engram v0.7.13
|
|
2
|
+
|
|
3
|
+
**账本终于记得住东西了。** 两个把「证据」变成零的 bug,都在重启后才看得见。
|
|
4
|
+
|
|
5
|
+
## 1. 压缩入账:永远是 0,因为读错了事件的形状
|
|
6
|
+
|
|
7
|
+
`committedGcSaving()` 读 `event.summary` / `event.shadowedTokenCount`,而 DSH 真正落盘的
|
|
8
|
+
`compaction/summary` 长这样:
|
|
9
|
+
|
|
10
|
+
```json
|
|
11
|
+
{ "type": "compaction/summary", "seq": 7, "time": 1,
|
|
12
|
+
"data": { "compactionId": "…", "summary": [...], "shadowedTokenCount": 83371, … } }
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
结果:**本机 137 个已提交 checkpoint 里 82 个是 Context GC 写的,一个都没入账**(若按真实
|
|
16
|
+
形状重放,它们合计 29,629,411 字符 ≈ 7.4M token)。单元测试一直是绿的,因为它自己造了一个
|
|
17
|
+
扁平事件——和 v0.7.8 的重入 bug 同一类:测试的假宿主比真宿主宽容。
|
|
18
|
+
|
|
19
|
+
修法:优先读 `event.data`,扁平形状保留兼容;wiring 测试改成发真实形状的事件。
|
|
20
|
+
|
|
21
|
+
## 2. 重启会把会话的剪枝计数清零
|
|
22
|
+
|
|
23
|
+
`flushSession()` 里写的是 `row.prunedChars = session.prunedChars`(绝对值),而重启后
|
|
24
|
+
内存计数从 0 开始 → **重启后的第一次 flush 把持久行的剪枝历史覆盖成 0**。
|
|
25
|
+
今天 10:04 就这么把本会话那 7,841 字符抹掉了(快照里现在还是 0)。
|
|
26
|
+
|
|
27
|
+
修法:三个计数(pruned/hostPruned/compacted)改成 delta 累加,并在 `ensure()` 时从持久行
|
|
28
|
+
回填种子。回归测试新增 `a restart does not zero a session's prune counters`。
|
|
29
|
+
|
|
30
|
+
## 3. 附带
|
|
31
|
+
|
|
32
|
+
- CLI 的 `readLedger()` 现在尊重 `DSH_HOME`(此前在沙箱里也会读真实账本,测试因此一直
|
|
33
|
+
在验证真机数据)。
|
|
34
|
+
- `npx dsh-engram status` 的「L1 未武装」告警区分「默认关闭」与「服务坏了」,前者不再报警。
|
|
35
|
+
|
|
36
|
+
## 已知边界
|
|
37
|
+
|
|
38
|
+
- 已被清零的历史行不会自动恢复(`sessions` 里的 0 是真的写坏了;`workspaces` 桶里还留着
|
|
39
|
+
一部分合计值)。从本版起不再发生。
|
|
40
|
+
- G1/G1b 这两个门之前 FAIL 有很大一部分是上面两个 bug 造成的,重启后应重跑
|
|
41
|
+
`npm run gate:audit` 再判断。
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# dsh-engram v0.7.14
|
|
2
|
+
|
|
3
|
+
**G7 从「未测」变成数字,G5 从「未测」变成 PASS,README 版本矩阵补齐到本期。**
|
|
4
|
+
|
|
5
|
+
## 1. G7:账本自身开销
|
|
6
|
+
|
|
7
|
+
新增 `npm run bench:ledger`([eval/ledger-bench.mjs](eval/ledger-bench.mjs)):在 200 个并发会话的账本上量每个热路径调用,分母用**本机真实 step 延迟**(最近 6 个会话 2,388 个 `step/start → step/end`,p50 5,080ms、p95 39,851ms),并额外给一个**严格下限**(300ms 请求,不含工具执行时间):
|
|
8
|
+
|
|
9
|
+
| 操作 | 每次 | 占 p50 step |
|
|
10
|
+
|---|---|---|
|
|
11
|
+
| `tick()` | 0.22µs | 0.0000% |
|
|
12
|
+
| `noteInjection()` | 0.10µs | 0.0000% |
|
|
13
|
+
| `notePrune()`(含一次落盘) | 360µs | 0.0071% |
|
|
14
|
+
| `estimatePrefixChars()`(300 条消息 ≈120KB) | 391µs | 0.0077% |
|
|
15
|
+
| `summary()` | 224µs | 0.0044% |
|
|
16
|
+
| `tick + flush`(原子写) | 162µs | 0.0032% |
|
|
17
|
+
|
|
18
|
+
最坏一项(一次边界剪枝 = 剪枝 + 前缀遍历)**0.0126% of p50 step · 0.2137% of 300ms**,门是 0.5% → **PASS**。
|
|
19
|
+
|
|
20
|
+
用 p50 step 作分母会被长工具调用稀释,所以判据用严格下限:**即使按一次 300ms 的快速请求算,账本也只占 0.21%**。
|
|
21
|
+
|
|
22
|
+
`gate-audit` 新增 `--g7 <pct>`,把这个数字喂回门表(`--g7 0` 之外不传仍是「未测」——不猜)。
|
|
23
|
+
|
|
24
|
+
## 2. G5:其实是能测的
|
|
25
|
+
|
|
26
|
+
之前一直「未测」,原因是 `--api` 少了 `?workspace=`。用
|
|
27
|
+
`--api "http://127.0.0.1:3080/api/dsh-engram?workspace=<ws>"` 实测 **681c ≤ 700c → PASS**。
|
|
28
|
+
|
|
29
|
+
## 3. README 版本矩阵
|
|
30
|
+
|
|
31
|
+
`0.7.2` 之后整段缺失(还停在「0.7.2 prepared」)。现补齐 `0.7.3`(未发布)/ `0.7.4`(npm)/
|
|
32
|
+
`0.7.5–0.7.9` / `0.7.10–0.7.12` / `0.7.13`(本期),并标清「published」与「本地」。
|
|
33
|
+
|
|
34
|
+
## 门表现状
|
|
35
|
+
|
|
36
|
+
`G1 FAIL 20% · G1b FAIL 33% · G2 PASS 78.8% · G4 PASS 0% · G5 PASS 681c · G7 PASS 0.21% · G6 未测`。
|
|
37
|
+
G1/G1b 要等 v0.7.13 的压缩入账真正落地一次之后再判——此前它们是「记账坏了」的读数,不是产品的读数。
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# dsh-engram v0.7.15
|
|
2
|
+
|
|
3
|
+
**G1 换分母:只算「有节省机会的会话」。** 这是口径修订,不是调参。
|
|
4
|
+
|
|
5
|
+
## 1. 为什么
|
|
6
|
+
|
|
7
|
+
G1 原来的分母是「所有账本行」。问题在于:一个没有剪枝、没有压缩的会话,
|
|
8
|
+
**只有注入支出、没有节省入账,净值结构上必为负**——把它算进分母,测的是
|
|
9
|
+
「机制在这个会话里跑没跑」(那是 G2 的事),不是「跑起来后省没省到」。
|
|
10
|
+
|
|
11
|
+
本机 14 天实测:挂载会话 33 个,其中 **21 个(64%)确实发生过剪枝或压缩**。剩下 36%
|
|
12
|
+
无论产品多好都算负,永远拉低 G1。
|
|
13
|
+
|
|
14
|
+
## 2. 改成什么
|
|
15
|
+
|
|
16
|
+
- 分母 = 账本行里 `prunedChars + compactedChars > 0` 的会话(有节省机会);
|
|
17
|
+
- 机会会话 < 5 时仍然只报数值、不给判定(一个会话不是分布);
|
|
18
|
+
- 备注里同时给出「N/M 机会会话(共 K 行)」,全量数据不隐藏;
|
|
19
|
+
- G1b(长会话 ≥200 请求)同样换成机会会话。
|
|
20
|
+
|
|
21
|
+
已写进 [PROPOSAL §6](PROPOSAL-evolution.zh.md) 的指标表,`gate-audit` 的输出脚注同步。
|
|
22
|
+
|
|
23
|
+
## 3. 立刻的后果:G1 从 FAIL 变成「未测」
|
|
24
|
+
|
|
25
|
+
当前账本里 5 行,机会会话 **0** 个——因为:
|
|
26
|
+
|
|
27
|
+
- 压缩入账直到 v0.7.13 才真正能落地(事件形状 bug);
|
|
28
|
+
- 会话级剪枝计数被重启清零过(v0.7.13 才修)。
|
|
29
|
+
|
|
30
|
+
也就是说,之前那个「G1 FAIL 20%」读的是**记账坏了**,不是产品差。「未测」比一个假
|
|
31
|
+
FAIL 更诚实。等 v0.7.13 的压缩入账真机落地、机会会话攒到 5 个,G1 才会给出第一个
|
|
32
|
+
有意义的判定。
|
|
33
|
+
|
|
34
|
+
## 4. 门表现状
|
|
35
|
+
|
|
36
|
+
`G1 未测(0/0 机会会话 · 共 5 行) · G1b 未测 · G2 PASS 78.8% · G4 PASS · G5 PASS 681c · G7 PASS 0.21% · G6 未测`
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# dsh-engram v0.7.16
|
|
2
|
+
|
|
3
|
+
**G6 有了测量装置,并且自己先承认「跑 1 对不算数」。**
|
|
4
|
+
|
|
5
|
+
## 1. G6 是唯一不能从日志里算出来的门
|
|
6
|
+
|
|
7
|
+
日志能回答「机制跑了没有、省了多少」,回答不了「任务变容易了还是变难了」。
|
|
8
|
+
后者只能同题同种子跑两臂。
|
|
9
|
+
|
|
10
|
+
新增 `npm run quality:ab`([eval/quality-ab.mjs](eval/quality-ab.mjs)):
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
node eval/quality-ab.mjs prepare --arm on=ab-engram,off=ab-bare --home ~/.dsh/quality-ab
|
|
14
|
+
DSH_HOME=~/.dsh/quality-ab dsh plugin --profile ab-engram add ~/.dsh/vendor/dsh-engram-0.7.16.tgz
|
|
15
|
+
node eval/quality-ab.mjs run --tasks tasks.json --workspace ./fixture \
|
|
16
|
+
--arm ab-engram,ab-bare --seeds 3 --out runs/g6 --home ~/.dsh/quality-ab
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
- 每个 run 复制一份 fixture workspace,先跑 `setup`,再 `dsh headless` 跑任务;
|
|
20
|
+
- 通过与否由任务的 `check` **命令**判定(退出码 0),不引入第二个 LLM 当裁判;
|
|
21
|
+
- Δ 用**配对**算(同一 task×seed 两臂都跑完才计入),并给出 only-A / only-B 胜场;
|
|
22
|
+
- 每次 run 的 `run.log` / `check.log` 全留档,判定可审计。
|
|
23
|
+
|
|
24
|
+
协议与语料挑选原则:[docs/G6-quality-ab.zh.md](G6-quality-ab.zh.md)。
|
|
25
|
+
|
|
26
|
+
## 2. 门槛:< 10 对配对一律「未测」
|
|
27
|
+
|
|
28
|
+
和 G1 的「行数 < 5 不出判定」同一条纪律。真机冒烟跑 1 任务 × 2 臂,
|
|
29
|
+
报告写的是 **未测(1 对配对 — 需要 ≥ 10)**,而不是 Δ=0.0pp 的假 PASS。
|
|
30
|
+
|
|
31
|
+
## 3. 两个实现细节,都是踩过的坑
|
|
32
|
+
|
|
33
|
+
- **凭据**:`dsh headless` 没有 web 的 Models 页,provider 路由从环境变量取 key。
|
|
34
|
+
沙箱 `DSH_HOME` 里放 `~/.dsh/.credentials.yaml` 的符号链接/拷贝**都不够**;
|
|
35
|
+
harness 直接把 `DEEPSEEK_API_KEY` 读进子进程环境(只进内存,不进报告)。
|
|
36
|
+
两处都没有就立刻报错,不跑出一串 MISSING_CREDENTIAL。
|
|
37
|
+
- **profile 创建**:`dsh --profile <name> --from-default-profile headless --dump-config`
|
|
38
|
+
才能把 one-shot 模板实例化成自定义 profile;直接 `dsh --profile <name>` 会因为没有该 profile 而失败。
|
|
39
|
+
|
|
40
|
+
## 4. 门表现状
|
|
41
|
+
|
|
42
|
+
`G1 未测(0/0 机会会话 · 共 5 行) · G2 PASS 78.8% · G4 PASS · G5 PASS 681c · G7 PASS 0.21% · G6 未测(装置就绪,缺一次真实配对实验)`
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# dsh-engram v0.7.3
|
|
2
|
+
|
|
3
|
+
自 v0.7.2 以来:**设置卡搬到「Engram 记忆 → 设置」tab**——DSH 0.2.1 删掉了
|
|
4
|
+
`settings.plugin.item` 槽位,旧位置不再渲染,卡片在 GUI 里彻底不可见(宿主 API
|
|
5
|
+
`/api/dsh-engram/settings` 一直正常,只是没有挂载点)。
|
|
6
|
+
|
|
7
|
+
## 根因
|
|
8
|
+
|
|
9
|
+
`settings.plugin.item` 是 DSH ≤0.2.0 时代「插件 → 插件配置」tab 的插槽;`0.2.1-rc.2`
|
|
10
|
+
把插件配置改成按 entry-id 的通用表单(插件管理页里的行详情),旧插槽在运行时不再声明
|
|
11
|
+
(只剩 `dsh-client-ui-settings-models` 里的一份类型声明)。插件把卡片注册进这个槽位,
|
|
12
|
+
`ctx.slots.inject` 因此在浏览器里静默失败,卡片从未挂载——用户看到的
|
|
13
|
+
「Engram 记忆」页只有 记忆 / ESR / 注入预览 / 遥测 四个 tab。
|
|
14
|
+
|
|
15
|
+
## 改动
|
|
16
|
+
|
|
17
|
+
- 「Engram 记忆」页新增 **「设置」tab**,直接渲染同一张 `EngramConfigCard`(默认展开);
|
|
18
|
+
共享 `EngramScopeImpl`(`/api/dsh-engram/settings`)传输,读写落在同一份 DSH 设置文档,
|
|
19
|
+
写回后 loader 提交 volatile ref → 插件热同步 `resolved`(与之前完全一致)。
|
|
20
|
+
- `EngramConfigCard` 新增 `defaultOpen`:设置页里展开,旧槽位里保持折叠的旧行为。
|
|
21
|
+
- 保留 `settings.plugin.item` 注册(DSH ≤0.2.0 上仍可见),失败仍只 `console.warn`。
|
|
22
|
+
- 设置 tab 下隐藏顶部「工作区」筛选(该 view 与工作区无关)。
|
|
23
|
+
- 文档:README 中英改为「设置 → Engram 记忆 → 设置 tab」,并注明旧座位仅 DSH ≤0.2.0。
|
|
24
|
+
|
|
25
|
+
## 验证
|
|
26
|
+
|
|
27
|
+
`npm run typecheck` 干净;`npm test` 406/406;`npm run build:client` 重新产出
|
|
28
|
+
`lib/client.js`(设置 tab、`setView("config")`、`defaultOpen` 均在产物中);
|
|
29
|
+
`/api/dsh-engram/settings` 实测返回 `autoWebCompaction=true · gcReplacesCompaction=true ·
|
|
30
|
+
gcNarrative=true`(与 profile patch 一致,无「卡片说 false、实际 true」的分裂)。
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# dsh-engram v0.7.4
|
|
2
|
+
|
|
3
|
+
自 v0.7.3 以来:**修复「推送腿」在 DSH 0.2.x 上整条静默失效**。`[RECALL]` 启动召回、
|
|
4
|
+
会话 episode、`[RESUME]` 恢复简报、会话结束 todo 沉淀——四个 v0.6/v0.7 的招牌机制,
|
|
5
|
+
在真机上**一次都没有触发过**,而它们的单测全绿。
|
|
6
|
+
|
|
7
|
+
## 根因(宿主 API 漂移 + 静默归零,第二次)
|
|
8
|
+
|
|
9
|
+
DSH ≤0.1.x 把会话日志暴露成 `session.events`(一个冻结数组)。**DSH 0.2.x 删掉了这个
|
|
10
|
+
getter**——`Session.prototype` 现在只有 `eventAt` / `snapshotEvents` / `ownEvents` /
|
|
11
|
+
`deriveEventMessage`,宿主内部的同步全量读已被标记 deprecated,官方路径是
|
|
12
|
+
`ctx.sessionQuery.live(session)` 租约。
|
|
13
|
+
|
|
14
|
+
插件里 6 处 `Array.isArray(session.events)` 因此全部拿到 `[]`:
|
|
15
|
+
|
|
16
|
+
| 位置 | 后果 |
|
|
17
|
+
|---|---|
|
|
18
|
+
| `index-block.firstUserMessageText` | 取不到首条用户消息 → 查询串为空 → `[RECALL]` 永不渲染 |
|
|
19
|
+
| 同上(`pendingRecall` 分支) | 每轮都「等下一轮再试」→ `[ENGRAM]` 块**永不缓存**,每轮重渲染;注入体积也不进账本(净收益被高估) |
|
|
20
|
+
| `episode.buildEpisode` | 永远返回 `null` → 0 条 episode → `[RESUME]` 永不触发 |
|
|
21
|
+
| `todo-sink.pendingTodosFromSession` | 永远读不到 `todo/write` → 会话结束不沉淀任何 ESR draft |
|
|
22
|
+
| `capture` / `tools` / `preference` 的 `seq` 戳 | 溯源恒为 `seq: 0`(可审计性的最小字段失效) |
|
|
23
|
+
|
|
24
|
+
真机数据(本机 store,286 条记忆):`kind` 分布里 **0 条 `episode`**、**0 条
|
|
25
|
+
`preference`**、只有 4 条带锚点——与「机制存在 ≠ 机制被调用」的旧教训完全一致。
|
|
26
|
+
单测之所以没发现:它们都 mock 了 `{ events: [...] }`,测的是旧宿主的形状。
|
|
27
|
+
|
|
28
|
+
## 修复
|
|
29
|
+
|
|
30
|
+
- 新增 **`lib/session-events.js`**:跨代际读取会话日志,按顺序取第一个可用路径
|
|
31
|
+
1. `session.events`(≤0.1.x 与所有单测替身)→ 2. `ctx.sessionQuery.live()` 租约
|
|
32
|
+
(≥0.2.x 官方路径,读完立即 dispose)→ 3. `session.snapshotEvents(0, seq)`
|
|
33
|
+
(无 ctx 时的兜底)→ 4. `[]`。
|
|
34
|
+
同时导出 `sessionEventsSource()`(`legacy` / `sessionQuery` / `snapshotEvents` /
|
|
35
|
+
`none`)与 `sessionSeq()`(溯源 seq,优先 `session.seq`)。
|
|
36
|
+
- 6 处读点全部改接:`firstUserMessageText`、`buildEpisode`、`pendingTodosFromSession`、
|
|
37
|
+
`episode` 写入的 seq、`capture` / `tools` / `preference` 的 seq。
|
|
38
|
+
- **不再无限重试**:`[RECALL]` 的 `pendingRecall` 现在只在「日志可读但首条用户消息还没
|
|
39
|
+
落地」时重试;日志彻底不可读时只告警一次并照常缓存 `[ENGRAM]` 块——恢复前缀稳定性,
|
|
40
|
+
也避免每轮重渲染。
|
|
41
|
+
- 新增一次性告警:`engram: this host exposes no readable session event log … —
|
|
42
|
+
[RECALL] / episode / todo-sink stay dormant`(把静默降级改成可观测)。
|
|
43
|
+
|
|
44
|
+
## 验证
|
|
45
|
+
|
|
46
|
+
- `test/session-events.test.mjs` 10 例:所有用例的 session 替身**故意不暴露 `.events`**
|
|
47
|
+
(只给 `seq` / `eventAt` / `snapshotEvents`,与 0.2.x 原型一致),覆盖 legacy /
|
|
48
|
+
lease(含 dispose 断言)/ 兜底 / lease 抛错 / 完全不可读 / seq 取值,以及
|
|
49
|
+
`[RECALL]` 取词、`buildEpisode`、`pendingTodosFromSession` 三条端到端回归。
|
|
50
|
+
一旦有人改回 `session.events`,这组用例会红而不是静默归零。
|
|
51
|
+
- `npm test` 416/416;`npm run typecheck` 干净。
|
|
52
|
+
- 交叉核对真机形状:`Session.prototype` 仅含 `eventAt/snapshotEvents/ownEvents/
|
|
53
|
+
deriveEventMessage`;真实会话文件里的 `user/message` 事件形状与读取逻辑一致
|
|
54
|
+
(`data.content[].text`)、`todo/write` 仍存在于 0.2.1。
|
|
55
|
+
- **未做实机重启验证**:修复要重启 `dsh web` 才生效,而本次修复是在同一进程里做的
|
|
56
|
+
(重启会终结当前会话)。验证方式:重启后新会话的系统提示里应出现 `[RECALL]`,
|
|
57
|
+
且 `/stats` 的 `recallInjection` 计数开始增长。
|
|
58
|
+
|
|
59
|
+
## 升级步骤
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
# 插件走 file: 链接,改完代码重启 web 面即可
|
|
63
|
+
# (设置文档不需要迁移)
|
|
64
|
+
dsh web # 或在你原来的终端里 Ctrl-C 后重新 dsh --profile web
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## 由此改变的产品判断
|
|
68
|
+
|
|
69
|
+
本提案「拉取式被证伪」的实测(store 123 / recall 24 / detail 8)**有一部分是假的**:
|
|
70
|
+
推送腿当时根本没接线。重启后 1–2 周的账本与激活漏斗,才是「推送 vs 拉取」的第一份
|
|
71
|
+
干净数据——在它出来之前,不应据此再加新机制。
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
# dsh-engram v0.7.5
|
|
2
|
+
|
|
3
|
+
自 v0.7.4 以来:**账本按会话落行**,并落地 `gate-audit`——把
|
|
4
|
+
[PROPOSAL-evolution.zh.md](PROPOSAL-evolution.zh.md) §6 预声明的门从「口号」变成
|
|
5
|
+
「能在本机算出数字的表格」。顺手修掉了让这些门永远读 0 的第三个静默 bug。
|
|
6
|
+
|
|
7
|
+
## 1. 账本按会话落行(`tsk_b9` 前半)
|
|
8
|
+
|
|
9
|
+
v0.6 的账本按 `workspace × 天` 汇总,于是 §6 的第一道门
|
|
10
|
+
(「净收益 > 0 的会话 ≥ 50%」)**在算术上无法回答**——分母被抹掉了。
|
|
11
|
+
|
|
12
|
+
- `ledger.json` 新增 `sessions: { <sessionId>: row }`,与日汇总**写同一份 delta**:
|
|
13
|
+
`requests / injectedChars / avoidedChars / rewriteCount / rewriteChars`,
|
|
14
|
+
另存 `prunedChars`、`compactedChars`(唯一只存在于会话级的两个字段)、
|
|
15
|
+
`workspace`、`firstDay`/`lastDay`、`updatedAt`。
|
|
16
|
+
- 行数与日汇总同步裁剪:`maxSessionRows`(默认 500),按 `updatedAt` 保留最新;
|
|
17
|
+
会话行**永不**参与 `totals` 计算,只是门审计的分母。
|
|
18
|
+
- `summary()` / `readLedger()` 都返回 `sessions`(最近 50 / 全部),
|
|
19
|
+
**未 flush 的活跃会话也会出现在 `summary()` 里**(临时合并 delta,不改状态)。
|
|
20
|
+
- 兼容老文件:缺 `sessions` 字段时按空表处理,不迁移、不重写历史。
|
|
21
|
+
|
|
22
|
+
## 2. `gate-audit`:六个门可计算(`tsk_b9` 后半)
|
|
23
|
+
|
|
24
|
+
新增 **`eval/gate-audit.mjs`**(`npm run gate:audit`),纯读本地数据、零模型、零网络:
|
|
25
|
+
|
|
26
|
+
| 数据源 | 用途 |
|
|
27
|
+
|---|---|
|
|
28
|
+
| `~/.dsh/engram/ledger.json` | 每会话净收益 → G1 / G1b |
|
|
29
|
+
| `~/.dsh/sessions/**/session*.jsonl.zstd` | 挂载会话、engram/esr 工具调用、`[RECALL]` 注入标记、recall 命中 → G2 / G2b / G3 |
|
|
30
|
+
| `~/.dsh/storages/dsh_engram.json` | 被 supersede 的行占比 → G4 |
|
|
31
|
+
| `--api <base>?workspace=<ws>` | 运行中宿主的 `/preview` → G5(渲染后索引字符数) |
|
|
32
|
+
|
|
33
|
+
判定只允许三种值:`PASS` / `FAIL` / **`未测`**。没有数据就说没数据——G6(配对任务
|
|
34
|
+
质量差)与 G7(账本时延开销)永远 未测,除非你跑配对实验/基准;脚本不会替你猜。
|
|
35
|
+
|
|
36
|
+
用法:
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
npm run gate:audit # 终端一行一门
|
|
40
|
+
npm run gate:audit -- --md # markdown 表格
|
|
41
|
+
npm run gate:audit -- --out docs/GATES.zh.md # 落盘
|
|
42
|
+
npm run gate:audit -- --api http://127.0.0.1:3080/api/dsh-engram # G5 取真值
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## 3. 修掉第三个静默归零:v4 会话日志的 tool-result 形状
|
|
46
|
+
|
|
47
|
+
`/stats` 与门审计都从会话日志里解析 `tool/result`。DSH 0.2.x 的 **v4 格式**把结果
|
|
48
|
+
直接放在 `data.message` 上(`{ role, source:{kind,callId}, toolCallId, isError, content:[{type:"text"}] }`),
|
|
49
|
+
不再有 v1–v3 的 `content:[{type:"tool-result", …}]` 包装层。
|
|
50
|
+
|
|
51
|
+
旧解析只认包装层 → **失败计数恒为 0、recall 命中率恒为 0**;而 `test/usage.test.mjs`
|
|
52
|
+
的 fixture 也只用 v3 形状,所以测试一直绿。(这是 v0.7.4 那类问题的翻版:
|
|
53
|
+
宿主序列化格式改了,读日志的一侧静默读到空。)
|
|
54
|
+
|
|
55
|
+
- `lib/usage.js` 新增导出 **`readToolResult(event)`**:两种形状都认,
|
|
56
|
+
返回 `{ callId, text, isError }`,非结果事件返回 `null`。
|
|
57
|
+
- `collectUsageStats` 改接它;`/stats` 的 failures / recall 命中率恢复真实值。
|
|
58
|
+
- 新增 2 条回归:`readToolResult` 双形状单测 + **v4 fixture 的 usage 汇总**
|
|
59
|
+
(`failures === 1`、`recall.withHits === 1`、`detailFollows === 1`)。
|
|
60
|
+
|
|
61
|
+
## 4. 本机第一次实测(2026-10-22,14 天窗口,40 个会话)
|
|
62
|
+
|
|
63
|
+
```
|
|
64
|
+
G2 Activation 75.9% PASS 22/29 挂载会话 fire 过 engram/esr 工具
|
|
65
|
+
G2b Push 0.0% — 0/29 会话的 request header 带 [RECALL]
|
|
66
|
+
G3 DrillRate 0.08 — 3 次 detail / 37 次有命中的 recall
|
|
67
|
+
G4 StaleRate 0.0% PASS 0/326 条被 supersede
|
|
68
|
+
G5 索引 680c PASS dsh-loom(真机 /preview)
|
|
69
|
+
G1 未测 v0.7.5 之前没有会话行 —— 重启后开始有分母
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
**G2b = 0.0% 是 v0.7.4 的验证基线**:修复上线前,14 天里没有任何一个会话的
|
|
73
|
+
request header 带过 `[RECALL]`。重启宿主后这个数字应当由 0 变正——那是推送腿第一次
|
|
74
|
+
真正被观测到,而不是被假设为在工作。
|
|
75
|
+
|
|
76
|
+
G3 = 0.08 也值得注意:模型在 recall 上很勤(37 次有命中),但几乎不 `engram_detail`
|
|
77
|
+
——推送被采纳率低,正是 v0.8 要解决的问题(证据化 procedure + 更短的推送行)。
|
|
78
|
+
|
|
79
|
+
## 5. wiring harness:把 `apply()` 也纳入测试
|
|
80
|
+
|
|
81
|
+
两次静默失效(会话日志改名的 8 号问题、`session.events` 被删)都发生在 **接线层**:
|
|
82
|
+
模块本身是对的,`apply()` 读的宿主 API 变了,而没有任何测试真正调用过 `apply()`。
|
|
83
|
+
|
|
84
|
+
新增 **`test/wiring.test.mjs`**:用假 cordis ctx(`get` / `inject` / `on` / `effect` /
|
|
85
|
+
`provide` / `tools.register` + 内存 storage facility)挂载 `apply()`,断言:
|
|
86
|
+
|
|
87
|
+
- 四个 section 全部注册(`engram:index` / `engram:jit` / `engram:esr-method` / `engram:esr`);
|
|
88
|
+
- **15 个工具**全部注册,`engramLedger` 服务被 provide;
|
|
89
|
+
- 事件接线:`tools/result` ×3、`session/event` ×4、`session/disposed` ×4;
|
|
90
|
+
- `enabled:false` 时零注册;
|
|
91
|
+
- **端到端**:一个没有 `.events`、只有 `sessionQuery.live()` 租约的 0.2.x 会话,
|
|
92
|
+
index section 仍渲染出 `[ENGRAM]` + `[RECALL]`,且命中的正是预置的记忆
|
|
93
|
+
—— 这条用例在 v0.7.4 之前会失败。
|
|
94
|
+
|
|
95
|
+
副产物:harness 暴露了「`apply` 的 effect disposer 必须被调用」——GC interval
|
|
96
|
+
不清,测试进程不退出。
|
|
97
|
+
|
|
98
|
+
## 6. 测试与兼容
|
|
99
|
+
|
|
100
|
+
- `npm test` **428/428**(v0.7.4:416);其中 ledger 14 → 18,usage 10 → 12,
|
|
101
|
+
新增 `test/gate-audit.test.mjs` 3 例、`test/wiring.test.mjs` 3 例。
|
|
102
|
+
- 老 `ledger.json` 无需迁移:新增字段可选,读侧全部做了缺省处理。
|
|
103
|
+
- `node client/typecheck.mjs` 干净。
|
|
104
|
+
|
|
105
|
+
## 7. 真机首日:推送腿第一次被观测到
|
|
106
|
+
|
|
107
|
+
重启到 0.7.5 后(宿主实际加载的是 `~/.dsh/profiles/web/node_modules/dsh-engram`——tarball 依赖,
|
|
108
|
+
不是本仓库,见 §8):
|
|
109
|
+
|
|
110
|
+
- **G2b 从 0.0% 变成 6.5%(2/31 挂载会话带 `[RECALL]` 块)**:v0.7.4 修好的推送腿第一次
|
|
111
|
+
在真实日志里留下痕迹:`activation.recall = { injections: 5, hitInjections: 5, hitsInjected: 9 }`。
|
|
112
|
+
- **账本第一次落行**:`ledger.json` 出现 `sessions` 行(首个会话 20 请求 / 注入 37,840c / avoided 0)。
|
|
113
|
+
写入时机 = 每 20 请求、或一次剪枝/压缩、或会话结束——所以行会迟到,但不会丢。
|
|
114
|
+
|
|
115
|
+
审计脚本本身被这次真机数据校正了三处:
|
|
116
|
+
|
|
117
|
+
1. **G2b 扫错了事件**:`[RECALL]` 块不只在 `request/header` 里,宿主还会在压缩/新一轮时把整段
|
|
118
|
+
system prompt 作为 `system/message` 事件发一次;只扫 header 会永远读到 0。现两者都扫,
|
|
119
|
+
且用 `/\[RECALL\] (?:recall|newest memory) · \d+ hit\(s\)/` 精确匹配渲染块——静态提示行
|
|
120
|
+
`drill: use [RECALL] below` 不算。
|
|
121
|
+
2. **G5 的 0c 是假 PASS**:空 `[ENGRAM]` 块(工作区没记忆)天然 ≤700,判定无意义;现在报「未测」
|
|
122
|
+
并提示传 `?workspace=<repo>`。
|
|
123
|
+
3. **G1 的最小样本**:1 条行也敢下 FAIL 是噪声。行数 < 5 时只报数值、判「未测」。
|
|
124
|
+
|
|
125
|
+
另外记一条口径:`injected` 按名义价逐请求记,而前缀缓存实际约 10% 付费;两侧同比例缩放
|
|
126
|
+
不改变 net 的**正负号**(G1 判的就是符号),所以门不受影响,但「节省 token」的绝对量偏悲观。
|
|
127
|
+
|
|
128
|
+
## 8. 部署形态(为什么重启不一定生效)
|
|
129
|
+
|
|
130
|
+
宿主从 **tarball 依赖**加载插件:`~/.dsh/profiles/web/package.json` →
|
|
131
|
+
`file:~/.dsh/vendor/dsh-engram-<v>.tgz`。仓库改动不重启不生效,**重启也不生效**——要先
|
|
132
|
+
`npm pack` → 换 profile 依赖 → `npm install`。本次已把 0.7.5 装进去(`--legacy-peer-deps`,
|
|
133
|
+
因为 `dsh-context` 的 peer 由宿主提供)。顺带发现 `files` 漏了 `eval/`,导致安装副本里
|
|
134
|
+
`npm run gate:audit` 指向不存在的文件;已补 `eval/gate-audit.mjs`。
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
> **更正(v0.7.7)**:本文第 1 节的根因判断**是错的**。真机装了
|
|
2
|
+
> `boundary-attempts.jsonl` 之后看到:同一个边界 `host` 与 `preset` 两个平面**都收到了**
|
|
3
|
+
> ——不是 realm 隔离。真正的杀手是**工作区键不一致**(`Users-x-proj` vs `/Users/x/proj`),
|
|
4
|
+
> 见 [RELEASE-NOTES-v0.7.7.md](RELEASE-NOTES-v0.7.7.md)。本文件保留当时的推理过程,
|
|
5
|
+
> 第 2/3/4 节的脚手架(preset 行、尝试日志、状态文件防降级)依然有效且有用。
|
|
6
|
+
|
|
7
|
+
# dsh-engram v0.7.6
|
|
8
|
+
|
|
9
|
+
自 v0.7.5 以来:**修掉 L1 边界剪枝在 `dsh web` 上「已武装、永不触发」的结构性哑火**,
|
|
10
|
+
并给「武装≠开火」这件事补上可离线检查的证据。
|
|
11
|
+
|
|
12
|
+
## 1. L1 在 web 上是哑的(真机证据 + 根因)
|
|
13
|
+
|
|
14
|
+
v0.7.5 之后状态快照说 `boundaryPrune.armed: true`,`/overview` 也一致,但:
|
|
15
|
+
|
|
16
|
+
- `activation.boundaryPrune` 恒为 `null`,`~/.dsh/engram/pack-telemetry.json` 从未生成;
|
|
17
|
+
- 账本所有会话行的 `prunedChars` 恒为 `0`;
|
|
18
|
+
- 本会话里真实发生了一次 todo 边界(`todo/write`,1 项完成),依然什么都没有。
|
|
19
|
+
|
|
20
|
+
根因不是阈值或还款时钟:**监听器装在 host 平面,而 `dsh web` 的会话事件属于
|
|
21
|
+
agent preset 的 realm**。host 那只监听器从头到尾没见过一个 `todo/write`,所以它
|
|
22
|
+
永远「武装、零触发」——账本信用侧的两条腿里,剪枝那条腿在 web 上是死的。
|
|
23
|
+
|
|
24
|
+
## 2. 新增 preset 行 `dsh-engram/prune`
|
|
25
|
+
|
|
26
|
+
web 接管现在同时写入**两行**(同一个 `compaction` 组、同一个 isolate realm):
|
|
27
|
+
|
|
28
|
+
```yaml
|
|
29
|
+
- id: engram-compaction
|
|
30
|
+
name: dsh-engram/compaction
|
|
31
|
+
- id: engram-prune
|
|
32
|
+
name: dsh-engram/prune # L1,能看见这个 realm 里会话的 todo/goal 边界
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
- `lib/prune.js`:realm 侧入口,复用同一个 `toolResultPruner` 与还款/自适应门;
|
|
36
|
+
入账走 host 提供的 `engramLedger`(账本仍只有一个写者)。
|
|
37
|
+
- 两平面并存是安全的:`pruneSession` 只剪超阈值结果,第二次扫同一 surface 找不到
|
|
38
|
+
可剪对象,自然不重复入账。
|
|
39
|
+
- 老接管(只有引擎行)在刷新配置时自动补上 prune 行;`revert` 会把它摘掉,
|
|
40
|
+
回到**逐字节一致**的出厂文本(有回归测试)。
|
|
41
|
+
|
|
42
|
+
## 3. 「武装≠开火」有证据了
|
|
43
|
+
|
|
44
|
+
新增 `~/.dsh/engram/boundary-attempts.jsonl`(JSONL,32 KB 内保留最近 20 条):
|
|
45
|
+
|
|
46
|
+
| 字段 | 含义 |
|
|
47
|
+
|---|---|
|
|
48
|
+
| `plane` | `host` / `preset` —— 哪个平面看见的边界 |
|
|
49
|
+
| `kind` | `fired` / `skipped`(还款时钟)/ `held`(账本自适应关闭) |
|
|
50
|
+
| `boundary` | `{kind: todo|goal, ...}` |
|
|
51
|
+
| `pruned` / `charsRemoved` | 真剪到几条、多少字符 |
|
|
52
|
+
|
|
53
|
+
`npx dsh-engram status` 直接打印摘要(各平面见过几次边界、真剪到几次、最近一次)。
|
|
54
|
+
从此「0 次尝试」和「尝试了但剪不到」是两件可区分的事。
|
|
55
|
+
|
|
56
|
+
## 4. 状态文件不再被杂进程覆盖
|
|
57
|
+
|
|
58
|
+
同一份 `context-gc.status.json` 变「说谎」的第二个来源找到了:**跨进程**覆盖。
|
|
59
|
+
`npm test` 的 wiring harness 会真的跑 `apply()`,于是每次跑测试都会用出厂默认
|
|
60
|
+
(`host: unavailable` / `L1 未武装`)覆盖开发机上正在跑的服务器快照。
|
|
61
|
+
|
|
62
|
+
- harness 现在把 `DSH_HOME` 指到临时目录;
|
|
63
|
+
- `writeContextGcStatus` 增加一条规则:**跨 pid 的降级写入,若原写者进程仍存活则拒绝**。
|
|
64
|
+
重启(新 pid、同样好的快照)照常接管,崩溃循环也不会把好快照刷成坏快照。
|
|
65
|
+
|
|
66
|
+
## 5. 顺手
|
|
67
|
+
|
|
68
|
+
- `gate-audit` 的报告日期不再写死 `2026-10-22`,改为审计时刻的真实日期。
|
|
69
|
+
|
|
70
|
+
## 已知边界
|
|
71
|
+
|
|
72
|
+
- 本版只让 L1 **能**开火;它到底剪了多少,要等下一次 todo 边界在真机发生
|
|
73
|
+
(`boundary-attempts.jsonl` 里出现 `plane: preset, kind: fired, pruned > 0`)。
|
|
74
|
+
- G1 仍是 1/4 行正收益(`未测`),判定需要 ≥ 5 条账本行。
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# dsh-engram v0.7.7
|
|
2
|
+
|
|
3
|
+
**L1 边界剪枝在真机上永久哑火的真正原因**:工作区键一边是 `Users-x-proj`,一边是
|
|
4
|
+
`/Users/x/proj`,查历史永远落空 → 还款闸门的「预期会话长度」塌回地板值 → 任何
|
|
5
|
+
`requests ≥ 1` 的会话都被判「快到尾巴了、别剪」,于是**坐着武装、永不触发**。
|
|
6
|
+
|
|
7
|
+
## 1. 证据链(v0.7.6 的脚手架抓到的)
|
|
8
|
+
|
|
9
|
+
v0.7.6 给每个「看见边界」加了落盘 `boundary-attempts.jsonl`。真机重启后我故意做了一次
|
|
10
|
+
todo 边界(1 项完成),文件里立刻出现两行:
|
|
11
|
+
|
|
12
|
+
```json
|
|
13
|
+
{"plane":"preset","kind":"skipped","boundary":{"kind":"todo","completed":1},"requests":7,"expectedTotal":50,"workspace":"/Users/sunchuxiong/dsh-loom"}
|
|
14
|
+
{"plane":"host","kind":"skipped","boundary":{"kind":"todo","completed":1},"requests":7,"expectedTotal":50,"workspace":"/Users/sunchuxiong/dsh-loom"}
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
三件事同时被证明:
|
|
18
|
+
|
|
19
|
+
1. **两个平面都收到了边界** —— v0.7.6 里「host 平面看不见 web 会话事件」的推断是错的,
|
|
20
|
+
realm 隔离不是病因(preset 行留着,无害且自限);
|
|
21
|
+
2. 拦住它的是**还款闸门**(`kind: skipped`),不是阈值、不是 pruner;
|
|
22
|
+
3. `expectedTotal: 50` —— 恰好等于地板值,说明**历史查询没命中**:一个有 17 场会话的
|
|
23
|
+
工作区,被当成了「第一次见的陌生目录」。
|
|
24
|
+
|
|
25
|
+
## 2. 根因:两套键,永远对不上
|
|
26
|
+
|
|
27
|
+
| 来源 | 键的形状 |
|
|
28
|
+
|---|---|
|
|
29
|
+
| 会话目录(`~/.dsh/sessions/--Users-…-dsh-loom--/`) | `Users-sunchuxiong-dsh-loom` |
|
|
30
|
+
| 活会话的 `header.cwd` | `/Users/sunchuxiong/dsh-loom` |
|
|
31
|
+
|
|
32
|
+
`expectedSessionRequests()` 拿后者去 `history.get(workspace)`,永远 `undefined` →
|
|
33
|
+
`prior` 落回 `DEFAULT_MIN_REMAINING`(50)→ 闸门是
|
|
34
|
+
`requests + minRemaining > expectedTotal`,在 `expectedTotal == minRemaining` 时对任何
|
|
35
|
+
`requests ≥ 1` 恒真 → **跳过**。只有 `requests == 0` 才可能开火——也就是永不。
|
|
36
|
+
|
|
37
|
+
这同时解释了今天早些时候「210 请求、真边界、零触发、`activation.boundaryPrune: null`」。
|
|
38
|
+
|
|
39
|
+
## 3. 修复
|
|
40
|
+
|
|
41
|
+
- `normalizeWorkspaceKey()`:`/Users/x/proj`、`Users-x-proj`、`--Users-x-proj--`
|
|
42
|
+
归一成同一个键(去首尾分隔符、`/ \` 折叠为 `-`、win32 小写)。
|
|
43
|
+
- `workspaceHistory()` 用归一键建表;`expectedSessionRequests()` 查表前也归一
|
|
44
|
+
(两侧都归一,向后兼容注入的旧式 map)。
|
|
45
|
+
- 回归测试:造一个真实形状的桶(`--Users-x-proj--`, 非空 `session.jsonl.zstd`),
|
|
46
|
+
断言活 cwd 能查到历史、`expectedTotal` 从 50 变 120、闸门从「必跳过」变成「有headroom」。
|
|
47
|
+
|
|
48
|
+
真机语料上的模拟:`expectedTotal` 由 `50 → 120`,`7 + 50 ≤ 120` → 边界会放行。
|
|
49
|
+
|
|
50
|
+
## 4. 顺带修掉的污染源
|
|
51
|
+
|
|
52
|
+
`engramHome()` 之前写死 `~/.dsh/engram`,不看 `DSH_HOME` —— 于是跑一次 `npm test`,
|
|
53
|
+
测试里的假会话(`/tmp/ws-preset`)就把 21 次假 fire 写进开发机的**真实**遥测
|
|
54
|
+
(`boundary-prune.status.json`)。现在它跟随 `DSH_HOME`,测试彻底隔离。
|
|
55
|
+
|
|
56
|
+
## 已知边界
|
|
57
|
+
|
|
58
|
+
- 本版让闸门**有 headroom**,但真机是否真的剪到东西,仍需一次 ≥ 50 请求后的 todo 边界
|
|
59
|
+
在 `boundary-attempts.jsonl` 里留下 `kind: fired, pruned > 0`。
|
|
60
|
+
- G1 仍是 1/4 行正收益(`未测`),判定需要 ≥ 5 条账本行。
|