dsh-fast-mode 1.5.21 → 1.5.22

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/BENCHMARK.md CHANGED
@@ -2181,3 +2181,141 @@ python3 bench/suite-run.py --tasks debugfix,ledger,codeimpl --arms off,def15,bud
2181
2181
  **读法:几何与胜场分离。** 快的时候快很多(0.407、0.470、0.560),慢的时候慢得不多(1.147、1.294、1.323,单看都在或接近 ±20 % 噪声带),所以几何 0.770× 而胜场只有 4/7。p3/p6 有一个共同点:`def15` 步数更少但输出 token 更多(4 553 vs 4 106、5 582 vs 4 046)——第 4 步的「少写过程」提醒在这两格里像是让模型**先压缩一次、再补回来**。这是短任务上提醒本身有成本的又一条实例(与 1.5.1 里 `perfrefactor` 1.18×、`csvpipe` 1.12× 同向),不是判 FAIL 的依据:`DEF_DBG` 不承担改默认的职责,它只回答「发布版默认还快不快」。
2182
2182
 
2183
2183
  **合池(`DEF_MIG` + `DEF_DBG`,发布版 1.5.19,12 对)。** 几何 **0.735×**(省约 26.5 %)、逐格中位 **0.708×**、9/12 更快、范围 0.407–1.323×;两族步数几何 0.886×、输出几何 0.907×。这一组是「发布版对新会话」的最干净对照(`off` = `enabled: false`,同一台机、ABBA 交错),结论:**1.5.19 减少提示之后,默认配置的提速还在,方向和量级与全池 0.823× 一致**;单块 n 小(5 和 7),对外仍只用 138 对全池口径,这组数字作为发布版复核记录。
2184
+
2185
+ ## 提醒实际投递分布:复用建议在 951 份套件转录里 0 次
2186
+
2187
+ **问题。** 文档把「同一条检查原样重复时建议复用」列为提速机制之一,但这个机制只有在模型真的重复同一条检查时才出现。先量它是否出现,再决定要不要为它投入。`python3 bench/reminders.py`(默认扫 `~/.dsh/sessions/--*dsh-runs-suite-*`,共 951 份转录、含全部已完成的臂与块)按注入文案分类统计:
2188
+
2189
+ | 提醒种类 | 投递次数 |
2190
+ | --- | --- |
2191
+ | `output-discipline`(少写过程) | 754 |
2192
+ | `write-and-verify`(改了 2 个文件后跑一次检查) | 84 |
2193
+ | `batch-now`(一次把独立调用发齐) | 60 |
2194
+ | `reuse-check`(检查原样重复,建议复用) | **0** |
2195
+
2196
+ 951 份转录里 742 份(78 %)至少收到过一条提醒,说明投递链路本身是通的;**`reuse-check` 一条都没有**。套件任务的形状是「改文件 → 跑一次隐藏测试 → 结束」,检查不会被逐字重跑,所以复用建议没有触发条件。结论有两条:
2197
+
2198
+ 1. 已测出的 0.823×(12 块 / 138 对)**不是**复用建议带来的,它来自少写过程、批量化与步数混合。任何把 0.823× 归因到「复用检查」的说法都不成立。
2199
+ 2. 复用建议的实现(按产物指纹失效、写未知即停)仍然正确,但它属于**防御性正确**而不是**已证实的加速项**:本地语料里它的收益是 0。要证明它有效,必须先构造一个会逐字重跑检查的任务形状,而不是在现有套件上再跑一个块。
2200
+
2201
+ 下一步因此改成量**投递最多的那一条**:`outputDiscipline: late` 与 `off` 的差,就是「少写过程」这一条提醒单独的贡献(块 `LEN_MIG` / `LEN_DBG`,臂 `def15` vs `nodisc`,后者与前者只差 `outputDiscipline: false`)。
2202
+
2203
+ ## 一步值多少钱:llm 秒数 ≈ 14.5 s × 步数 + 24 s × 千 token(R² = 0.42,n = 568 条合格行)
2204
+
2205
+ **为什么量这个。** 前面所有块的结论都能归结成一句「快在步数,不在每步」。这句话需要一个换算比,否则「再省一步」值多少秒说不清。对全部已完成的合格行(`valid`,568 条,跨所有块与臂)做最小二乘,被解释量是该行记录的模型耗时 `llm`(秒),解释量是 `steps` 与 `total`(千 token):
2206
+
2207
+ ```
2208
+ llm = 14.5 × steps + 24.2 × tokens − 286.8 R² = 0.416
2209
+ 均值:steps 9.34,tokens 14.26,llm 193.8 s
2210
+ ```
2211
+
2212
+ (`llm` 是模型耗时秒数;`tokens` 单位是千 token;n=568 是所有已完成块的合格行。这个式子是描述性的,不是外推模型。)
2213
+
2214
+ 读法:**在均值附近,每一步对应约 14.5 s**,每千 token 约 24 s。两点必须同时说清:①截距是 −286.8 s,且 `steps` 与 `tokens` 高度共线(长任务两样都多),所以这个线性式只在均值附近可读,不能外推成「第 20 步值 290 s」;②它给出的是**换算比**而不是因果——真正的因果证据是下一节的配对拆分(步数 0.778×、每步 1.02×)。
2215
+
2216
+ **换算成目标。** 在 token 不变的前提下,把总时长压到 0.90× 需要步数从 9.34 降到 8.00(−14 %);0.85× 要到 7.33(−21 %);0.80× 要到 6.67(−29 %)。已测的池化口径 0.823×(步数几何 0.806)正好落在这条线的位置:它**已经**吃到了约 19 % 的步数下降,而每步内部几乎没动(0.996)。
2217
+
2218
+ **因此剩下的空间在哪。** ①继续找「可以合并/跳过的一步」;②降低每步的固定成本本身(工具往返、等待落地),但后者不是提示词能碰的;③模型侧尾巴(单格 3–7× 的长尾)无法靠机制消除,几何均值在 n≈7 时对一格极值极度敏感——这也是前面反复出现「同形状两块给出相反结论」的原因。任何声称 0.6× 的说法都要用「步数降到 6.7 以下且质量不掉」来证明,而不是拿一次长尾反面样本充数。
2219
+
2220
+ ## 到底省在哪:步数 0.78×、可见输出 0.53×、token 1.00×、每步时间 1.02×(15 对同题同配)
2221
+
2222
+ 把池化口径拆成可解释的四个量,只在 **`off` 与 `def15` 各自都合格**的那 15 对上取中位数(同题、同一 pair 号,避免跨题混池):
2223
+
2224
+ | 量 | `off` | `def15` | 比值 |
2225
+ | --- | --- | --- | --- |
2226
+ | 步数 | 9.0 | 7.0 | **0.778** |
2227
+ | 可见输出字符 | 12 913 | 6 887 | **0.533** |
2228
+ | 总 token(千) | 14.0 | 14.0 | 1.000 |
2229
+ | 每步 llm 秒 | — | — | 1.02 |
2230
+ | 总时长(秒) | 148.5 | 117.9 | **0.794** |
2231
+
2232
+ 读法:插件把**可见输出砍掉一半**、把**步数砍掉约 22 %**,但**总 token 没动**(省下来的可见文字被推理/工具结果补上了),**每一步并没变快**。所以 0.79× 的时长收益几乎全部来自「少走几步」,与上面 14.5 s/步的固定成本换算一致:少 2 步 ≈ 省 29 s ≈ 20 %。
2233
+
2234
+ 这一条同时排除了两种常见误判:①「插件让模型每步跑得更快」——不成立,每步时间一致;②「插件靠少写文字省 token」——可见文字确实减半,但总 token 不变,省的是**步数**,不是账单。要再往下压时间,只能继续减步数,或者在固定成本(工具往返、等待落地)上做文章。
2235
+
2236
+ ## 逐步骤指标里的交换比:每个输出 token 约 10 ms(R² = 0.82,n = 8 917 步,本机今日单会话)
2237
+
2238
+ 上面那块是套件里的行级换算,样本被任务长度主导。另一条独立证据来自插件自己记的逐步骤指标。**口径先说清**:`~/.dsh/fast-mode-metrics.jsonl` 目前只有本机今天的记录(同一个长会话),所以下面是「这台机器、这个网关、这个模型」的交换比,不是用户群体的统计。取 `mode: on` 的 8 917 个正常步,被解释量是该步模型耗时 `llmMs`,解释量是 `output`(输出 token)、`ttftMs`(首字延迟):
2239
+
2240
+ ```
2241
+ llmMs ≈ 10.05 × output + 1.09 × ttftMs − 159 R² = 0.817
2242
+ ```
2243
+
2244
+ - **每个输出 token 约 10 ms**:真机中位输出 264 token ⇒ 约 2.6 s/步,占该步中位 3.69 s 的七成。这是「少写过程」这条提醒唯一能直接触碰的项,也是它为什么值钱。
2245
+ - **首字延迟整体平移**:`ttftMs` 系数 1.09(不是叠加而是同步放大),中位 1.16 s。
2246
+ - 只放 `output` 时 R² = 0.649,加入 `ttftMs` 跳到 0.817;再加 `cacheRead` 不再提升(0.817)——上下文的**缓存命中**不直接影响单步耗时,影响的是首字里已经算进去的那部分。
2247
+
2248
+ 与 `src/guidance.js` 里记的旧模型(`720 ms + 6.6 ms/输出 token`,R² 0.93)相比,斜率从 6.6 涨到 10.0 ms,说明这台机器当前的解码更慢、每 token 更贵:**同样的「少写一半」在这里更值钱**。两个模型的共同结论是同一个:输出长度是插件能动的最大单项。
2249
+
2250
+ 由此得到本轮的口径:池化 0.823× 里,步数贡献约 0.78×,输出减半贡献的主要是每步的 token 项;两者相乘才对得上实测。下一步要再压时间,只有两条路——更少步数、更少输出 token——而它们都已经在默认开关里打开,剩下是**措辞与触发**能不能让模型真的照做。这个会话本身就是反例:8 888 步里 `calls ≥ 2` 的只占 5.4 %(`1` 调用 8 128 步、`2` 调用 421 步),说明「一次把独立调用发齐」的提示在实际行为里基本没被执行——**遵从率才是下一个可量的点**,而不是再加一条提醒。
2251
+
2252
+ ## 机制拆解:不是靠并行,是「每样都少一点」(168 对同题同配,直接数转录)
2253
+
2254
+ 前面的换算都是行级数字。这一节换了证据来源:打开 168 对**同 tag、同任务、同 pair、只差 arm** 的会话转录(`~/.dsh/sessions/--*suite-<tag>-<task>-p<pair>-<arm>--`),直接数里面的东西。命令:
2255
+
2256
+ ```
2257
+ python3 bench/mechanism.py --arm off --arm def15 # 步数 / 调用数 / 并行率 / 只叙述步
2258
+ python3 bench/textshare.py --arm off --limit 0 --json # 全文的口径与 token 分类
2259
+ ```
2260
+
2261
+ | 每会话中位数 | off | def15 | 几何比 | 中位比 |
2262
+ |---|---|---|---|---|
2263
+ | assistant 消息(= 步数) | 10 | 7 | 0.806 | 0.828 |
2264
+ | 工具调用数 | 11 | 9 | 0.786 | 0.815 |
2265
+ | 每消息调用数 | 1.00 | 1.00 | 0.994 | — |
2266
+ | 多调用消息占比 | 5.2 % | 6.3 % | — | 1.000 |
2267
+ | 只叙述不带调用的消息占比 | 10.0 % | 12.5 % | — | 1.194 |
2268
+ | 可见 prose 字符 | 92 | 93 | 0.804 | 1.000 |
2269
+ | 工具参数字符 | 11 812 | 10 120 | 0.839 | 0.911 |
2270
+ | 推理(reasoning)字符 | 47 510 | 34 778 | **0.753** | 0.815 |
2271
+
2272
+ 三个结论,都能被上面那张表直接读出:
2273
+
2274
+ 1. **并行不是杠杆。** 每消息调用数 1.00 vs 1.00(比 0.994),多调用消息占比 5.2 % → 6.3 %(中位比 1.000)。加不加插件,模型都是一条消息一个调用;池化 0.823× 里没有任何一份来自「一次发多个调用」。这也解释了为什么反复打磨并行提示词没有效果(见 B1 段落)。
2275
+ 2. **省的是「每样都少一点」,不是某一种输出。** 步数 0.83、调用 0.79、工具参数 0.84、可见 prose 0.80、推理 0.75——全都落在 0.75–0.84。没有任何一项是「砍半」,也没有任何一项反向变大。乘起来正好是实测的 0.82× 墙钟。
2276
+ 3. **真正的大头是推理,不是可见文字。** 每个会话中位 47.5 k 推理字符 vs 92 个可见 prose 字符——**可见文字只占总输出字符的 0.2 %**。所以「少写过程」这条提醒值钱的地方不是它写的那些字,而是它连带把模型每步的**思考**压短了 25 %(0.753);只盯着可见文字去优化,天花板就是 0.2 %。
2277
+
2278
+ 读法上的边界:这是本机 168 对套件转录,`off` 与 `def15` 各自只有一遍,落在 ±20 % 的诚实带内(见「诚实带」一节),所以「0.75–0.84」应读作方向与量级,不是精确系数;但「并行率不变」在这种样本量下是稳的(两条独立口径都给 1.00)。
2279
+
2280
+ ## 纪律提醒到底占多少:关掉它慢 1.54×(11 对,单遍)
2281
+
2282
+ `def15` 与 `nodisc` 解析后的配置**只差一项**:`discipline: 'full' → 'off'`(`bench/arms/nodisc.yml`,用 `bench/arm-config.mjs --json` 对过). 于是这一块直接量「输出纪律」这条提醒的独立份额:
2283
+
2284
+ ```
2285
+ python3 bench/block_score.py LEN_MIG def15 nodisc
2286
+ python3 bench/block_score.py LEN_DBG def15 nodisc
2287
+ ```
2288
+
2289
+ | 块 | 可用对 | wall 几何比(nodisc/def15) | 中位比 | def15 更快 | 符号检验 |
2290
+ |---|---|---|---|---|---|
2291
+ | LEN_MIG(migrate,5 对) | 4 | 1.189 | 1.275 | 2/4 | p = 1.000 |
2292
+ | LEN_DBG(debugfix,7 对) | 7 | **1.790** | 1.978 | **6/7** | p = 0.125 |
2293
+ | 池化 | 11 | **1.542** | 1.744 | 8/11 | — |
2294
+
2295
+ - 在 **debugfix** 上关掉输出纪律几乎翻倍:中位 1.98×,7 对里 6 对变慢,步数 1.32×、输出 token 1.51×。
2296
+ - 在 **migrate** 上没有信号:4 对里 2 对更快、2 对更慢,几何比 1.19 但被一个 2.86× 的单元格撑起来。
2297
+ - 池化 **1.54×**(中位 1.74×)⇒ 输出纪律是本机目前**最大的单项杠杆**,量级 1.3–1.8×,方向稳定、精度不足(每个单元格只有一遍,落在诚实带内)。
2298
+
2299
+ 这条同时给「不要再加提醒时机」提供了支持:既然唯一的提醒已经承担了最大份额,继续加同类提醒的边际收益应当来自**同一句话的措辞**,而不是更多触发点——这也是下一轮该测的东西。
2300
+
2301
+ ## 同一配置再跑一遍会差多少:块级差 4 %,任务级差 15–31 %(REP2,10 对)
2302
+
2303
+ 外部评审提的第一条是稳定性。这一块不新增任何开关,只把 `migrate`+`debugfix` 的同一批格子**用同一套配置再跑一遍**(`REP2`,20 次运行,顺序 ABBA 与之前的块一致),看同配置的两次独立测量互相差多少:
2304
+
2305
+ ```
2306
+ python3 bench/block_score.py REP2 off def15
2307
+ ```
2308
+
2309
+ | 口径 | DEF(第一遍) | REP2(第二遍) | 两次相差 |
2310
+ |---|---|---|---|
2311
+ | 全部可用对(DEF 12 / REP2 10) | 0.823 几何 / 0.799 中位 | **0.791 几何 / 0.746 中位** | **4 %** |
2312
+ | `debugfix` 5–7 对 | 0.770 | 0.628(5 对) | 15 % |
2313
+ | `migrate` 5 对 | 0.688 | 0.997(5 对) | 31 % |
2314
+ | 同一批 cell(migrate p1–p4,逐格比) | 0.655 | 0.861 | 31 % |
2315
+ | 同一批 cell(debugfix p1–p5,逐格比) | 0.736 | 0.628 | 15 % |
2316
+
2317
+ - **块级结论可复现**:两次独立测量给 0.79 / 0.82,差 4 %;两次都是「插件更快」。
2318
+ - **任务级不可复现**:`migrate` 一遍 0.688(5/5 更快)、另一遍 0.997(3/5 更快),同一批 4 个格子的逐格几何比也从 0.655 漂到 0.861。原因在单格:REP2 的 10 个格子跨度是 **0.28×–1.79×**,其中 `migrate p4` 一次 1.79×、`debugfix p1` 一次 0.28×——单格结果由那一次模型走多少步决定,不由配置决定。
2319
+ - 因此文档里只对外报**池化**口径(138 对 0.823×、中位 0.799×、93/138 更快),并明确标注「单格/单块差异落在 ±20 % 内是噪声」。**任何以单块(含 n≈5–12 的块)为依据的结论都不作为决策依据**——这一条现在有了同配置重复的直接证据,不再是凭经验给的宽限。
2320
+
2321
+ `REP2` 的默认手臂同时复核了一件事:`def15` 在这里是**发布版源码**(`node_modules/dsh-fast-mode` 指回仓库),所以 0.791 也是「当前代码在跑」的又一次独立确认,不是旧数据的复用。
package/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.5.22(只加量测与文档:机制拆解、纪律份额、同配置重复性;默认行为不变)
4
+
5
+ - **机制拆解落地成工具**:新增 `bench/mechanism.py`,把 168 对「同 tag、同任务、同 pair、只差 arm」的会话转录逐项数出来(步数、调用数、并行率、只叙述步、prose/参数/推理字符)。结论写进 BENCHMARK:并行的两条口径都是 1.00(即并行**不是**杠杆);步数 0.83 / 调用 0.79 / 参数 0.84 / prose 0.80 / 推理 0.75,全都落在 0.75–0.84——插件是「每样都少一点」,不是砍某一种输出。转录里推理字符占输出字符的 99.8 %(每会话中位 47.5 k vs 92),所以只盯可见文字优化、天花板是 0.2 %。
6
+ - **纪律提醒的独立份额实测**:`bench/arms/nodisc.yml`(与 `def15` 只差 `discipline: 'full' → 'off'`)在 `LEN_MIG` + `LEN_DBG` 上给出 pool **1.54×**(11 对,中位 1.74×;debugfix 单看 1.79×、6/7 对变慢)⇒ 输出纪律是目前最大的单项杠杆。据此把 `docs/REFERENCE.md` / `REFERENCE.en.md` 里「这条不是提速主要来源」的旧说法改成实测口径。
7
+ - **同配置重复性**:`REP2` 把 `migrate`+`debugfix` 用同一套配置再跑一遍。块级 0.791 vs 首遍 0.823(差 4 %),任务级差 15–31 %,单格跨度 0.28×–1.79× ⇒ 「单块/单格差异在 ±20 % 内算噪声」现在有同配置重复的直接证据,不再只是经验宽限。
8
+ - **文档口径刷新**:可见正文占「正文+工具参数」字符的比例按最新全量(385 会话 / 3 659 消息 / 4 007 调用)从 1.18 % 更新为 1.19 %。
9
+ - **没有行为变更**:默认值(`outputDiscipline:'late'`、`stepBudget:0`、`workspaceMap:false`、`disciplineRepeatAtSteps:0`、`compactRereadHint:false`、`enabled:true`、`gear:auto`)全部未动;这一版只加量测工具与文档。
10
+
3
11
  ## 1.5.21(更正 1.5.20 的说法,并把「补丁路径是否存在」变成可复算的证据)
4
12
 
5
13
  - 1.5.20 的 CHANGELOG 读起来像「补丁编辑恢复复用证据能提速」。实测不是:本机 2 485 个会话里补丁类调用 0 次,这条修复在本机零收益。代码保留(对其他宿主有防御价值、有测试覆盖),说法改成它实际是防御性修复。
package/README.md CHANGED
@@ -37,7 +37,7 @@
37
37
  一步的用时 = 往返开销 × 步数 + 解码时间 × 输出 token + 工具等待。模型和思考强度不在这个式子里,所以快模式只动三个程序量:
38
38
 
39
39
  - **并行扇出**:调度器先判断这一步是不是“多个互不依赖的读”,是才要宽(最多 8),并且跨轮记住这个形状;写操作、单调用、连续报错或无进展的回合**保持原生** —— 整段批处理提示不渲染,不为它付输入 token。
40
- - **输出纪律**:提示模型不预告调用、不复述结果、不写进度报告,但**不缩短最终答案**。口径要说清:实测里可见助手正文只占「正文+工具参数」字符的 **1.18%**(def15 全量 341 会话 / 3,327 消息 / 3,569 调用,正文 51,366 / 参数 4,315,096 字符;不含 reasoning 与工具返回;`python3 bench/textshare.py --limit 0` 可复算),所以提速不能归因于删旁白——它来自回合数与输出 token 变少(少重复编辑、少回头读、少验证往返),这和「内容不受影响」不是一回事。
40
+ - **输出纪律**:提示模型不预告调用、不复述结果、不写进度报告,但**不缩短最终答案**。口径要说清:实测里可见助手正文只占「正文+工具参数」字符的 **1.19%**(def15 全量 385 会话 / 3,659 消息 / 4,007 调用,正文 57,101 / 参数 4,757,921 字符;不含 reasoning 与工具返回;`python3 bench/textshare.py --limit 0` 可复算),所以提速不能归因于删旁白——它来自回合数与输出 token 变少(少重复编辑、少回头读、少验证往返),这和「内容不受影响」不是一回事。
41
41
  - **卡顿看门狗**:给 shell/脚本类调用设 120–300 秒自适应上限,卡住的调用被取消并把“换做法”写回上下文。
42
42
 
43
43
  没有模型切换、没有 `reasoningEffort` 改写、没有请求字段注入;开着时模型输入里会多出本插件自己的调度提示与提醒(这是机制本身),关掉后连这些也不存在。DeepSeek 官方 API、OpenAI 兼容中转、自建网关、Anthropic 协议 —— 走哪条路由都成立。
@@ -74,7 +74,7 @@ Stall budget: take the p90 of the last 32 tool-call durations, multiply by 3, cl
74
74
 
75
75
  Directives: 6 consecutive no-progress steps emit "change the approach" — it asks for a different method, not for the turn to end. No step-count directive ends a turn by default: `stepBudget` above 0 emits one "report where you are" at its line, and `finishLineSteps` above 0 emits one "stop widening scope" at its line. At least 3 steps pass between directives. Directives are inserted into the message list as producer-owned notices (source `plugin:fast-mode`), so the model reads them and they remain visible in the transcript.
76
76
 
77
- **Reusing a check result (`reuse-check`).** When a call with arguments succeeds, its result is stored under its signature together with the artifact epoch it ran under. If the model then re-runs that same check verbatim and nothing wrote in between, the scheduler stops pestering it and says once: "that check already passed on the current files, rely on its result and finish the remaining requirements." A write, an edit, `apply_patch`/`patch`, or a shell call carrying a redirection or a common mutator (`sed -i`, `git apply`, `npm`, `make`, …) bumps the epoch, and one bump invalidates every stored check at once — so "passed" can never be mistaken for "still passed after the code changed". It is **advice only**: the plugin returns no cached result, skips no call, and never ends the turn; the model may still re-run it.
77
+ **Reusing a check result (`reuse-check`).** When a call with arguments succeeds, its result is stored under its signature together with the artifact epoch it ran under. If the model then re-runs that same check verbatim and nothing wrote in between, the scheduler stops pestering it and says once: "that check already passed on the current files, rely on its result and finish the remaining requirements." A write, an edit, `apply_patch`/`patch`, or a shell call carrying a redirection or a common mutator (`sed -i`, `git apply`, `npm`, `make`, …) bumps the epoch, and one bump invalidates every stored check at once — so "passed" can never be mistaken for "still passed after the code changed". It is **advice only**: the plugin returns no cached result, skips no call, and never ends the turn; the model may still re-run it. Note that in 951 local suite transcripts this reminder was **never delivered** (`python3 bench/reminders.py`): those tasks do not re-run a check verbatim. It is a defensive correctness property, not a measured source of the speedup.
78
78
 
79
79
  On the counting: `turnSteps` is genuinely per turn. `index.js` calls `noteTurn` on the session's `turn/start` event, which clears the per-turn counters while the session-level memory of what has been tried and how long it has stalled survives — that memory is exactly what a stuck loop re-reads. There is no step-count stop line by default; if `stepBudget` is set above 0, that ceiling is **per turn**, not a budget for the whole session.
80
80
 
@@ -90,7 +90,7 @@ Details worth knowing:
90
90
  - **The fan-out number is the width the scheduler computed.** Under `auto` the section and the stall guard share one `autoSchedule` result, so the number the model reads and the number being enforced cannot drift apart.
91
91
  - **Prefetch appears only at width ≥ 4.** Below that, an extra call just competes with the work the step already has.
92
92
  - **The bounded-calls block appears only when `stallGuard !== false`**, because it describes the same policy the guard enforces.
93
- - **Output discipline has two strengths**: `light` for the legacy `fast` gear, `full` for `auto`/`turbo`/`max`/`ludicrous`. Both ask the model to skip call previews, restatements and progress reports; the last step still writes the answer in full. `full` adds one sentence: send bare calls when a step needs no prose. **It never shortens the answer and never tells the model to skip a check.** Scope matters: visible prose is only 1.18 % of the characters in (prose + tool arguments) (def15, 341 sessions; excludes reasoning and tool results; recompute with `python3 bench/textshare.py --limit 0`), so this is not where the speedup mainly comes from — the gain is fewer round trips and fewer output tokens, and it does not license the claim that "the work itself is unaffected".
93
+ - **Output discipline has two strengths**: `light` for the legacy `fast` gear, `full` for `auto`/`turbo`/`max`/`ludicrous`. Both ask the model to skip call previews, restatements and progress reports; the last step still writes the answer in full. `full` adds one sentence: send bare calls when a step needs no prose. **It never shortens the answer and never tells the model to skip a check.** Scope matters: visible prose is only 1.19 % of the characters in (prose + tool arguments) (def15, 385 sessions; excludes reasoning and tool results; recompute with `python3 bench/textshare.py --limit 0`), so its value is not the words it deletes: switching that one item off (`bench/arms/nodisc.yml`) is measured at **1.54× slower** (11 matched pairs, median 1.74×), the largest single lever on this machine, and the mechanism is that it also shortens per-step reasoning (reasoning characters 0.753× inside the transcripts) and cuts steps to 0.83×; it still does not license the claim that "the work itself is unaffected". `python3 bench/block_score.py LEN_DBG def15 nodisc` recomputes it.
94
94
  - **`parallel: 1` is the documented "on but no fan-out" setting**: the switch still applies to the stall guard, and the guidance section disappears entirely.
95
95
 
96
96
  The cost is explicit: about 2,700 characters under `auto`, paid as input tokens on every request. That is the intended trade — input tokens for fewer round trips.
package/docs/REFERENCE.md CHANGED
@@ -78,7 +78,7 @@
78
78
 
79
79
  调度指令:连续无进展达到 `noProgressWarn`(6)步,发一条"换个做法"——它只要求换方法,不要求停下。默认没有任何按步数触发的结束语:`stepBudget` 设成正数时到线才发一次"按现状汇报",`finishLineSteps` 设成正数时到线才发一次"别再扩大范围"。两条指令之间至少隔 3 步。指令以 producer-owned notice 的形式插进消息列表(source 是 `plugin:fast-mode`),模型读得到,会话记录里也看得到。
80
80
 
81
- **检查结果复用(`reuse-check`)。** 一个带参数的调用成功之后,结果会按签名存下来,连同当时的产物版本号(epoch)。如果模型随后把同一条检查原样再跑一遍,而中间没有发生任何写操作,调度器就不重复催它,而是提醒一句:"这个检查在当前文件上已经通过了,可以直接依赖它的结果,把剩下的要求做完。"写、编辑、`apply_patch`/`patch`,以及带重定向或常见改写命令(`sed -i`、`git apply`、`npm`、`make` 等)的 shell 调用都会让 epoch 加一,一次加一就让所有存下来的检查立即失效——"通过"因此永远不会被错当成"改了代码之后还通过"。它只是**建议**:插件不返回缓存结果、不跳过任何调用、也不结束回合,模型仍可选择重跑。
81
+ **检查结果复用(`reuse-check`)。** 一个带参数的调用成功之后,结果会按签名存下来,连同当时的产物版本号(epoch)。如果模型随后把同一条检查原样再跑一遍,而中间没有发生任何写操作,调度器就不重复催它,而是提醒一句:"这个检查在当前文件上已经通过了,可以直接依赖它的结果,把剩下的要求做完。"写、编辑、`apply_patch`/`patch`,以及带重定向或常见改写命令(`sed -i`、`git apply`、`npm`、`make` 等)的 shell 调用都会让 epoch 加一,一次加一就让所有存下来的检查立即失效——"通过"因此永远不会被错当成"改了代码之后还通过"。它只是**建议**:插件不返回缓存结果、不跳过任何调用、也不结束回合,模型仍可选择重跑。 需要说明的是,本地 951 份套件转录里这条建议**一次都没投递过**(`python3 bench/reminders.py`):那些任务的检查不会被逐字重跑。它属于防御性正确的机制,不是已测出的加速来源。
82
82
 
83
83
  计数口径:`turnSteps` 按轮计数。`index.js` 在 `session/event` 的 `turn/start` 上调用 `noteTurn` 清零每轮计数,会话级的"试过什么、卡了多久"记忆则保留——这正是卡住时循环要重读的东西。默认没有按步数的停止线;自己把 `stepBudget` 设成正数时,那条硬上限是**每轮**的,不是整段会话的总预算。
84
84
 
@@ -94,7 +94,7 @@
94
94
  - **扇出目标里的数字就是调度器算出的宽度。** `auto` 下提示段和看门狗共用同一个 `autoSchedule` 结果,所以模型读到的数字和被执行的数字不会漂移。
95
95
  - **预取只在宽度 ≥ 4 时出现。** 宽度不够时多取一个调用只会挤占别的工作。
96
96
  - **有界调用那一段只在 `stallGuard !== false` 时出现**,因为它描述的规则和看门狗是一套东西。
97
- - **输出纪律有两档**:`light` 给旧档位 `fast` 用,`full` 给 `auto`/`turbo`/`max`/`ludicrous` 用。两档要求模型不预告调用、不复述结果、不写进度报告;最后一步的答案仍然要完整写出。`full` 只多一句"不需要文字时只发调用"。**它不会让答案变短,更不会让模型跳过检查。** 口径要说清:可见正文只占「正文+工具参数」字符的 1.18 %(def15 全量 341 会话;不含 reasoning 与工具返回;`python3 bench/textshare.py --limit 0` 可复算),所以这一条不是提速的主要来源——收益来自回合数与输出 token 下降;也不能由此宣称"工作内容不受影响"。
97
+ - **输出纪律有两档**:`light` 给旧档位 `fast` 用,`full` 给 `auto`/`turbo`/`max`/`ludicrous` 用。两档要求模型不预告调用、不复述结果、不写进度报告;最后一步的答案仍然要完整写出。`full` 只多一句"不需要文字时只发调用"。**它不会让答案变短,更不会让模型跳过检查。** 口径要说清:可见正文只占「正文+工具参数」字符的 1.19 %(def15 全量 385 会话;不含 reasoning 与工具返回;`python3 bench/textshare.py --limit 0` 可复算),所以它的价值不在"删掉的字"本身:把这一项单独关掉(`bench/arms/nodisc.yml`)实测慢 **1.54×**(11 对同题同配,中位 1.74×),是本机目前最大的单项杠杆,作用机制是连带把每步的推理压短(转录内 reasoning 字符 0.753×)并把步数压到 0.83×;同样不能由此宣称"工作内容不受影响"。`python3 bench/block_score.py LEN_DBG def15 nodisc` 可复算。
98
98
  - **`parallel: 1` 是"开着但不要求扇出"的正式写法**:开关仍然生效于看门狗,提示段整体消失。
99
99
 
100
100
  成本是明码标价的:`auto` 下这一段约 2.7 千字符,每个请求都要付这部分输入 token。这是有意的交换——用输入 token 换更少的往返。
package/index.js CHANGED
@@ -97,7 +97,7 @@ const TAG = '[fast-mode]'
97
97
  * Shipped identity of this build. Traced into the debug log so a stale install is
98
98
  * visible at a glance, and pinned by a test against `package.json`.
99
99
  */
100
- export const VERSION = '1.5.21'
100
+ export const VERSION = '1.5.22'
101
101
  const GLOBAL_AGENT = '__global__'
102
102
 
103
103
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-fast-mode",
3
- "version": "1.5.21",
3
+ "version": "1.5.22",
4
4
  "description": "DeepSeek Harness fast mode: same thinking effort, fewer round trips. An adaptive parallel tool fan-out, output discipline that never trims the answer, and a cancellable stall guard. Never changes reasoning effort, the model, or the request body. Provider-agnostic, one dependency (@deepseek-ai/schemastery).",
5
5
  "repository": {
6
6
  "type": "git",