dsh-gokifu-compact 1.9.1 → 1.9.2
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/CHANGELOG.md +102 -0
- package/README.md +33 -1
- package/docs//346/226/271/346/241/210-/346/214/201/347/273/255/344/274/230/345/214/226.md +272 -2
- package/docs//350/215/211/347/250/277-/346/214/201/347/273/255/344/274/230/345/214/226.md +60 -0
- package/package.json +2 -1
- package/scripts/doctor.mjs +34 -0
- package/src/engine.mjs +211 -26
- package/src/graph.mjs +267 -0
- package/src/ledger.mjs +162 -8
- package/src/ui.mjs +125 -47
- package/src/verifier.mjs +93 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,7 +1,109 @@
|
|
|
1
1
|
# 更新记录
|
|
2
2
|
|
|
3
|
+
## 1.9.2 — 2026-10-30
|
|
4
|
+
|
|
5
|
+
### ⚠ 破坏性契约变化:`graph.json` 不再是「最新状态」
|
|
6
|
+
|
|
7
|
+
**1.9.2 起,落盘拆成「上一回合基线 + 当前增量」:**
|
|
8
|
+
|
|
9
|
+
| 文件 | 内容 |
|
|
10
|
+
|---|---|
|
|
11
|
+
| `graph.json` | **上一个回合边界**的完整基线 |
|
|
12
|
+
| `graph.json.delta.jsonl` | 本回合的增量帧,一帧一行 `<baseHash16>\t<checksum31>\t<json>` |
|
|
13
|
+
|
|
14
|
+
`graph.json` 只在**回合边界**(`agent/status idle`)落一次;回合内每一步只追加 delta 帧。
|
|
15
|
+
⇒ **外部工具若只 `JSON.parse(graph.json)`,会少一个回合的数据。** 最新状态 = 基线 + 按序重放 delta
|
|
16
|
+
(`fromJSON` → `decodeDeltaFrames(text, baseHash)` → `applyDelta`,三个函数在 `src/graph.mjs`)。
|
|
17
|
+
README 新增「graph.json 契约(外部工具必读)」一节,含三条余量(短回合写放大 / dirty>100 flush / `appendFileSync` 跨平台)。
|
|
18
|
+
|
|
19
|
+
### P3 · flush 写放大 ×800–975 治理(增量 delta + index 追加 + dirty 节点)
|
|
20
|
+
|
|
21
|
+
- `graph.json.delta.jsonl`:每步只追加 dirty 节点/边(绝对 upsert)+ 标量 + order 追加;
|
|
22
|
+
合并阈值 `max(64KB, 基线 20%)`,超过则写新基线(`tmp` → `rename`)并清空 delta
|
|
23
|
+
- 崩溃安全:帧带 `baseHash`(generation 保护,基线重写后旧帧整体忽略)+ `checksum31`
|
|
24
|
+
(半写帧就地停止并截断到最后一个好帧)+ 幂等重放(同一批帧重放多次结果相同)
|
|
25
|
+
- `index.md` 稳态**追加**增量段(~200–800 B/次),fold / 首次 / 缺失 / >256KB 时全量重建;
|
|
26
|
+
`nodes/*.md` 稳态不写,只记 pending,fold 时只写 dirty
|
|
27
|
+
- 实测(三张真实图 × 60 步 + 1 回合边界):写放大 **×80.16 / ×70.04 / ×61.75 → ×37.41 / ×33.31 / ×30.75**;
|
|
28
|
+
稳态每步写 **10.5–11.8 KB → 591–713 B(−94%)**;fold 后 flush **429–836 ms → 9.4–22.9 ms**
|
|
29
|
+
(5–20 个 dirty 节点)/ **76.5–88.8 ms**(100 个)
|
|
30
|
+
- 测试:`test/persist-incremental.test.mjs` **8 项**(重放等价 / 幂等 / 半写帧 / generation 保护 /
|
|
31
|
+
index 增量 / engine 端到端 / nodes dirty)
|
|
32
|
+
- 已知边界:**极短回合(<10 步)** 每回合的全量基线摊销不足,写放大仍可能 >×50;
|
|
33
|
+
`appendFileSync` 跨平台原子性未验证
|
|
34
|
+
|
|
35
|
+
### P4 · 首屏 `state` 的 graph.json 解析缓存
|
|
36
|
+
|
|
37
|
+
- `src/ui.mjs` 新增 `(绝对路径, mtimeMs, size) → 聚合摘要` 缓存 + `pruneGraphCache()`:
|
|
38
|
+
改文件必失效(同 mtime 但 size 不同也失效)
|
|
39
|
+
- 133 图 / 33 MB 实测:热路径 p50 **140.5 ms → 20.9 ms**(另一轮 **242.5 → 51.1**);
|
|
40
|
+
冷路径也更快(**170.7 → 101.3** / **386.4 → 196.7**);`state` 响应 **3,648 B 逐字相同**
|
|
41
|
+
- 测试:`test/ui-perf.test.mjs` **10 项**
|
|
42
|
+
|
|
43
|
+
### P5 · 生产口径 token 预算与 `pickFallback`
|
|
44
|
+
|
|
45
|
+
- `renderLedger` 新增 `fallback` 三态(`none` / `over-budget` / `impossible`)+ `overBudgetBy`:
|
|
46
|
+
**不再静默返回超预算文本** —— 调用方拿到的每个结果都能区分「预算内 / 兜底超限 / 无解」
|
|
47
|
+
- `pickFallback` 择优顺序固定:预算内优先 → tokens 最小 → 平手列得多;全超选最省并标 `over-budget`;
|
|
48
|
+
无非空文本返回 `null`(`impossible`)
|
|
49
|
+
- 生产口径(`treeDir` = 真实会话目录、`maxTokens=500`,133 图):
|
|
50
|
+
**>500 = 24/133(与 1.9.1 HEAD 的 24 相同,新增 0)**、avg **444.80**(HEAD 430.85,+13.95 ≤ +20)、
|
|
51
|
+
max **905**(与 HEAD 完全相同)。24 张全部是 L4/L5 兜底档 —— 正文本身 >1500 字符,不是预算记账 bug
|
|
52
|
+
- 测试:`test/ledger-budget.test.mjs` **7 项**
|
|
53
|
+
|
|
54
|
+
### P6a · `TRACE:` 行绝对路径改相对,回收 avg +17 tok/图
|
|
55
|
+
|
|
56
|
+
- `TRACE:` 从 `D:\DeepSeek Harness\home\chess-context\sessions\<sid>/index.md …`(137.7 字符)
|
|
57
|
+
改成 `sessions/<sid>/index.md`(57.5 字符),`traceRoot` 可配置(默认无前缀,生产传 `sessions`)
|
|
58
|
+
- 生产口径 133 图:avg **445.08 → 429.64(−15.44 tok/图)**;over500 **24 → 18**;max **905 → 878**;
|
|
59
|
+
逐会话 85 便宜 / 7 变贵(5 个是 TRACE 变短后落进更低档位、列了更多内容)/ 41 不变;
|
|
60
|
+
`renderFacts` **133 图 0 差异**
|
|
61
|
+
- 测试:`test/trace-relative.test.mjs` **7 项**(Windows 盘符 / POSIX 用户名路径 / traceRoot 可配置 / 不泄漏绝对路径 …)
|
|
62
|
+
|
|
63
|
+
### P6-A · 悬空边只在分析中排除(不剪数据)
|
|
64
|
+
|
|
65
|
+
- 新增 `classifyDangling()`(`residue` / `live-unsettled` / `from-broken` / `none`)与 `danglingStats()`;
|
|
66
|
+
**只过滤显示,不改 `graph.json`**
|
|
67
|
+
- 判定:`residue := to==null && age>10min`;**`live_unsettled`(untested 且 ≤10 分钟)必须保留可见**
|
|
68
|
+
—— 这是 Lead 批准的**更保守**口径(keeper 先 settle 结果、`step/end` 才回填 `to`,
|
|
69
|
+
按字面公式会把刚结算的新鲜失败当场隐藏)
|
|
70
|
+
- `from` 悬空 **214 条**(全在 `5acafe42`,`mergeNodeInto` 老 bug)单独标记,不混入 `to=null` residue
|
|
71
|
+
- 实测(133 图):热清单**无终点失败边 158 → 0**;avg **−18.98**;over500 **17 → 17**;max **878 → 878**;
|
|
72
|
+
全库 `residue 5,566 / liveUnsettled 1 / fromBroken 214`
|
|
73
|
+
- `renderFacts` **仍列全部失败边**(失败记忆不丢),新增 `DANGLING_RESIDUE: N` / `FROM_DANGLING: 214` /
|
|
74
|
+
`LIVE_UNSETTLED: N` 显式计数
|
|
75
|
+
- 测试:`test/dangling-exclusion.test.mjs` **8 项**
|
|
76
|
+
|
|
77
|
+
### P7 · `fallback` 与 `traceRoot` 接进 engine / doctor(不留死开关)
|
|
78
|
+
|
|
79
|
+
- `src/engine.mjs`:新增配置 `hotLedgerTraceRoot`(schema 默认 `"sessions"`),生产 `renderLedger` 调用传
|
|
80
|
+
`traceRoot: "sessions"` ⇒ 生产热清单 `TRACE:` 为 `sessions/<sid>/index.md`,不泄漏盘符/用户名
|
|
81
|
+
- engine **消费** `fallback`:`over-budget` → warn 日志含 `overBudgetBy=` / `budget=`;
|
|
82
|
+
`impossible` → error;`ledgerBudgetMetrics` 累计并原子落盘 `<graphDir>/ledger-budget.json`;
|
|
83
|
+
提供 `getLedgerBudgetStats()` / `onLedgerBudget` 钩子(可测,不静默)
|
|
84
|
+
- `scripts/doctor.mjs`:新增「热清单预算(fallback)」项,显示 `none / over-budget / impossible` 分布与
|
|
85
|
+
`maxOverBudgetBy`;字段缺失写「未采集」;`impossible > 0` 记 error
|
|
86
|
+
- 测试:`test/engine-harness.test.mjs` 增至 **25 项**(+4 条 P7 接线用例)
|
|
87
|
+
|
|
88
|
+
### S2b · `no-exit` 弱成功按 N1 收窄
|
|
89
|
+
|
|
90
|
+
- `verifier.classify()` 的 shell 分支:`[exit code]` 存在时行为不变;**无退出码时默认给 `weak:"shell"`**,
|
|
91
|
+
命中 N1 任一规则则不给。`result` **永远是 `unverified`**(不进已验证事实)
|
|
92
|
+
- N1 八条 veto 规则集中在 `SHELL_WEAK_VETO_RULES` 一处(id + 说明 + 正则):末行自报非零 / 末行 `Node.js vX` /
|
|
93
|
+
尾部 node 栈迹 / `command not found`·`No such file`·`cannot stat` / 行首 `SyntaxError|AssertionError|…` /
|
|
94
|
+
`FAILED (failures=N)` 等;规则表可测试、可追溯
|
|
95
|
+
- 真实采样:全标 weak 的误报率 **2.94% → 收窄后约 0.49%**(≈48/9762 条假进展)
|
|
96
|
+
- 测试:`test/weak-shell.test.mjs` **13 项**(每条规则命中/不命中 + 旧图兼容 + renderFacts 不变 + 变异自检)
|
|
97
|
+
|
|
98
|
+
### 测试
|
|
99
|
+
|
|
100
|
+
- `node test/run.mjs`:**373 passed / 0 failed**(30 个测试文件,`EXIT 0`;2026-10-30 03:2x 实测)
|
|
101
|
+
- 新增测试文件:`persist-incremental`(8) · `ui-perf`(10) · `ledger-budget`(7) · `trace-relative`(7) ·
|
|
102
|
+
`dangling-exclusion`(8) · `weak-shell`(13);`engine-harness` 21 → **25**
|
|
103
|
+
|
|
3
104
|
## 1.9.1 — 2026-10-29
|
|
4
105
|
|
|
106
|
+
|
|
5
107
|
### 修图坍缩:全库 61.3% 的边是自环(三份独立评估共同指向)
|
|
6
108
|
|
|
7
109
|
事故:`worldstate.touch(paths)` 收到的是**单个路径字符串**(`engine.mjs` 的
|
package/README.md
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
热清单 323 tokens (2000 边压力测试;旧版全量渲染 28,800)
|
|
9
9
|
失败保留 8 / 8 (真实模型对照;官方 8 段模板 4.67 / 8)
|
|
10
10
|
清单开销 0.2% of prompt (v1.3.1 起;事故会话的旧版是 21.3%)
|
|
11
|
-
验证
|
|
11
|
+
验证 373 项单测 · 真实包 8/8 · 真实进程 fold 3~6/6
|
|
12
12
|
```
|
|
13
13
|
|
|
14
14
|
GO = 围棋,kifu = 棋谱,compact = 压缩。一局棋下完,棋谱上不只有落子顺序,还有每一手的评语。
|
|
@@ -50,6 +50,38 @@ GO = 围棋,kifu = 棋谱,compact = 压缩。一局棋下完,棋谱上不
|
|
|
50
50
|
| 失败记录 | 融进散文摘要,模型可以丢 | 规则抽取 + 逐字注入,模型无权丢 |
|
|
51
51
|
| 热上下文 | 无 | 323 tokens / 步,带语义标记与报错原文 |
|
|
52
52
|
|
|
53
|
+
### ⚠ 棋谱落盘:`graph.json` 的语义(1.9.2 起)
|
|
54
|
+
|
|
55
|
+
**`graph.json` 不再代表「最新状态」,而是「上一回合基线 + 当前增量」。**
|
|
56
|
+
|
|
57
|
+
```
|
|
58
|
+
每次都重写整图(≤1.9.1) 增量落盘(1.9.2 起)
|
|
59
|
+
graph.json = 最新 graph.json = 上一个回合边界的完整基线
|
|
60
|
+
graph.json.delta.jsonl = 本回合的增量帧(带 baseHash + checksum)
|
|
61
|
+
(两者合并 = 最新状态)
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
**⇒ 它在回合内落后一回合。** 任何直接 `JSON.parse(graph.json)` 当「最新」的脚本,**都会少一回合的数据**。
|
|
65
|
+
`graph.json` 只在**回合边界**(`agent/status idle`)落一次完整快照;回合内的每一步只往
|
|
66
|
+
`graph.json.delta.jsonl` 追加一帧。
|
|
67
|
+
|
|
68
|
+
| 你要什么 | 怎么拿 |
|
|
69
|
+
|---|---|
|
|
70
|
+
| **最新状态**(推荐) | **基线 + 增量一起读**:`fromJSON(graph.json 文本)` → 用基线内容哈希 `decodeDeltaFrames(delta 文本, baseHash)` → 逐帧 `applyDelta(g, frame)`。三个函数都在 `src/graph.mjs`(插件源码随包发布:`node_modules/dsh-gokifu-compact/src/graph.mjs`) |
|
|
71
|
+
| 上一回合基线(做 diff / 审计) | 直接读 `graph.json` 可以,但**要说明「这是基线不是最新」** |
|
|
72
|
+
| 崩溃安全 | 每帧带 `baseHash`(generation)+ `checksum31`:半写帧解码时就地停止,加载时把 delta 截到最后一条好帧;**旧 generation 的帧整体忽略** |
|
|
73
|
+
|
|
74
|
+
**为什么这样设计**:写放大从 **×80 / ×70 / ×62** 降到 **×37 / ×33 / ×31**(三张真实图 60 步 + 1 回合边界),
|
|
75
|
+
fold 后 flush 从 **429–836 ms** 降到 **≤95 ms**;代价是「读的人必须知道这是增量结构」。
|
|
76
|
+
|
|
77
|
+
**三条已知余量(选型时要知道)**
|
|
78
|
+
|
|
79
|
+
1. **短回合写放大**:每回合边界落一份完整基线(0.5–0.6 MB)。真实分布 p50=40 步/回合 ⇒ 写放大 ≈×20–25;
|
|
80
|
+
但**极短回合(<10 步,p10=13.9)**摊销不足,写放大仍可能 >×50。
|
|
81
|
+
2. **dirty > 100 的 flush**:fold 后只写 dirty 节点 —— 5–20 个 dirty 时 **9–23 ms**,100 个时约 **87–89 ms**
|
|
82
|
+
(改前全量 429–836 ms)。dirty 数极大时收益收窄。
|
|
83
|
+
3. **`appendFileSync` 的原子性依赖 OS**:本机 NTFS 上 1 KB 级追加已过半写测试;**跨平台(尤其网络文件系统)未验证**。
|
|
84
|
+
|
|
53
85
|
### 热上下文长什么样
|
|
54
86
|
|
|
55
87
|
每步 `agent/pre-step` 注入(内容不变时复用;变了就**就地替换**上一条,surface 永远只有一条):
|
|
@@ -251,7 +251,7 @@ pageContent { max-width:980px; margin:0 auto; padding:24px 28px 48px; display:fl
|
|
|
251
251
|
修法:改用 `fileURLToPath(new URL(...))`(实测得到 `D:\主工作区\...\src\ui.mjs`,读 `../package.json` 得 `1.9.1` ✓)。
|
|
252
252
|
> 编号说明:§三 已占用 S10(安装器 link)与 S11(文档同步),本条记为 **S10′**,在本轮任务表里是独立一行。
|
|
253
253
|
|
|
254
|
-
> **已实施(2026-10-29 22:4x,S10′ 子代理)**:`src/ui.mjs:22` 加 `fileURLToPath`、`:24-34` 新增 `HERE`、`:322` 改读 `HERE` 下的 `../package.json`(行号为修复后;旧实现是 `308-315` 那一行)。新增 `test/version-path.test.mjs`(4 用例 / 32 条运行时断言 / 零跳过)——`node test/run.mjs version-path` **4/0 [exit 0]**,变异回旧表达式 **2/2 [exit 1]**;全量 `node test/run.mjs` **168/0 [exit 0]**;真实 HTTP 入口 `/gokifu/api` state 返回 **`version: "1.9.1"`**(`client.js:197` 渲染 `v1.9.1`)。live profile 两文件 sha256 未变。证据见仓库根 `计划表.md` §S10′ 完成证据(**注:`计划表.md` 在仓库根、不在 npm 包的 `files` 内,包内读者请查仓库** —— 原本这里是 `[计划表 §S10′ 完成证据](../计划表.md)` 的链接,安装包里会 404,2026-10-30
|
|
254
|
+
> **已实施(2026-10-29 22:4x,S10′ 子代理)**:`src/ui.mjs:22` 加 `fileURLToPath`、`:24-34` 新增 `HERE`、`:322` 改读 `HERE` 下的 `../package.json`(行号为修复后;旧实现是 `308-315` 那一行)。新增 `test/version-path.test.mjs`(4 用例 / 32 条运行时断言 / 零跳过)——`node test/run.mjs version-path` **4/0 [exit 0]**,变异回旧表达式 **2/2 [exit 1]**;全量 `node test/run.mjs` **168/0 [exit 0]**;真实 HTTP 入口 `/gokifu/api` state 返回 **`version: "1.9.1"`**(`client.js:197` 渲染 `v1.9.1`)。live profile 两文件 sha256 未变。证据见仓库根 `计划表.md` §S10′ 完成证据(**注:`计划表.md` 在仓库根、不在 npm 包的 `files` 内,包内读者请查仓库** —— 原本这里是 `[计划表 §S10′ 完成证据](../计划表.md)` 的链接,安装包里会 404,2026-10-30 02:3x 按 WS-RELEASE-EXEC 的 V4 项改为纯文本引用)。
|
|
255
255
|
> 测试侧附带事实:本机 `os.tmpdir()` 是 8.3 短名 `ADMINI~1`,`~` 编码成 `%7E` ⇒ **连纯 ASCII 临时目录也会踩同一坑**,故 ASCII 对照组只比转换结果、不建真实文件。
|
|
256
256
|
|
|
257
257
|
### 8.8 复跑
|
|
@@ -1502,7 +1502,7 @@ renderLedger(g, { maxTokens: 500, markers: "semantic", treeDir: <该会话真实
|
|
|
1502
1502
|
|
|
1503
1503
|
---
|
|
1504
1504
|
|
|
1505
|
-
## 三十 · 冻结线收口定稿(2026-10-30
|
|
1505
|
+
## 三十 · 冻结线收口定稿(2026-10-30 02:3x)· 只写当前决定
|
|
1506
1506
|
|
|
1507
1507
|
> 逐项证据见根 `计划表.md` §五十八–§六十一。
|
|
1508
1508
|
|
|
@@ -1540,3 +1540,273 @@ renderLedger(g, { maxTokens: 500, markers: "semantic", treeDir: <该会话真实
|
|
|
1540
1540
|
|
|
1541
1541
|
- 只记录决定;**不含新测量**。
|
|
1542
1542
|
- **未验证**:task-47(C-13 reviewer 判定)、task-48(合并树 delta)均未出结论;`ws-s6` / `ws-release-verify` 仍未落盘;运行期重启未验证(贯穿全篇的同一项)。
|
|
1543
|
+
|
|
1544
|
+
> ✅ **2026-10-30 02:45 全部解**:task-43 **PASS/ACCEPT**、task-44 **PASS/ACCEPT**、task-45 **PASS/ACCEPT WITH CAVEATS**、**task-47 PASS/ACCEPT**、**task-48 PASS/ACCEPT**、**task-49 PASS**(发布基线 `dfb255da…`);`ws-s6`(200 行)与 `ws-release-verify`(214 行)**均已落盘并已入表**。详见根 `计划表.md` §六十二–§七十一。
|
|
1545
|
+
|
|
1546
|
+
---
|
|
1547
|
+
|
|
1548
|
+
## 三十一 · 发布就绪与 **1.9.1 已发布**(2026-10-30 02:45)· 只写当前决定
|
|
1549
|
+
|
|
1550
|
+
> 逐项证据见根 `计划表.md` §六十三 / §六十六 / §六十七 / §六十八 / §七十。
|
|
1551
|
+
|
|
1552
|
+
### 31.1 定稿:**1.9.1 已发布成功**
|
|
1553
|
+
|
|
1554
|
+
| 步 | 结果 |
|
|
1555
|
+
|---|---|
|
|
1556
|
+
| `npm publish` | **exit 0**,未遇 OTP |
|
|
1557
|
+
| registry | **`1.9.1` 可见**;**`dist.shasum = `dfb255da9d48b0785efd40d8c32b69b9e8ce6447`**、integrity `sha512-KwHekUYQ…` **与 task-49 基线一致**;`dist-tags.latest = 1.9.1` |
|
|
1558
|
+
| commit / push | `571a994e1bce6181bc4b34e0893f44c727af1cd6`;`053a98a..571a994` exit 0 |
|
|
1559
|
+
| tag / Release | annotated **`v1.9.1` → `571a994`** 已 push;Release 已建 |
|
|
1560
|
+
|
|
1561
|
+
### 31.2 定稿:发布门禁的四条硬线(G1–G4)
|
|
1562
|
+
|
|
1563
|
+
| 门 | 结果 |
|
|
1564
|
+
|---|---|
|
|
1565
|
+
| **G1** | `node test/run.mjs` **316 passed / 0 failed(24 文件)** |
|
|
1566
|
+
| **G2** | `verify-install` **16/16** |
|
|
1567
|
+
| **G3** | `check-maintenance` —— **publish 前 5 通过/1 失败(缺 tag,exit 1)→ 打 tag 后 6 通过/0 失败(exit 0)** |
|
|
1568
|
+
| **G4** | `npm pack` **61 文件 / 407,712 B(unpacked 1,035,272 B)/ shasum `dfb255da…`** |
|
|
1569
|
+
|
|
1570
|
+
**⇒ 定稿纪律(G3 的教训):「红灯在发布前」有两种 —— 一种是真缺陷,一种是「顺序依赖的必然」。区分办法是问「这个红灯会在哪个动作之后自然消失」。** G3 消失于 tag;「缺 CHANGELOG 节」这类不会自己消失,那才是真缺陷。
|
|
1571
|
+
|
|
1572
|
+
### 31.3 定稿:五项 V 系列处置
|
|
1573
|
+
|
|
1574
|
+
| 项 | 决定 |
|
|
1575
|
+
|---|---|
|
|
1576
|
+
| **V4** `docs/方案-持续优化.md` → `../计划表.md` 包内死链 | ✅ **已修**(改为纯文本引用;复扫包内 14 个 md:**真实死链 0 条**) |
|
|
1577
|
+
| **V5** `HANDOFF.md` 过期 | **单独立项**(只改两行会「看起来新」的误导,见 §31.5) |
|
|
1578
|
+
| **V8** `files` 排除维护侧材料 | **本轮不改**(1.9.1 保持现包形态,62.3% 维护材料被接受) |
|
|
1579
|
+
| **V9** 补 `v1.9.0` tag | **不补** |
|
|
1580
|
+
| **V10** | 不阻断 |
|
|
1581
|
+
|
|
1582
|
+
### 31.4 定稿:C-13 与测试完整性的收口
|
|
1583
|
+
|
|
1584
|
+
- **C-13** 三轨全过:implementer 12 项自测 → **install 只读复核 6/6 变异真红** → **reviewer task-47 PASS/ACCEPT**(`--strict` **133 checked/15 failed/exit 1**;带豁免 **0 failed/applied 17/entries 17/exit 0**;6 类反例全过)。
|
|
1585
|
+
- **测试完整性**:`selfloop` 假绿已改显式断言(删 WORKING 必红)、**L4/L5 裁决现被 ledger/markers 双测试覆盖**(改裁决必红)、**24 文件 194 用例 0 零断言/0 早退/0 skip**。
|
|
1586
|
+
|
|
1587
|
+
### 31.5 定稿:本文件自身的两条已知问题(诚实登记)
|
|
1588
|
+
|
|
1589
|
+
1. **`HANDOFF.md` 整体过期**(顶部仍是 v1.3.5 的紧急提示)⇒ **只改两行会制造「看起来新」的误导**,须整篇重写(单独立项)。
|
|
1590
|
+
2. **`docs/` 占包内容 62.3%** ⇒ **本文件的任何一行都改变发布包**(§三十二)。
|
|
1591
|
+
|
|
1592
|
+
---
|
|
1593
|
+
|
|
1594
|
+
## 三十二 · **冻结期与「基线先于写入」的顺序错误**(2026-10-30 02:4x)· 只写当前决定
|
|
1595
|
+
|
|
1596
|
+
### 32.1 事故(**这不再是事故,是已固化的流程教训**)
|
|
1597
|
+
|
|
1598
|
+
`ws-release-exec` 提 **V4**(包内死链)→ lead 判「交 scribe」→ **我按最小修复改了 `docs/方案-持续优化.md:254` 一行** → **该文件在 npm `files` 内 ⇒ 包内容变了** → **`npm pack` shasum 从 `e9be1e2d…` 变了** → **task-24 刚出的 G4 基线立刻作废** → 新建 **task-49 re-gate**。
|
|
1599
|
+
|
|
1600
|
+
**⇒ 定性:修复本身是对的,错的是时机。这不算「改错」,算「顺序错」。**
|
|
1601
|
+
|
|
1602
|
+
### 32.2 定稿:发布冻结令的正确顺序(**两步,不可颠倒**)
|
|
1603
|
+
|
|
1604
|
+
1. **先冻结写入面**(列出 `files` 内的全部可写文件并锁死)
|
|
1605
|
+
2. **再打基线**(`npm pack` / 全树指纹)
|
|
1606
|
+
|
|
1607
|
+
**⇒ 「冻结令不写清『哪些文件在包内』就等于没冻结」。** 本次是「先打基线、后改包内文档」,自己把基线作废了。
|
|
1608
|
+
|
|
1609
|
+
### 32.3 定稿:冻结期的写入面划分
|
|
1610
|
+
|
|
1611
|
+
| 面 | 处置 |
|
|
1612
|
+
|---|---|
|
|
1613
|
+
| `files` 内(`src` / `lib` / `scripts` / `docs` / `evidence` / `CHANGELOG` / `QUICKSTART` / `LICENSE`) | ⛔ **冻结** —— 改一行就作废基线 |
|
|
1614
|
+
| **仓库根 `计划表.md`**(不在 `files`) | ✅ **可写** —— 冻结期所有记录写这里 |
|
|
1615
|
+
| `tmp/dsh-gokifu-compact/scribe-buffer/` | ✅ 可写 —— 待并入内容先缓冲 |
|
|
1616
|
+
|
|
1617
|
+
### 32.4 定稿:re-gate 的验收手法(**可复用**)
|
|
1618
|
+
|
|
1619
|
+
**不只看 shasum 变没变,要看「文件数 / 体积差」是否与「声称的改动量」吻合。**
|
|
1620
|
+
|
|
1621
|
+
| 指标 | task-24(旧) | task-49(新) | 变化 |
|
|
1622
|
+
|---|---:|---:|---|
|
|
1623
|
+
| entryCount | 61 | 61 | **0** |
|
|
1624
|
+
| unpackedSize | 1,034,996 | **1,035,272** | **+276 B** |
|
|
1625
|
+
| tarball | 407,580 | **407,712** | **+132 B** |
|
|
1626
|
+
|
|
1627
|
+
⇒ **两个体积增量与「一行文本从链接改成纯文本」同量级** ⇒ **反证无其他成分混入包**。若差几十 KB,就说明有未承认的改动。
|
|
1628
|
+
|
|
1629
|
+
### 32.5 定稿:全树指纹 START == END(最强的时间窗口证据)
|
|
1630
|
+
|
|
1631
|
+
task-49 用 `sha256( src/*.mjs + lib/*.js + scripts/*.mjs + package.json + README + CHANGELOG + .gitignore + **docs/*.md** + test/*.mjs )` = **`21ac4459…`**,门禁开始与结束**两次完全一致**;`git status` 计数 **42 == 42**。
|
|
1632
|
+
**⇒ 它同时覆盖 `docs/*.md`,正好能抓到「文档被改」这类问题。建议以后所有门禁都按这个做法。**
|
|
1633
|
+
|
|
1634
|
+
### 32.6 定稿:发布后纪律
|
|
1635
|
+
|
|
1636
|
+
- **发布已完成 ⇒ 本文件恢复可写**(lead 明确授权)。
|
|
1637
|
+
- **但每次再发布(1.9.2 / 1.10.x)前必须重走「冻结 → 打基线 → re-gate」。**
|
|
1638
|
+
- **写前按最新的树基线记录** —— 发布后 docs 写入不再影响已发布包,**但会影响下一次发布的基线**。
|
|
1639
|
+
|
|
1640
|
+
### 32.7 本节边界
|
|
1641
|
+
|
|
1642
|
+
- 只记录决定与教训;**不含新测量**。
|
|
1643
|
+
- 只记录决定与教训;**不含新测量**。
|
|
1644
|
+
- **未验证**:task-50(发布后验收,verifier 独立做)未出结论;`npx dsh-gokifu-compact install` 到真实 profile 未执行;运行期重启未验证。
|
|
1645
|
+
|
|
1646
|
+
> ✅ **2026-10-30 03:0x 更新**:**task-50 已 PASS**(`dist.shasum` 逐位相同、tag annotated 三处 commit 一致、空目录 `npm pack` 解包 61 文件/407,712 B/sha1 相同、清单双向 `only-in` 为空、解包后 `doctor` 错误 0/警告 1/exit 0)。详见根 `计划表.md` §七十五。
|
|
1647
|
+
|
|
1648
|
+
---
|
|
1649
|
+
|
|
1650
|
+
## 三十三 · 1.9.2 工作定稿:P3 / P4 / P5 / P6 与一条**契约变化**(2026-10-30 03:0x)· 只写当前决定
|
|
1651
|
+
|
|
1652
|
+
> 逐项证据见根 `计划表.md` §七十四 / §七十六–§八十四。**本节只写决定与必须落地的约定。**
|
|
1653
|
+
|
|
1654
|
+
### 33.1 定稿:已验收的切片(1.9.2 累计)
|
|
1655
|
+
|
|
1656
|
+
| 切片 | 结论 | 关键数字 |
|
|
1657
|
+
|---|---|---|
|
|
1658
|
+
| **P2 索引化**(task-38) | ✅ 已验收(task-48) | `renderIndexMd` 133/133、`renderNodeMd` 7843/7843 逐字节;5000 边 **3506.8→4.26 ms** |
|
|
1659
|
+
| **P4 首屏 state 缓存**(task-40) | ✅ 已验收(task-52) | warm p50 **124.4 → 9.3 ms**、cold **158 → 90.8 ms**、`state` **3648 B 逐字相同**;**同 size 只改 mtime 必失效** |
|
|
1660
|
+
| **P3 增量落盘**(task-39) | ✅ 已验收(task-51) | 写放大 **×80.16→37.41 / ×70.04→33.31 / ×61.75→30.75**;fold 后 flush ≤**95.3 ms**(100 dirty);**崩溃探针 5 项全过** |
|
|
1661
|
+
| **P5 token 预算三态**(task-41) | 执行方完成(待 task-54) | 生产口径 **24/133 新增 0**、avg **+13.95(≤+20)**;`fallback none 109 / over-budget 24 / impossible 0` |
|
|
1662
|
+
| **P6 悬空边**(task-30) | 设计已交付(P6-A 待 task-56 实施) | `to=null` **5,567 / 18,038(30.9%)**;**A 方案零风险** |
|
|
1663
|
+
|
|
1664
|
+
### 33.2 🔴 定稿(**最重要**):**`graph.json` 的语义变了,必须写进维护文档**
|
|
1665
|
+
|
|
1666
|
+
**P3 落地后:`graph.json` 不再是「最新状态」,而是「上一回合基线 + 当前增量(`graph.delta.jsonl`)」。**
|
|
1667
|
+
⇒ **它在回合内落后一回合。**
|
|
1668
|
+
**⇒ 任何直接读 `graph.json` 的外部工具会少一回合的数据。**
|
|
1669
|
+
|
|
1670
|
+
**必须落地的约定**:
|
|
1671
|
+
1. **维护文档 / README 必须写明**:读取最新状态请走加载器(会合并增量),**不要直接 `JSON.parse(graph.json)` 当最新**。
|
|
1672
|
+
2. **新增脚本若要读图,先确认走的是加载路径**。
|
|
1673
|
+
3. **这是「行为契约变化」,不是性能优化** —— 性能收益可回滚,契约变化会影响所有下游消费者。
|
|
1674
|
+
|
|
1675
|
+
### 33.3 定稿:三条必须保留的「余量/边界」声明
|
|
1676
|
+
|
|
1677
|
+
| # | 项 | 必须怎么写 |
|
|
1678
|
+
|---|---|---|
|
|
1679
|
+
| 1 | **写放大 ≤×50** | **只在这三张图(`e16faf2b` / `d393944b` / `5acafe42`)与「回合数 ≥10」时验证过**;**极短回合(<10 步)可能 >×50(p10=13.9)**;**全库分布未给** |
|
|
1680
|
+
| 2 | **fold 后 flush ≤100 ms** | **只在 `dirty ≤100` 时验证过**;**100 dirty 实测到 95.3 ms,余量仅 4.7 ms**;`dirty` 几百个会突破 |
|
|
1681
|
+
| 3 | **`appendFileSync` 的原子性** | **只在 Windows + 单进程崩溃场景测过**;**POSIX `O_APPEND` 的跨平台保证在 Windows 上不成立** |
|
|
1682
|
+
|
|
1683
|
+
### 33.4 定稿:P6 的不变量(可作为通用范式)
|
|
1684
|
+
|
|
1685
|
+
```
|
|
1686
|
+
residue(e) := e.to == null && (e.result !== "untested" || (now - e.ts) > STALE_MS)
|
|
1687
|
+
live_unsettled(e) := e.to == null && e.result === "untested" && (now - e.ts) <= STALE_MS
|
|
1688
|
+
STALE_MS = 10 * 60 * 1000
|
|
1689
|
+
```
|
|
1690
|
+
**它的正确性建立在「状态的语义排他性」上(结算过的边必有终点;未结算只有 `untested` 一种合法形态),时间只是兜底。**
|
|
1691
|
+
**⇒ 定稿原则:凡是「清理/治理」类不变量,优先用状态语义判据,不要用「时间看起来旧」这类启发式。**
|
|
1692
|
+
**负向门禁**:`live_unsettled > 0` ⇒ **拒绝执行清理**。
|
|
1693
|
+
|
|
1694
|
+
### 33.5 定稿:P6 方案选择
|
|
1695
|
+
|
|
1696
|
+
**A(保留但在分析中排除)立即做**(零风险、失败噪声 −100%、tokens −4.5%);**B(剪除)/ C(sidecar)等主人给停插件窗口** —— **要保留失败历史选 C,确认可丢选 B**。
|
|
1697
|
+
**优先处理 14 个 >80% 的会话**(都是 10-29 10:26 的历史会话)。
|
|
1698
|
+
|
|
1699
|
+
### 33.6 定稿:本轮新立的口径纪律(**第 6 次「同一件事两个数」之后**)
|
|
1700
|
+
|
|
1701
|
+
| # | 量 | 两个数 | 消解维 |
|
|
1702
|
+
|---|---|---|---|
|
|
1703
|
+
| 1 | HEAD 生产口径 avg | 422.77 / 427.18 / 428.62 / 428.86 / 429.77 | **快照指纹** |
|
|
1704
|
+
| 2 | S2a 边际 token | +57.8 / +1.15 / +4.35 | **`treeDir` 口径** |
|
|
1705
|
+
| 3 | 全量 passed/failed | 245/11 … 354/0 | **时点 + 窗口稳定性** |
|
|
1706
|
+
| 4 | tewari 调用次数 | 3523 / 2459 | **插桩 vs 结构推导** |
|
|
1707
|
+
| 5 | S2b 计数 | 11,172/11,205;1,182/2,590 | **口径未统一(待定)** |
|
|
1708
|
+
| **6** | **悬空率** | **46.8% / 30.9%** | **分母(11,907 → 18,038)** |
|
|
1709
|
+
|
|
1710
|
+
**⇒ 第 6 条最危险:比例类指标会被分母漂移伪造成「改善」。**
|
|
1711
|
+
**⇒ 定稿纪律(最终版):任何计数/比例指标,报告里必须自带一行「定义式 + 分子 + 分母 + 时点」**,缺任一维视为不可引用。
|
|
1712
|
+
|
|
1713
|
+
### 33.7 🔴 定稿:**1.9.2 不得带死开关**(lead 2026-10-30 03:0x 令;主表 P7-1 / task-58)
|
|
1714
|
+
|
|
1715
|
+
**「死开关」= 造出来了、但没有消费者的可配置项 / 状态字段 / 指标。**
|
|
1716
|
+
**本会话在 1.9.2 里出现了两次**:
|
|
1717
|
+
|
|
1718
|
+
| 次 | 实例 | 形态 |
|
|
1719
|
+
|---|---|---|
|
|
1720
|
+
| 1 | **P5**(task-41) | `fallback` / `overBudgetBy` / `budgetMax` **没有任何消费者**,只存在于 `renderLedger` 返回值与测试里 ⇒ **「不静默超预算」只完成可观测的一半** |
|
|
1721
|
+
| 2 | **P6a**(task-46) | **`traceRoot` 可配置但没有上游传值** ⇒ 生产走默认 `""`,**开关没拨** |
|
|
1722
|
+
|
|
1723
|
+
**⇒ 定稿(写死):1.9.2 不得带任何死开关。**
|
|
1724
|
+
**⇒ 验收硬要求(两条,缺一不可)**:
|
|
1725
|
+
1. **每个新增的可配置项 / 状态字段 / 指标,验收必须写明「谁消费它」** —— 写不出消费者就不许进验收。
|
|
1726
|
+
2. **必须有一条负向测试**:**不消费时测试必须红**(`traceRoot` 退回 `""` 必红、去掉 `fallback` 消费必红)。
|
|
1727
|
+
|
|
1728
|
+
**⇒ task-58(P7,blocked_by task-56)就是这条的执行项**:
|
|
1729
|
+
① `engine` 传 `traceRoot:"sessions"`;② `engine` 消费 `fallback`(over-budget 日志/指标含 `overBudgetBy`,`impossible` 走 error);③ `doctor`/health 显示 `fallback` 分布与最大 `overBudgetBy`;④ 两条负向测试。
|
|
1730
|
+
|
|
1731
|
+
**📌 为什么「死开关」比「没造」更危险**:**它会让下一个读代码的人以为问题已解决** —— 一个存在但无人消费的 `fallback` 字段,**在验收单上和在代码里都「看起来完成了」**。
|
|
1732
|
+
**验收语(Lead 定):「造了仪表盘必须有人看」。**
|
|
1733
|
+
|
|
1734
|
+
### 33.8 本节边界
|
|
1735
|
+
|
|
1736
|
+
- 只记录决定;**不含新测量**。
|
|
1737
|
+
- **未验证**:P5 的 `fallback` 是否有下游消费者(报告自认「未消费」);`impossible` 真实触发(0/133,且复核指出代码不可达);P6 的 B/C 未实施;**`traceRoot` 的运行期传值(task-58 之前一直未传)**;运行期收益全部未验证(贯穿全篇的同一项)。
|
|
1738
|
+
|
|
1739
|
+
---
|
|
1740
|
+
|
|
1741
|
+
## 三十四 · **1.9.2 发布前冻结顺序与验收清单**(2026-10-30 03:1x 定稿)
|
|
1742
|
+
|
|
1743
|
+
> 本节的顺序**不可颠倒**(教训来自 1.9.1:先打基线后改包内文档 ⇒ 基线作废,见 §三十二)。
|
|
1744
|
+
|
|
1745
|
+
### 34.1 冻结线内容(**进 1.9.2 的切片**)
|
|
1746
|
+
|
|
1747
|
+
| 切片 | task | 状态 |
|
|
1748
|
+
|---|---|---|
|
|
1749
|
+
| **P3 增量落盘** | task-39 | ✅ 已验收(task-51) |
|
|
1750
|
+
| **P4 首屏 `state` 缓存** | task-40 | ✅ 已验收(task-52) |
|
|
1751
|
+
| **P5 token 预算三态** | task-41 | ✅ 已验收(task-54,有保留) |
|
|
1752
|
+
| **P6a TRACE 相对** | task-46 | ✅ 已验收(task-55,有保留) |
|
|
1753
|
+
| **P6-A 悬空边分析排除** | task-56 | 执行方完成 · **待 task-57 复核** |
|
|
1754
|
+
| **P7 死开关接线** | task-58 | 执行方完成 · **待 task-60 复核** |
|
|
1755
|
+
| **S2b no-exit 收窄** | task-27 | ✅ 已验收(task-53,有保留) |
|
|
1756
|
+
|
|
1757
|
+
### 34.2 **明确不进 1.9.2 的**(写死,防止被临时加回来)
|
|
1758
|
+
|
|
1759
|
+
| 项 | 理由 |
|
|
1760
|
+
|---|---|
|
|
1761
|
+
| **task-59(`addUserNode` 返回语义,技术债)** | **低优先级、不挡发布**;它是「API 形状」问题,适合单独一轮改 |
|
|
1762
|
+
| **P6 的 B / C 方案(剪除 / sidecar 归档 5,567 条悬空边)** | **需停插件窗口 + 主人授权**;B 还会不可逆丢失 268 条失败边 |
|
|
1763
|
+
| **S1b(清理 2 张历史自环图)** | 同上,需停插件窗口 |
|
|
1764
|
+
| **`files` 瘦身(V8)** | 1.9.2 不动包形态,避免又一次基线作废 |
|
|
1765
|
+
| **`HANDOFF.md` 重写(V5)** | 单独立项(整篇重写,非改两行) |
|
|
1766
|
+
|
|
1767
|
+
### 34.3 冻结顺序(**五步,必须按序**)
|
|
1768
|
+
|
|
1769
|
+
| 步 | 动作 | 为什么在这一步 |
|
|
1770
|
+
|---|---|---|
|
|
1771
|
+
| **1** | **等 task-57(P6-A 复核)与 task-60(P7 复核)双双 PASS** | 冻结线里最后两个未复核项;**复核未过就冻,冻的是不合格的树** |
|
|
1772
|
+
| **2** | **列出 `files` 内的全部可写面并锁死**(docs / evidence / src / lib / scripts / CHANGELOG / README / QUICKSTART / LICENSE) | **先冻面** —— 否则任何人改一行包内文件就作废基线 |
|
|
1773
|
+
| **3** | **出全树指纹**:`sha256(src/*.mjs + lib/*.js + scripts/*.mjs + package.json + README.md + CHANGELOG.md + .gitignore + docs/*.md + test/*.mjs)` | 沿用 task-49 §9.1 做法(**该指纹同时覆盖 `docs/*.md`,能抓「文档被改」**) |
|
|
1774
|
+
| **4** | **打基线**:`npm pack --dry-run --json` → 记 **entryCount / unpackedSize / size / shasum**,并逐文件记 sha256 | 基线只能在面锁死之后打 |
|
|
1775
|
+
| **5** | **re-gate**:G1 全量 / G2 `verify-install` / G3 `check-maintenance`(**publish 前必然红,属顺序问题**)/ G4 pack;**门禁 START==END 全树指纹一致** | 证「窗口内无人写盘」 |
|
|
1776
|
+
|
|
1777
|
+
### 34.4 验收清单(**逐项打勾才许 publish**)
|
|
1778
|
+
|
|
1779
|
+
**A · 功能与测试**
|
|
1780
|
+
- [ ] 全量测试 **0 failed**(**稳定窗口**:无 teammate 在写 `src/`/`test/`;门禁前后全树指纹一致)
|
|
1781
|
+
- [ ] `verify-install` **16/16**
|
|
1782
|
+
- [ ] `doctor` **错误 0**(警告数记录在案)
|
|
1783
|
+
- [ ] 每个切片**至少一个变异体真红**(已各自在报告里给出,复核时确认)
|
|
1784
|
+
|
|
1785
|
+
**B · 语义与契约**
|
|
1786
|
+
- [ ] **`graph.json` 契约变化已进 README**(「上一回合基线 + 当前增量,回合内落后一回合,外部工具不得直接当最新」)
|
|
1787
|
+
- [ ] **无死开关**:`traceRoot` 有 engine 调用点、`fallback` 有消费者、`doctor` 能显分布(task-58 / task-60)
|
|
1788
|
+
- [ ] `renderFacts` 与基线一致(P6-A 除外,其改动有单独验收)
|
|
1789
|
+
- [ ] 生产口径:**超预算新增 0**、max 不增、avgDelta ≤ 上限;**CUR-only = 0**
|
|
1790
|
+
|
|
1791
|
+
**C · 包与发布物**
|
|
1792
|
+
- [ ] `npm pack` **61 文件**(若文件数变化须解释)
|
|
1793
|
+
- [ ] 包内 **`tmp/` 0、`test/` 0、密钥 0**
|
|
1794
|
+
- [ ] **包内死链 0**(口径:剥掉 ``` 围栏与 `行内代码` 后只匹配真 markdown 链接)
|
|
1795
|
+
- [ ] 版本一致性:`package.json` / `CHANGELOG` 顶部 / `README` 版本行 / npm latest / git tag 五处对齐
|
|
1796
|
+
|
|
1797
|
+
**D · git 与发布顺序**
|
|
1798
|
+
- [ ] `HEAD..origin/main = 0`(发布提交建立在 origin/main 之上)
|
|
1799
|
+
- [ ] 无敏感路径(`grep -iE 'key|token|secret|.env|credential'` = 0)
|
|
1800
|
+
- [ ] **顺序:commit → push → tag → publish → Release**(或明确把 G3 定义为 post-publish 复验)
|
|
1801
|
+
|
|
1802
|
+
### 34.5 记帐纪律(每轮都必须执行)
|
|
1803
|
+
|
|
1804
|
+
1. **数字必须带时点 + 批次**(`347/0` 与 `354/0` 与 `369/0` 与 `373/0` 都是真的,差的是窗口)。
|
|
1805
|
+
2. **跨批不可比**:HEAD 的绝对 avg / over 会随图库增长漂移,**只有「同快照净增」是稳定判据**。
|
|
1806
|
+
3. **双 sha**:被审版本与验收版本可能不同(如 P6a 的 `96b0bbce` vs 当前 `d71a1dab`),**报告必须写清锁哪一个**。
|
|
1807
|
+
4. **每个 task 完成当轮回填主表状态列** —— 否则账本从「索引」退化为「笔记」。
|
|
1808
|
+
|
|
1809
|
+
### 34.6 本节边界
|
|
1810
|
+
|
|
1811
|
+
- 只写顺序与清单;**不含新测量**。
|
|
1812
|
+
- **未验证**:task-57 / task-60 结论未出;冻结线的最终文件指纹与 shasum **必须届时现打**(本节故意不写具体数值,避免过期误导)。
|
|
@@ -896,3 +896,63 @@ ode_modulesdsh-gokifu-compactscriptsinstall.mjs`)⇒ 判 false ⇒ 连 `run()`
|
|
|
896
896
|
**另记一条正面实践**:`277 passed / 7 failed` 那次重跑**同时记了窗口内三文件 sha**(`ui-config 0513f4d5` / `ledger de135fc3` / `c17 测试 dafa0442`)⇒ **报数字同时报树指纹**。这是对「输入在变被读成输出在变」(§18.2-3)的**工程化解决**,比写纪律更可靠。**以后门禁重跑一律带窗口 sha。**
|
|
897
897
|
|
|
898
898
|
- **2026-10-30 R12 追加 · S2a 归因更正 + 五元组**:撤回「S2a +57.8」的归因(真值 +1.15、超预算 10→10);新增否决 R12-5(把「数值变大」当「归因成立」);口径四元组升五元组(补「实现状态」维);记录「重跑带窗口 sha」这一正面实践。**R1–R11 与 R12 前六节一字未改。**
|
|
899
|
+
|
|
900
|
+
---
|
|
901
|
+
|
|
902
|
+
## 十九 · R13 · 我把「修一个死链」当成了纯文档动作(2026-10-30 02:36–02:45)
|
|
903
|
+
|
|
904
|
+
> **这一轮只问一件事:一个「正确的改动」能不能造成一次事故?**
|
|
905
|
+
> 答案:**能 —— 如果时机错。** 这是本会话第一次由**我自己的合规动作**直接作废别人的基线。
|
|
906
|
+
|
|
907
|
+
### 19.1 事故链(每一步都合规,合起来是事故)
|
|
908
|
+
|
|
909
|
+
| 步 | 我的动作 / 事实 | 当时是否合规 |
|
|
910
|
+
|---|---|---|
|
|
911
|
+
| 1 | `ws-release-exec` 提 **V4**:`docs/方案-持续优化.md:254` 的 `[计划表 §S10′ 完成证据](../计划表.md)` 在安装包内 **404** | — |
|
|
912
|
+
| 2 | lead 判「**V4 交 scribe**」 | ✅ 是给我的活 |
|
|
913
|
+
| 3 | **我按「最小修复」改了那一行**(链接 → 纯文本引用 + 反引号包裹原链接) | ✅ **改法正确、范围最小、只 1 处** |
|
|
914
|
+
| 4 | **但 `docs/方案-持续优化.md` 在 npm `files` 里** | ❌ **我不知道(没查 `files`)** |
|
|
915
|
+
| 5 | ⇒ `npm pack` shasum 从 `e9be1e2d…` 变了 | — |
|
|
916
|
+
| 6 | ⇒ **task-24 刚出的 G4 基线立刻作废** | — |
|
|
917
|
+
| 7 | ⇒ 新建 **task-49** re-gate,verifier 白跑一轮 | — |
|
|
918
|
+
|
|
919
|
+
**⇒ 定性:修复本身是对的,错的是时机。这不算「改错」,算「顺序错」。**
|
|
920
|
+
|
|
921
|
+
### 19.2 根因(不是「不小心」,是「缺一步检查」)
|
|
922
|
+
|
|
923
|
+
**我改文件前查了「这行说的是什么」,但没查「这个文件在不在发布包里」。**
|
|
924
|
+
**而 `docs/` 占包内容 62.3%** —— **在这个仓库里,「文档」和「发布物」是同一个东西。**
|
|
925
|
+
|
|
926
|
+
⇒ **新纪律**:**凡改 `files` 覆盖范围内的文件,改动前必须先问「这次改动会不会让某个基线失效」**,并**在动手前告知维护基线的人**。
|
|
927
|
+
|
|
928
|
+
### 19.3 本轮否决(R13-1…R13-3,★ 只增不删)
|
|
929
|
+
|
|
930
|
+
| # | 设想 | 否决理由(可复现) | 时间 |
|
|
931
|
+
|---|---|---|---|
|
|
932
|
+
| R13-1 | ~~「文档改动不影响发布」~~ | `files` 含 `docs`,占包内容 **62.3%** ⇒ **改一行文档 = 改发布包**。我的 V4 修复直接作废了 task-24 的 G4 基线(`e9be1e2d…`),须 re-gate(task-49) | 2026-10-30 |
|
|
933
|
+
| R13-2 | ~~「先打基线、再收尾文档」~~ | 顺序反了 ⇒ **基线必然作废**。正确顺序是 **① 冻结写入面(列出 `files` 内全部可写文件并锁死)→ ② 打基线**。**「冻结令不写清『哪些文件在包内』就等于没冻结」** | 2026-10-30 |
|
|
934
|
+
| R13-3 | ~~「re-gate 只看 shasum 有没有变」~~ | 更该看 **「文件数 / 体积差」是否与声称的改动量吻合**:task-49 相对 task-24 **entryCount 61→61(0)**、**unpacked +276 B**、**tarball +132 B** ⇒ 与「一行文本改引用」同量级 ⇒ **反证无其他成分混入**。若差几十 KB,说明有未承认的改动 | 2026-10-30 |
|
|
935
|
+
|
|
936
|
+
### 19.4 被划掉的设想
|
|
937
|
+
|
|
938
|
+
- ~~「既然 publish 完了,就把冻结期的 pending 内容一次性全塞进 docs」~~ —— 划掉:**每次再发布前仍要重走「冻结 → 打基线 → re-gate」**;**发布后 docs 写入不影响已发布包,但会影响下一次发布的基线。**
|
|
939
|
+
- ~~「V4 那条我改完了,`docs/计划表.md:3` 那处也顺手一起改」~~ —— 划掉:**冻结令下不能顺手**;而且后来证据表明 **`docs/计划表.md` 那处根本不是 markdown 链接**(复扫口径剥掉围栏与行内代码后,真实死链 **0 条**)⇒ **它不需要改**。**「顺手」会制造不必要的事故面。**
|
|
940
|
+
|
|
941
|
+
### 19.5 正面样本(本轮唯一做对的事)
|
|
942
|
+
|
|
943
|
+
**冻结令下来后,我做的第一件事是把「能写什么 / 不能写什么 / 写哪去」变成表格 + 缓冲目录**(`scribe-buffer/README.md` + `pending-方案.md` + `pending-草稿.md`),并在每次自核时**复查冻结文件 sha 是否变化**(`93c8a36c` / `35de851c` 全程未变)。
|
|
944
|
+
⇒ **verifier 在 task-49 §9.6 点名「scribe 的 task-12/13 仍 in_progress 是唯一结构性风险」时,我能用「冻结时实测 sha 与报告基线逐字一致」自证已停手。**
|
|
945
|
+
**⇒ 结论:冻结期最值钱的动作不是「忍住不写」,是「把『我还能写哪』变成可核对的清单」。**
|
|
946
|
+
|
|
947
|
+
### 19.6 本轮待定问题
|
|
948
|
+
|
|
949
|
+
| # | 问题 | 归属 | 倾向 |
|
|
950
|
+
|---|---|---|---|
|
|
951
|
+
| 1 | `HANDOFF.md` 整体过期(顶部还是 v1.3.5 紧急提示) | 单独立项 | **整篇重写**,不要只改两行(那会造成「看起来新」的误导) |
|
|
952
|
+
| 2 | `files` 是否排除维护侧材料(62.3% 是 docs+evidence) | 主人 | 1.9.1 保持现包形态已定;**1.9.2 再议** |
|
|
953
|
+
| 3 | 是否补 `v1.9.0` tag | 主人 | **不补**(会让 GitHub 出现 npm 上并不存在的 1.9.0) |
|
|
954
|
+
| 4 | 每次再发布前的冻结面清单要不要固化成脚本 | 后续 | 倾向做成 `scripts/release-freeze.mjs`:从 `files` 生成「冻结清单 + 全树指纹」,**把 R13-2 的顺序变成机器强制** |
|
|
955
|
+
|
|
956
|
+
### 19.7 变更日志追加
|
|
957
|
+
|
|
958
|
+
- **2026-10-30 R13 冻结期与顺序错误**:确立「发布冻结令 = 先冻结写入面、再打基线」、「文档改动 = 包改动(`docs` 占包 62.3%)」、「re-gate 要看体积差而非只看 shasum」三条;新增三条否决(R13-1 文档不影响发布 / R13-2 先打基线后收尾文档 / R13-3 只看 shasum);记录事故链(V4 修复 → G4 基线作废 → task-49)、冻结期缓冲机制,以及「1.9.1 已发布成功(shasum `dfb255da…`)」。**R1–R12 的设想与否决记录一字未改。**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-gokifu-compact",
|
|
3
|
-
"version": "1.9.
|
|
3
|
+
"version": "1.9.2",
|
|
4
4
|
"description": "DSH context-compression backend with a go game record (kifu) DAG: node = position, edge = attempted move + result. Incremental fold (each span summarized once), failure memory that survives compaction, transposition detection (chess-engine style), and a tiny hot ledger rebuilt every step.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -27,6 +27,7 @@
|
|
|
27
27
|
"files": [
|
|
28
28
|
"src",
|
|
29
29
|
"!src/.engine-under-test.mjs",
|
|
30
|
+
"!src/.engine-under-test-*.mjs",
|
|
30
31
|
"!src/.harness-stubs",
|
|
31
32
|
"lib",
|
|
32
33
|
"docs",
|
package/scripts/doctor.mjs
CHANGED
|
@@ -290,6 +290,40 @@ add(
|
|
|
290
290
|
);
|
|
291
291
|
add("最近一次落盘", newestFlush > 0 ? "ok" : "warn", newestFlush ? new Date(newestFlush).toISOString() : "没有任何棋谱");
|
|
292
292
|
|
|
293
|
+
/* ── 5. 热清单预算(fallback 仪表盘,P7 / task-58) ──
|
|
294
|
+
engine 每次 renderLedger 后会把 {none, over-budget, impossible, maxOverBudgetBy} 落到
|
|
295
|
+
`<DSH_HOME>/chess-context/ledger-budget.json`。没有这个文件 = 从未采集(旧版本或还没跑过)
|
|
296
|
+
—— 必须明确写「未采集」,不能假装健康。 */
|
|
297
|
+
{
|
|
298
|
+
const budgetFile = nodePath.join(home, "chess-context", "ledger-budget.json");
|
|
299
|
+
let stats = null;
|
|
300
|
+
let parseError = "";
|
|
301
|
+
if (nodeFs.existsSync(budgetFile)) {
|
|
302
|
+
try {
|
|
303
|
+
stats = JSON.parse(nodeFs.readFileSync(budgetFile, "utf8"));
|
|
304
|
+
} catch (err) {
|
|
305
|
+
parseError = err?.message ?? String(err);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
if (!nodeFs.existsSync(budgetFile)) {
|
|
309
|
+
add("热清单预算(fallback)", "warn", "未采集(还没有 ledger-budget.json —— 插件尚未产生新的热清单,或版本 < 1.9.2)");
|
|
310
|
+
} else if (parseError) {
|
|
311
|
+
add("热清单预算(fallback)", "warn", `未采集(ledger-budget.json 读不出:${parseError})`);
|
|
312
|
+
} else {
|
|
313
|
+
const none = Number(stats.none ?? 0);
|
|
314
|
+
const over = Number(stats["over-budget"] ?? 0);
|
|
315
|
+
const impossible = Number(stats.impossible ?? 0);
|
|
316
|
+
const maxOver = Number(stats.maxOverBudgetBy ?? 0);
|
|
317
|
+
const total = none + over + impossible;
|
|
318
|
+
add(
|
|
319
|
+
"热清单预算(fallback)",
|
|
320
|
+
impossible > 0 ? "error" : over > 0 ? "warn" : "ok",
|
|
321
|
+
`none=${none} / over-budget=${over} / impossible=${impossible} · 最大 overBudgetBy=${maxOver}` +
|
|
322
|
+
`(共 ${total} 次渲染${total === 0 ? ";样本为空" : ""}${stats.updatedAt ? `,更新于 ${new Date(stats.updatedAt).toISOString()}` : ""})`,
|
|
323
|
+
);
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
|
|
293
327
|
/* ── 输出 ── */
|
|
294
328
|
const bad = checks.filter((c) => c.status === "error").length;
|
|
295
329
|
const warn = checks.filter((c) => c.status === "warn").length;
|