dsh-fast-mode 1.5.17 → 1.5.19

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
@@ -63,9 +63,10 @@ W1 出现过唯一一格门禁违规:`ledger` p1 的 `def15` 红在 `test_roun
63
63
 
64
64
  口径与判据:
65
65
 
66
- - 有效格(bench 判据,不是 `green`):`exit == 0 && steps >= 1 && out > 0 && exact && !leak`。`green` 只说明隐藏断言过了,崩溃到没产出的会话也可能 green,所以不能当计量单位。
66
+ - 有效格(bench 判据,不是 `green`):`exit == 0 && steps >= 1 && out > 0 && hasSessionIds && !leak`。`green` 只说明隐藏断言过了,崩溃到没产出的会话也可能 green,所以不能当计量单位。
67
67
  - 配对:同一 `(tag, task, pair)` 内 `off` 与该臂都有效才算一对;比值一律「该臂 / 同块 `off`」,跨块不混算。
68
- - 排除:`V15`/`TA3`/`TA4` 三块已因 fixture 泄漏 + byte-window 指标混用撤回,且缺 `exact` 字段,严格口径下 0 条有效。
68
+ - 排除:`V15`/`TA3`/`TA4` 三块已因 fixture 泄漏 + byte-window 指标混用撤回,且缺 `hasSessionIds` 字段,严格口径下 0 条有效。
69
+ - 字段更名:`bench/suite-run.py` 现在写 `hasSessionIds`(该行的 steps/out 是否归属本会话自己的 session id)。历史 result 行里同一个布尔叫 `exact`,所有读取点走 `row.get('hasSessionIds', row.get('exact'))`,旧文件不需要转换;`exact` 从来不是「产物精确性」。
69
70
  - 复算:`python3 bench/pool.py`(只读 `bench/results/*.jsonl`,不跑 bench、不写文件)。
70
71
 
71
72
  | 块 | 对数 | 墙钟总量 | 更快 |
@@ -1458,7 +1459,7 @@ node bin/experiment.mjs stop # end it; later sessions g
1458
1459
  - 原始结果 `bench/results/suite-REL16.jsonl`;`python3 bench/report.py --files bench/results/suite-REL16.jsonl --baseline off` 可复算。
1459
1460
  ## 合池的自助区间与逐任务稳定性(2026-10-28,12 块 138 对)
1460
1461
 
1461
- 合池点估计 **0.8234×** 是 138 对有效单元的比值加总,不是承诺值。给这 138 对做 4000 次自助重抽(`python3 bench/pool.py --ci 4000`):**墙钟总比 95 % 区间 [0.7418, 0.9133]**、逐格中位 0.7994×(区间 [0.7026, 0.8850])、几何均值 0.8248×(区间 [0.7532, 0.9001])、93/138 对更快(符号检验 p = 0.0001);同池输出比 **0.8179×**、步数比 **0.8062×**。`bench/results/suite-*.jsonl` 里每行都带 validity 判定,合池只吃 `exit==0 && steps>=1 && out>0 && exact && !leak` 的单元(`bench/pool.py` 的 `valid()`)。
1462
+ 合池点估计 **0.8234×** 是 138 对有效单元的比值加总,不是承诺值。给这 138 对做 4000 次自助重抽(`python3 bench/pool.py --ci 4000`):**墙钟总比 95 % 区间 [0.7418, 0.9133]**、逐格中位 0.7994×(区间 [0.7026, 0.8850])、几何均值 0.8248×(区间 [0.7532, 0.9001])、93/138 对更快(符号检验 p = 0.0001);同池输出比 **0.8179×**、步数比 **0.8062×**。`bench/results/suite-*.jsonl` 里每行都带 validity 判定,合池只吃 `exit==0 && steps>=1 && out>0 && hasSessionIds && !leak` 的单元(历史行里 `hasSessionIds` 写作 `exact`,读取时回退)(`bench/pool.py` 的 `valid()`)。
1462
1463
 
1463
1464
  12 个块全部有 `off` 锚、全部在现行 validity 规则下测出:`W1`、`W3`、`W5`、`W6`、`W7`、`W8b`、`REL16`、`PPX2`、`SG2`、`NX1_IMPL`、`NX1_PIPE`、`NX1_SG`。**块间不可比**:每块只做自己块内 `(tag, task, pair)` 的 `def15 / off` 配对,跨块加总走「先算每对的比值、再对 138 个比值取中位/几何/加总比」。`V15`、`TA3`、`TA4` 三个块按名字剔除(fixtures 泄漏,`llm` 列是字节窗口的产物)。
1464
1465
 
@@ -1512,6 +1513,17 @@ node bin/experiment.mjs stop # end it; later sessions g
1512
1513
 
1513
1514
  口径限制:这些数字来自**还留在磁盘上的 run 目录抽样**,不是 138 对全池的成分统计;结论只用来说方向(正文 ≪ 参数、单调用步占多数),不要当成全池精确占比引用。
1514
1515
 
1516
+ ### 复核:1.2 % 的正文占比现在有命令了(2026-10-28)
1517
+
1518
+ 上面那 40 个会话的数字(6,267 / 518,773 = 1.19 %)没有留下抽样命令,无法复现。用 `bench/textshare.py` 重算,口径固定、可重跑:
1519
+
1520
+ - 计数单位:会话 transcript 里每条 `assistant/message`。分子 = `text` part 的 `len(text)` 之和;分母 = 分子 + `tool-call` part 的 `len(arguments)` 之和。不含 reasoning、不含工具返回、不含用户消息。
1521
+ - 抽样规则:`~/.dsh/sessions` 下 run 目录名匹配 `suite-<tag>-<task>-p<pair>-<arm>` 的会话,按目录名排序取前 40 个;`--limit 0` 取全量。同样的库必得同样的数。
1522
+ - `python3 bench/textshare.py`:n=38 会话 / 368 条消息 / 408 次调用,正文 6,555 字符、工具参数 456,476 字符 ⇒ **1.42 %**(逐会话中位 1.43 %、p10 0.07 %、p90 2.80 %)。
1523
+ - `python3 bench/textshare.py --limit 0`:全量 `def15`,n=341 / 3,327 条消息 / 3,569 次调用,正文 51,366、参数 4,315,096 ⇒ **1.18 %**(中位 0.98 %、p90 4.08 %)。
1524
+
1525
+ 全量 1.18 % 与文首的 1.2 % 一致,结论(正文 ≪ 工具参数)在两套样本上都成立。后续引用建议用 `--limit 0` 的 1.18 % 并写明 n;两套样本都不含 reasoning 与工具返回,不是「输出 token 的全部」。
1526
+
1515
1527
  ## `batchVerify` 复测块 BV8(2026-10-28):codeimpl 1.478× 变慢、shellpipeline 0.956× 无差,合并 1.188× —— **淘汰**
1516
1528
 
1517
1529
  `batchVerify` 是最后一个「修好 liveness 缺陷后再测一次」的默认关闭机制。W4 曾筛出 0.76×,但 W8b 的逐会话检索证明那一臂**一条提醒都没下发**(20 个会话命中 0),所以 0.76× 不是它的效果;W5 同样零下发。修复投递后第一次真测就是这两块。
@@ -1599,7 +1611,7 @@ python3 bench/suite-run.py --tasks debugfix,ledger,codeimpl --arms off,def15,bud
1599
1611
 
1600
1612
  判据(全部满足才并入默认,否则记为负结果并保持默认关):
1601
1613
 
1602
- 1. **质量门禁**:`BUD3` 块内 `bud8` 每个单元 `exit==0`、`exact`、无 `leak`;`off` 与 `def15` 在同块同样全绿(分母按有效单元算,不按格数算)。
1614
+ 1. **质量门禁**:`BUD3` 块内 `bud8` 每个单元 `exit==0`、`hasSessionIds`、无 `leak`;`off` 与 `def15` 在同块同样全绿(分母按有效单元算,不按格数算)。
1603
1615
  2. **输出真的降了**:`bud8/def15` 的同块输出 token 总和比 ≤ 0.90;若 ≥ 0.97 判定「机制未生效」,不许用墙钟数字解释。
1604
1616
  3. **墙钟不倒退**:`bud8/def15` 同块墙钟总和比 ≤ 1.00,逐对中位数 ≤ 1.03;三个家族里至少两个家族单独 ≤ 1.00。
1605
1617
  4. **每家族 ≥ 2 个同块有效对**,否则该家族写「未判定」,不并入总体结论。
@@ -1656,7 +1668,7 @@ python3 bench/suite-run.py --tasks debugfix,ledger,codeimpl --arms off,def15,bud
1656
1668
 
1657
1669
  - 断言通过数:`off` **2038/2038(100.00 %)**,`def15` **2037/2038(99.951 %)**,差 **1 条断言**。
1658
1670
  - 逐对看:137/138 对两臂断言数完全相同;唯一不同的还是 `W1 / ledger / pair 1`(`def15` 21/22,`off` 22/22),即 `RX` 那格题面欠明确的历史样本,不是新出现的回归。
1659
- - 产物精确性 `exact`:两臂都是 138/138。
1671
+ - 会话归属 `hasSessionIds`:两臂都是 138/138。(这不是「产物精确性」:该字段只说 steps/out 归属本会话;产物质量看断言计数与 `green`。)
1660
1672
  - 结论只能说到这里:**在这 10 个家族、138 个任务上,速度不是用正确率换来的**;1 条断言的分差在统计上无法与噪声区分(也无法证明「绝对无损」),所以对外表述用「断言对等,1/2038 差异」而不是「零质量损失」。
1661
1673
 
1662
1674
  ## 质量对等(历史口径):132 对里断言 1959/1960 对 1960/1960
@@ -1665,7 +1677,7 @@ python3 bench/suite-run.py --tasks debugfix,ledger,codeimpl --arms off,def15,bud
1665
1677
 
1666
1678
  - 断言通过数:`off` **1960/1960(100.00 %)**,`def15` **1959/1960(99.95 %)**,差 **−0.05 个百分点 = 1 条断言**。
1667
1679
  - 逐对看:131/132 对两臂断言数完全相同;唯一不同的是 `W1 / ledger / pair 1`,`def15` 21/22、`off` 22/22。
1668
- - 产物精确性 `exact`(入口文件 / 输出与期望逐字节一致):两臂都是 132/132。
1680
+ - 会话归属 `hasSessionIds`(旧名 `hasSessionIds`):两臂都是 132/132。(入口文件与期望的逐字节比较是另一件事,由断言计数 `passed/total` 与 `green` 覆盖。)
1669
1681
  - 结论只能说到这里:**在这 10 个家族、132 个任务上,速度不是用正确率换来的**;1 条断言的分差在统计上无法与噪声区分(也无法证明「绝对无损」),所以对外表述用「断言对等,1/1960 差异」而不是「零质量损失」。
1670
1682
 
1671
1683
  另外记一条运行可靠性的事实,避免以后误读:同批数据里有 **`off` 6 行、`def15` 3 行**是零输出的退化运行(`out=0`、`steps` 为 1、任务只做了一半),按有效单元规则已经全部排除,不参与任何比值。这类行的出现与宿主负载相关,不是某一个臂的固有缺陷;它们也解释了为什么 `suite-run.py` 必须按「有效行」而不是「有行」来判断进度(见 `bench/scrub.py`)。
@@ -1728,7 +1740,7 @@ python3 bench/suite-run.py --tasks debugfix,ledger,codeimpl --arms off,def15,bud
1728
1740
  三点读法:
1729
1741
 
1730
1742
  1. **点估计没动**(0.8247 → 0.8234,差 0.13 个百分点,远小于区间宽度 0.18),所以对外仍是「约 1.21× 快、约 17–18 % 省时」,区间照旧报 CI95,不报单点。新增的 6 对确实更偏快(中位从 0.813 掉到 0.799),但样本量只涨 4.5 %,不足以支撑新报价。
1731
- 2. **唯一的断言差异还是那一个**:`(W1, ledger, 1)` 的 `def15` 21/22 对 `off` 22/22;其余 137 对断言计数完全一致,`exact` 138/138。质量措辞仍是「断言对等」,不是「零质量损失」。
1743
+ 2. **唯一的断言差异还是那一个**:`(W1, ledger, 1)` 的 `def15` 21/22 对 `off` 22/22;其余 137 对断言计数完全一致,`hasSessionIds` 138/138。质量措辞仍是「断言对等」,不是「零质量损失」。
1732
1744
  3. 三个估计量互相不一致的程度(0.799 / 0.823 / 0.825)正好说明为什么发布口径必须并列报,而不能挑最小的那个当中位卖点。
1733
1745
 
1734
1746
  ## 空转对照:开关根本没触发时,同一对能差多少(9 对,纯分析,不新增格子)
@@ -1865,7 +1877,7 @@ python3 bench/suite-run.py --tasks debugfix,ledger,codeimpl --arms off,def15,bud
1865
1877
  | `D2_MIG` | migrate | 6 | **0.751×** | 0.738× | 0.713× | 4/6 | 0.887× | 0.700× | 0.229–1.724 |
1866
1878
  | 合并 | 2 家族 | 13 | **0.625×** | 0.551× | 0.680× | 9/13 | 0.857× | 0.614× | 0.229–1.724 |
1867
1879
 
1868
- 墙钟合计 1963 s → 1335 s。26 行全 `exact`、全 `green`,逐格断言 `debugfix` 13/13、`migrate` 14/14 满,**没有一格质量下降**。
1880
+ 墙钟合计 1963 s → 1335 s。26 行全 `hasSessionIds`、全 `green`,逐格断言 `debugfix` 13/13、`migrate` 14/14 满,**没有一格质量下降**。
1869
1881
 
1870
1882
  **机制:不是「正文写短了」。** 同一批 transcript 里,`debugfix` 每会话正文总量 `def15` 109 字符、`d2` 54 字符(本身就近乎为零),`migrate` 118 → 53 —— 正文差几十字节,无法解释墙钟 0.535×。真正的差别在**往返次数**:
1871
1883
 
@@ -1888,11 +1900,11 @@ python3 bench/suite-run.py --tasks debugfix,ledger,codeimpl --arms off,def15,bud
1888
1900
 
1889
1901
  **预注册:把默认从 4 改成 2 的通过判据(先写判据,`D2_IMPL` 数据还没出来)。** 只认 `codeimpl`(4–13 步,中等长度,是已证明受益的家族)8 对:
1890
1902
 
1891
- 1. **8 对全有效**(`exit == 0 and steps >= 1 and out > 0 and exact and not leak`),且每格 `green`。
1903
+ 1. **8 对全有效**(`exit == 0 and steps >= 1 and out > 0 and hasSessionIds and not leak`),且每格 `green`。
1892
1904
  2. **几何 ≤ 0.95×、中位 ≤ 1.00×、赢面 ≥ 5/8**:三家族(debugfix 0.535×、migrate 0.751×、codeimpl)不得出现方向翻转。`codeimpl` 若 > 1.00×,说明收益只属于「回合短」的家族,默认**不动**(维持 4),这条线判定为家族相关。
1893
1905
  3. **三家族合并(21 对)几何 ≤ 0.85×、中位 ≤ 0.90×**。
1894
1906
  4. **下发必须命中**:`d2` 臂 ≥ 7/8 会话在第 2 步收到(`codeimpl` 有 0 个会话不到 2 步才行;目前 `codeimpl` 16 个 `def15` 会话步数最少 4,够)。
1895
- 5. 质量:任何一格 `exact`/`green` 倒退 ⇒ 不通过(速度不能以质量为代价)。
1907
+ 5. 质量:任何一格 `hasSessionIds`/`green` 倒退 ⇒ 不通过(速度不能以质量为代价)。
1896
1908
 
1897
1909
  全部满足才改默认并发版;否则保持 4,并把「时点」这条线记为家族相关、不再扫。**不因为「几何好看」就改默认**:`odt15` 当年就是被单个家族的好看数字骗过一次。
1898
1910
 
@@ -1904,7 +1916,7 @@ python3 bench/suite-run.py --tasks debugfix,ledger,codeimpl --arms off,def15,bud
1904
1916
 
1905
1917
  **这条杠杆的「成本侧」是严格受控的(离线读 transcript)。** 提醒文本在 `def15` 与 `d2` 两臂是同一句、同样长度:三个块 42 个会话**每回合恰好注入 1 次**,每次 **318 字符**,两臂总字符数完全相同(`D2_DBG` 各 2226、`D2_IMPL` 各 2544、`D2_LED` 各 1908);唯一差别是注入步:`d2` 一律第 2 步、`def15` 一律第 4 步(`at_steps` 逐条核对)。所以家族间那 0.5×/1.07× 的差别**全部来自时点**,不是「多给提示」换来的——这也是为什么它能保持质量零倒退。
1906
1918
 
1907
- **第三族 `D2_LED` 也跑满了(6 对 12 行,发布前就登记在 d2 的块表里):这一族是**大幅受益**。** 几何 **0.506×**、中位 0.551×、合计 0.436×、5/6 更快、符号 p = 0.2188,范围 0.157–1.232×;步数几何 0.699×、输出几何 0.540×。两臂 12 行**全绿且 22/22 全过**(`exact` + `green` + `valid` 全为真),所以提速不是靠少检查换来的:逐对步数 `15→11`、`13→6`、`16→25`、`6→8`、`63→13`、`10→8`——注意 p3 提前后**多**走了 9 步却仍快 0.616×,p5 把 63 步压到 13 步(1009 s → 158 s)。这样方向表就完整了:`ledger` 0.506×、`debugfix` 0.535×、`migrate` 0.751× 三族受益,`codeimpl` 1.073× 受害;受害的只有「短回合、单文件、第一步还在定位」这一类。
1919
+ **第三族 `D2_LED` 也跑满了(6 对 12 行,发布前就登记在 d2 的块表里):这一族是**大幅受益**。** 几何 **0.506×**、中位 0.551×、合计 0.436×、5/6 更快、符号 p = 0.2188,范围 0.157–1.232×;步数几何 0.699×、输出几何 0.540×。两臂 12 行**全绿且 22/22 全过**(`hasSessionIds` + `green` + `valid` 全为真),所以提速不是靠少检查换来的:逐对步数 `15→11`、`13→6`、`16→25`、`6→8`、`63→13`、`10→8`——注意 p3 提前后**多**走了 9 步却仍快 0.616×,p5 把 63 步压到 13 步(1009 s → 158 s)。这样方向表就完整了:`ledger` 0.506×、`debugfix` 0.535×、`migrate` 0.751× 三族受益,`codeimpl` 1.073× 受害;受害的只有「短回合、单文件、第一步还在定位」这一类。
1908
1920
 
1909
1921
  原因不是「提醒这句没用」,而是**同一个时点在两族里方向相反**:`debugfix` 0.535×、`migrate` 0.751×,`codeimpl` 1.073×。逐格看更清楚——`codeimpl` p4 的 `d2` 走 17 步(`def15` 8 步)、p3 1.980×、p4 2.688×,而唯一赢的 p1 是 `def15` 自己走到 5 步的那格。把 p4 的调用序列摊开:`def15` 5 步 4 次调用(`write` → `python3 - <<PY` 校验 → `ls && py_compile` → `rm -rf`),`d2` 8 步 7 次(`pwd && ls` → `write` → python 校验 **×2** → `python3 --version && python` 再校验 → `ls && find` → `rm -rf`):在本来就最短的直线上,第 2 步的提醒**加**了探索和重复校验。所以这条旋钮的准确描述是「**把会走远的回合作短,代价是给本来就短的回合加了开销**」——家族相关的时点杠杆,默认维持 4。
1910
1922
 
@@ -1944,11 +1956,11 @@ python3 bench/suite-run.py --tasks debugfix,ledger,codeimpl --arms off,def15,bud
1944
1956
 
1945
1957
  **判据(配对,treatment / baseline < 1 才是快):**
1946
1958
 
1947
- 1. **至少 6 对有效配对**(`migrate`,同一块 `DR12_MIG`,两臂交错)。有效 = `exit == 0 and steps >= 1 and out > 0 and exact and not leak`。
1959
+ 1. **至少 6 对有效配对**(`migrate`,同一块 `DR12_MIG`,两臂交错)。有效 = `exit == 0 and steps >= 1 and out > 0 and hasSessionIds and not leak`。
1948
1960
  2. **几何均值 ≤ 0.95×,且中位 ≤ 1.00×**。只报几何/中位/合计比,算术均值禁用(重尾)。
1949
1961
  3. **赢面至少 4/6**,否则即使几何更好也记为「不稳定」。
1950
1962
  4. **输出 token 的几何均值 ≤ 0.95×** 才允许说「赢在输出变短」;如果墙钟变好而输出没变短,那就是采样噪声,判空。
1951
- 5. **质量不得倒退**:`exact` 全对,`green` 不少于对侧臂;任何一格质量下降 ⇒ 直接不通过(速度不能以质量为代价)。
1963
+ 5. **质量不得倒退**:`hasSessionIds` 全对,`green` 不少于对侧臂;任何一格质量下降 ⇒ 直接不通过(速度不能以质量为代价)。
1952
1964
  6. 不通过就维持默认 `0`,并在文档里写「第二次提醒无效/有害」,不再重试第三次。
1953
1965
 
1954
1966
  **已知上限。** 单块 n=6 的检出下限约 ±15 %(见「空转对照」一节:同臂对同臂 9 对的范围是 0.889–1.342×)。所以这里只认「几何 ≤ 0.95× 且赢面 4/6 以上」这种**远大于噪声**的结果;0.96–1.04× 一律判空。
@@ -1967,8 +1979,8 @@ python3 bench/suite-run.py --tasks debugfix,ledger,codeimpl --arms off,def15,bud
1967
1979
 
1968
1980
  **两处与预注册的偏差,记在这里不掩盖:**
1969
1981
 
1970
- 1. **判据 5 的「下发条件」原本没被脚本执行,现已补上并判 FAIL。** 预注册写的是「`D2G_IMPL` 8/8 仍在**第 4 步**收到,`D2G_DBG`/`D2G_MIG` 在第 2 步收到」。实际逐会话扫描:`def15` 8/8/6/7 全在第 4 步,`d2g` 在 `IMPL` 是**第 3 步**、在 `DBG`/`MIG` 是第 2 步。`d2gscore.py` 现在自己调 `d2gsign.py` 断言每个臂的命步(`5 delivery <block> <arm> step N`),输出里 `D2G_IMPL d2g step 4` 一行是 **FAIL**(实际 `[3]`)。`IMPL` 按设计应在 `disciplineEarlyCalls: 2` 下退化成第 4 步,实际提前到了第 3 步 —— 这解释了它为什么没像预期那样完全退化(0.889× 而不是 ≈1.00×),也说明判据 5 的「按条件命中」这一半同样没通过。原先 gate 5 只校验 `exact`/`green`,缺口已修(见上);判据 5 因此是「质量过、下发步位不过」,结论仍是 HOLD。
1971
- 2. **`delivery audit` 探针不可用。** 报告按固定句式找完成通报,命中 0/16、2/14、0/12。这个探针在本套 fixture 上恒为 0,不能当「任务没交付」的证据;质量结论只引用 `exact`/`green`(21 对全部 1/1)与判据 5 的 0 坏格。
1982
+ 1. **判据 5 的「下发条件」原本没被脚本执行,现已补上并判 FAIL。** 预注册写的是「`D2G_IMPL` 8/8 仍在**第 4 步**收到,`D2G_DBG`/`D2G_MIG` 在第 2 步收到」。实际逐会话扫描:`def15` 8/8/6/7 全在第 4 步,`d2g` 在 `IMPL` 是**第 3 步**、在 `DBG`/`MIG` 是第 2 步。`bench/d2gscore.py` 现在自己调 `bench/d2gsign.py` 断言每个臂的命步(这两个脚本原先只在 `~/.local/fm/`,1.5.19 起随 `bench/` 一起进仓,`python3 bench/d2gscore.py` 可复现本节全部数字)(`5 delivery <block> <arm> step N`),输出里 `D2G_IMPL d2g step 4` 一行是 **FAIL**(实际 `[3]`)。`IMPL` 按设计应在 `disciplineEarlyCalls: 2` 下退化成第 4 步,实际提前到了第 3 步 —— 这解释了它为什么没像预期那样完全退化(0.889× 而不是 ≈1.00×),也说明判据 5 的「按条件命中」这一半同样没通过。原先 gate 5 只校验 `hasSessionIds`/`green`,缺口已修(见上);判据 5 因此是「质量过、下发步位不过」,结论仍是 HOLD。
1983
+ 2. **`delivery audit` 探针不可用。** 报告按固定句式找完成通报,命中 0/16、2/14、0/12。这个探针在本套 fixture 上恒为 0,不能当「任务没交付」的证据;质量结论只引用 `hasSessionIds`/`green`(21 对全部 1/1)与判据 5 的 0 坏格。
1972
1984
 
1973
1985
  ### 预注册前提检查:提醒之后输出会不会「重新变长」
1974
1986
 
@@ -1989,7 +2001,7 @@ python3 bench/suite-run.py --tasks debugfix,ledger,codeimpl --arms off,def15,bud
1989
2001
 
1990
2002
  | 指标 | 数值 |
1991
2003
  |---|---|
1992
- | 对数 | 7 / 7 有效,`exact`+`green` 21/21 格全过 |
2004
+ | 对数 | 7 / 7 有效,`hasSessionIds`+`green` 21/21 格全过 |
1993
2005
  | 几何均值 | **0.717×** |
1994
2006
  | 中位 | 0.864× |
1995
2007
  | 总计 | 0.633× |
@@ -1999,3 +2011,96 @@ python3 bench/suite-run.py --tasks debugfix,ledger,codeimpl --arms off,def15,bud
1999
2011
  | 提醒步位 | `def15` 7/7 在第 4 步,`d2` 7/7 在第 2 步 |
2000
2012
 
2001
2013
  怎么读:方向是复现的(输出变短、整体更快),但**幅度掉到 0.717×,中位只剩 0.864×,7 对里 5 对更快在统计上说明不了问题**;同时 `steps_geo` 1.118× 说明早提醒在这套 debugfix 上会让回合多走一两步——加速来自「每步写得更短」,不是「步数更少」,这和 `D2G_MIG` 的分解(`out` 0.478、`steps` 0.964)是同一套机制。合并原来的 `D2_DBG`(0.535×)与这块(0.717×),`d2` 在 debugfix 上的真实档位大约 **0.6–0.7×,且方差很大(单对 0.236–1.327×)**,所以任何「早提醒稳定快 2 倍」的说法都不成立。
2014
+
2015
+ ## 结果:`D2B_MIG`(`d2` 在 migrate 上的新会话复现,3 对可用)
2016
+
2017
+ `D2` 在迁移类上的老证据只有 `D2_MIG` 的 6 对(几何约 0.75×)。同协议开一块新会话,6 对,ABBA 交错:
2018
+
2019
+ | 指标 | 数值 |
2020
+ |---|---|
2021
+ | 可用对数 | **3 / 6**(`migrate p4`–`p6` 的 12 行全是 1 步、0 字符输出、墙钟约 2 s 的空转,探测请求打空,整对作废,不参与平均) |
2022
+ | 几何均值 | **0.326×** |
2023
+ | 中位 | 0.322× |
2024
+ | 更快 | 3 / 3 |
2025
+ | 步骤 | `steps_geo` **0.575×**(18→7、11→10、13→7) |
2026
+ | 输出 | `out_geo` **0.373×**(30470→9655、15107→12176、21858→4451) |
2027
+
2028
+ 逐对:`p1` 393.3→126.7 s、`p2` 197.4→165.4 s、`p3` 355.3→45.5 s。`p1` 的基线 393 s 本身是这台机器当时负载下的长尾(同一块里 `def15` 其余行 117–280 s),所以**不把 0.326× 当成档位**,只记两点:方向在 migrate 上第三次复现(`D2_MIG` 0.75×、`D2G_MIG` 0.495×、本块 0.326×),且省下来的同样是步数与输出(长迁移里反复回看目录的调用被砍掉)。样本 3 对,符号检验没有意义,不进任何合并口径。
2029
+
2030
+ ## 结果:`DR8_MIG`(`disciplineRepeatAtSteps: 8`,6 对)——重复提醒更慢,杠杆关闭
2031
+
2032
+ 预注册的动机是「第 4 步提醒之后输出会反弹,所以第 8 步再说一遍」。`migrate` 上 6 对完整跑完:
2033
+
2034
+ | 指标 | 数值 |
2035
+ |---|---|
2036
+ | 对数 | 6 / 6 有效(12 格 `hasSessionIds`+`green` 全过;中途因 load ≥ 14 触发一次 ABORT,scrub 后补齐) |
2037
+ | 几何均值 | **1.394×**(更慢) |
2038
+ | 中位 | 1.451× |
2039
+ | 更快 | 2 / 6(符号检验 p = 0.688,不显著) |
2040
+ | 步骤 | `steps_geo` **1.091×** |
2041
+ | 输出 | `out_geo` **1.365×**(提醒没压住,反而更长) |
2042
+
2043
+ 逐对墙钟比:0.918 / 1.315 / 0.968 / 1.353 / 1.311 / 2.241。最差的一对 `p6`:`def15` 6 步 153 s / 13453 字符,`dr8` 15 步 343 s / 27414 字符——第 8 步那句提醒在这条长迁移上没有让模型收手,它把已完成的检查又做了一遍。
2044
+
2045
+ 结论:**`disciplineRepeatAtSteps` 维持默认 `0`,这条杠杆关闭。** 预注册前提里「反弹存在」那半句是对的(`D2G` 的逐步骤输出确实反弹),但「再提醒一次能把反弹压回去并换来墙钟」被本块否掉;提醒本身不免费,每次注入都要占一段 prompt 与一次决策。
2046
+
2047
+ ## 预注册:R1(压缩提醒里加一句「别重读本回合写过的文件」)
2048
+
2049
+ 动机来自迁移族的长尾结构,不是新猜想:`D2B_MIG`、`DR8_MIG` 的逐对记录里,最慢的几对都是同一个模式——模型写完一处,随后把整份文件重新读回来确认,再进入下一次编辑;`DR8_MIG` 的最差对 `p6` 甚至把已完成的检查整段重做。读回来的内容要占工具参数与回显的输出 token,而输出 token 全部是解码时间。
2050
+
2051
+ 实现取最小侵入:不新增消息、不改调度点,只在已有第 4 步压缩提醒的文本里追加一句「本回合已经写过的文件,按最后一次编辑和检查输出来判断,不要为了确认整份重读」。开关 `compactRereadHint` 默认 `false`,1.5.17 行为逐字不变。
2052
+
2053
+ 臂:`bench/arms/r1.yml` = `outputDiscipline: late` + `compactRereadHint: true`,其余与 `def15` 同。基线 `def15`。
2054
+
2055
+ 预注册判据(先写判据,再跑数据):
2056
+
2057
+ - **R1_MIG**(migrate,6 对):几何均值墙钟比 ≤ **0.85** 且至少 **4/6** 对更快 ⇒ 判「有效」。0.85–1.00 视为「方向对但不够」,> 1.00 视为「这句提醒是噪声」,两种情况杠杆关闭。
2058
+ - **R1_DBG**(debugfix,7 对):只作族间一致性参考,不作判据;若 migrate 通过而 debugfix 明显变慢(> 1.05)也要重估,因为提醒是按步数入场、与任务族无关。
2059
+ - 质量门槛与既有块相同:每对必须 `hasSessionIds` 且 `green`,否则该对不计入(照旧报 n 与丢弃对)。
2060
+
2061
+ ## 预注册:DEF(默认配置 vs 完全关闭,现代任务集上的干净对照)
2062
+
2063
+ `README` 一直写「默认就更快」,但这个对照在**当前任务集**上还没有一块干净的测量:带 `off` 臂的块要么是旧行格式(`V15`/`W3` 等),要么只有零星单对(`REL16` 5 对、`DBG2` 7 对,几何 0.889 / 0.958,都不是任务族内的成对结论)。所以先把判据写下来,再测。
2064
+
2065
+ 臂:`def15`(`enabled: true, gear: auto` + 1.5.17 默认)对 `off`(`enabled: false`,插件完全退场)。任务族取长尾最明显的两族:migrate(6 对)与 debugfix(7 对)。
2066
+
2067
+ 预注册判据:
2068
+
2069
+ - **DEF_MIG**:几何均值墙钟比 ≤ **0.85** 且至少 **4/6** 对更快 ⇒「默认在长迁移上更快」成立,可继续写在 `README`。0.85–1.00 ⇒ 只能写「不劣于原生」,不得写具体倍数。> 1.00 ⇒「默认更快」这句话必须从 `README` 撤回。
2070
+ - **DEF_DBG**:同一门槛,族内独立判断;两族都过才算「默认普适更快」,只有一族过就按族写。
2071
+ - 质量门槛照旧:`hasSessionIds` 且 `green` 才算一对;报 n 与丢弃对。这一块的墙钟是唯一可以横向引用绝对值的对照(`off` 臂是同一台机器同一时段的原生基线)。
2072
+
2073
+ ## 预注册:D3(压缩提醒放到第 3 步)——默认时点能不能再往前挪一格
2074
+
2075
+ 现有事实:`disciplineAfterSteps: 2`(`d2`)在 migrate 上 0.495–0.751×,但在 debugfix 与 codeimpl 上掉到 0.903–1.073×,所以默认停在 4;第 4 步(`def15`)是当前默认。第 3 步是两者中间唯一还没测过的位置——如果它保留迁移式的收益而把 `d2` 在 debugfix/codeimpl 上的损失收回来,默认时点就有值得改的方向;如果它两头都不占,默认继续停在 4,这条线彻底关闭。
2076
+
2077
+ 臂:`d3.yml` = `outputDiscipline: late` + `disciplineAfterSteps: 3`,其余同 `def15`。
2078
+
2079
+ 预注册判据(先写判据,再跑数据):
2080
+
2081
+ - **`D3_MIG`**(6 对):几何 ≤ **0.85** 且 ≥ 4/6 更快(与 `d2` 同门槛)。
2082
+ - **`D3_DBG`**(7 对):几何 ≤ **0.90** 且 ≥ 5/7 更快(比 `d2g` 那条 0.80 松一档,因为这次不是要它「更快」,只是要它「不掉队」)。
2083
+ - **改默认只用同时满足两条**:`MIG` 过且 `DBG` 过,且 `DBG` 不比 `def15` 差到 1.00 以上;否则默认继续是 4。任何一条不过,本块都只作为「第 3 步的代价」记录,不改默认。
2084
+ - 质量门槛照旧:`hasSessionIds` 且 `green` 才算一对;报 n 与丢弃对。
2085
+
2086
+ ## 结果:`D3_MIG` 第 3 步提醒在 migrate 上 **0.509×(5 对可用)**,过 MIG 门槛;但出现一次跑到 50 步、测试没跑起来的无效跑
2087
+
2088
+ **数字(`def15` vs `d3`,migrate 6 对)。** 5 对可用(第 6 对 `d3` 被判 `valid=false`,见下):几何 **0.509×**、中位 0.636×、4/5 更快、范围 0.151–1.134×;步数几何 0.622×、输出几何 0.528×。逐对:
2089
+
2090
+ | 对 | `def15` | `d3` | 比值 | 步数 | 输出 token |
2091
+ |---|---|---|---|---|---|
2092
+ | p1 | 275.6 s | 96.7 s | **0.351×** | 14→11 | 25 480→10 638 |
2093
+ | p2 | 151.8 s | 96.5 s | 0.636× | 12→7 | 19 488→11 857 |
2094
+ | p3 | 121.2 s | 137.5 s | 1.134× | 9→10 | 13 602→10 916 |
2095
+ | p4 | 620.6 s | 93.7 s | **0.151×** | 36→11 | 40 815→9 277 |
2096
+ | p5 | 199.1 s | 177.9 s | 0.893× | 20→12 | 23 771→21 142 |
2097
+ | p6 | 210.0 s | 665.3 s(无效) | — | 11→**50** | 18 755→40 979 |
2098
+
2099
+ **判定。** 预注册 `D3_MIG` 要求几何 ≤ 0.85 且 ≥ 4/6 更快:可用 5 对几何 0.509×、4/5 更快,两条都过(若把 p6 计进去是 0.690×、4/6,也过)。所以 MIG 这一半 **PASS**,是否改默认还要等 `D3_DBG`(≤ 0.85 且 5/7)与 `D3_IMPL`。
2100
+
2101
+ **两处必须写清的偏差。**
2102
+
2103
+ 1. **p6 的 `d3` 是无效跑,不是「慢」那么简单。** `valid=false` 在这一套里的定义是「测试没跑起来」:`total==0`(或 leak)。这一跑走了 50 步、40 979 token、665 s,`hasSessionIds`/`green` 仍为真但 grade 没有产出,所以按预注册的「`hasSessionIds` 且 `green` 且 `valid`」被丢弃。它同时也是**第 3 步提醒的尾部风险证据**:更早的提醒可能让模型在长迁移上更早收束、也可能让它反复试探。这一条不能当「d3 更慢 3.17×」讲,只能当「一次没交付的跑」记着。
2104
+ 2. **同一块里 `def15` 也出了一次 620 s / 36 步的长尾**(`valid=true`,交付正常)。两臂各一次超长尾,方向不同,说明 migrate 这一族本身是重尾分布,单看某一对没有意义——这也是继续只报几何 + 胜场、不报单对的原因。
2105
+
2106
+ **机制读数(沿用 9 条分解)。** 5 对里赢的四对全部同时压步数与输出(步数 0.622×、输出 0.528×),与 `d2`/`d2g` 在 migrate 上看到的「输出 token 才是主因」一致:p1 的 275.6→96.7 s 里步数贡献 −58 s、每步成本贡献 −120 s;p4 的 620.6→93.7 s 里步数贡献 −368 s。p5 是唯一「步数少了 8 步、每步反而更贵」的一对(9.8→14.6 s/步),净收益只剩 11 %——和「早提醒不是无条件赢」的既有结论吻合。
package/CHANGELOG.md CHANGED
@@ -1,5 +1,24 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.5.19(稳定性发布:面板三项真正可读写、检查复用证据不透支、取消三态、实验臂与判据对齐)
4
+
5
+ > 1.5.18 在 npm 上只有文档改动(README 结论先行),代码修复都在 1.5.19;升级请直接装 1.5.19。
6
+
7
+ - **面板三项设置在官方宿主上真正可读可写**:官方的 `describe()` 只投影 volatile 字段,`outputDiscipline` / `stepBudget` / `workspaceMap` 原先非 volatile,于是面板只能画内置默认(`late` / 0 / off)、写入被宿主以 `Config field "…" is not volatile` 拒绝。三项声明 `.volatile()` 后 describe 投影、write 接受、写后读回同一值;默认值不变,实际写入仍只落在本会话的 `sessions` 条目。
8
+ - **检查复用证据按检查独立,且要求路径可信**:`reuse-check` 的命中判定从「同一工具 + 同一参数」收紧到「同一条检查 + 同一份产物」,只有参数里出现可信路径、且该路径在本轮没有被改写过时才算复用;否则只按无进展提醒,不给「结果可复用」的结论。
9
+ - **取消三态可信**:取消结果区分「已请求取消」「已确认进程退出」「未确认」三种,只有第二种能当失败结论;后两种都把「进程可能仍在跑」写回上下文,不再把未确认的取消报成已退出。
10
+ - **`disciplineEarlyCalls` 的决策在第 1 步冻结**:首次请求时确定本轮的提醒步数,同一轮内不再随步数重算,避免同一轮里提醒时点漂移。
11
+ - **输出预算按本轮步数**:`outputBudget` / `outputBudgetAfterSteps` 用本轮步数结算,不再读跨轮累计值。
12
+ - **父 signal 监听器清理**:看门狗在调用结束(成功、失败、取消)后移除挂在父 `AbortSignal` 上的监听器,长会话不再累积。
13
+
14
+ - README 顶部换成结论先行:先说清楚这是 DeepSeek Harness 自己的快速模式、能控制什么(执行策略:减少冗余回合与输出,不改模型与思考强度),再给四行实测——默认 0.72× 总耗时 / 0.74× 输出 token / 0.71× 步数;提醒提前到第 2 步在 `debugfix` 0.535×、`ledger` 0.506×、`migrate` 0.751×,13 对合并 0.625×、输出 0.614×;第 3 步在 `migrate` 到 0.509×、输出 0.528×;短改修与数据整形不承诺。数字全部取自 BENCHMARK.md 已有块,没有新增实验。
15
+ - 表述统一:正文里的「墙钟」改成「耗时」(术语表词条同步重写),不再以「墙钟」作为对读者的解释词;结论只讲「快多少、token 少多少」。
16
+ - 市场卡面文案同步改写(en / zh):去掉「墙钟」措辞,速度口径与上面的全池 0.823×(12 块 138 对)一致。
17
+ - 英文 README 同步。
18
+ - README / REFERENCE 的机制表述与实测对齐:新增「三条边界」(承诺不改模型与 `reasoningEffort`;收益来自冗余回合与输出 token 下降,不归因于删旁白;质量以隐藏测试与真实评测为准,不宣称行为完全等价)。旁白占比有了可复算口径:新增 `bench/textshare.py`(transcript 里 `assistant/message` 可见 text 字符 ÷(text + `tool/call.arguments` 字符),不含 reasoning、工具返回与用户消息),def15 全量 341 会话 / 3,327 消息 / 3,569 调用,正文 51,366 / 参数 4,315,096 = **1.18 %**(中位 0.98 %、p90 4.08 %),`python3 bench/textshare.py --limit 0` 复算。
19
+ - 面板默认值与 1.5.17 相同;`compactRereadHint` 默认关闭,不在本版默认行为内。
20
+ - **验证**:`node --test test/*.test.js` **302 pass / 0 fail**(新增面板读写的宿主投影/校验用例、`reuse-check` 证据语义用例、取消三态与监听器清理用例);`ui-copy-lint` clean、`zh-lint` 0 error(37 warning 既有)、`style-lint` 通过;`bench/verify-arm-reminders.mjs` 机制 10/10、臂×场景 8/8 通过。
21
+
3
22
  ## 1.5.17(面板四行、按会话隔离、网关用量兼容)
4
23
 
5
24
  - **四个开关按会话隔离(核心修复)。** 面板此前写的是 `outputDiscipline` / `stepBudget` / `workspaceMap` 三个共享字段,而设置列的写入只接受 volatile 字段:`settings.write` 对这三个路径直接抛 `Config field "<path>" is not volatile`,所以按钮点了不生效。现在所有选择都写进 volatile 的 `sessions` 列里本会话的条目(`{enabled, gear, discipline, stepBudget, map, ts}`),面板与闪电只写自己那条,不碰共享的 `enabled`/`gear`:A 会话「更快 + 少写过程」与 B 会话「更快 + 不写过程」同时成立,没点过的会话沿用配置默认值。条目上限 200,按 `ts` 淘汰最旧的。
package/README.en.md CHANGED
@@ -8,8 +8,8 @@
8
8
  <h1 align="center">dsh-fast-mode</h1>
9
9
 
10
10
  <p align="center">
11
- <b>Same thinking strength. Speed only where reads can run together.</b><br>
12
- Wide when there is evidence, native when there is not; no request fields are added or changed.
11
+ <b>DeepSeek Harness's own fast mode: a few configs that set how fast a task runs.</b><br>
12
+ Never changes the model or its reasoning effort; it cuts redundant round trips and output through execution-strategy hints — the gain is fewer steps and fewer output tokens, not deleted narration.
13
13
  </p>
14
14
 
15
15
  <p align="center">
@@ -22,25 +22,40 @@
22
22
 
23
23
  ---
24
24
 
25
+ ## Results first
26
+
27
+ Across 10 task families and 30 complete units (hidden tests 60/60 green):
28
+
29
+ - **Default (whole pool, no block picking)**: 12 clean blocks, 10 task families, 138 valid pairs, added as whole tasks — **0.823x** total time (about 18 % saved), 0.818x output tokens, 0.806x steps, 93 of 138 pairs faster (recompute with `python3 bench/pool.py`). The earlier 0.72-0.74x came from three hand-picked blocks and has been withdrawn; why is in BENCHMARK.md.
30
+ - **Moving the "write it once" reminder from step 4 to step 2**: `debugfix` 0.535x, `ledger` 0.506x, `migrate` 0.751x; 13 pairs pooled **0.625x** with output tokens **0.614x**. At step 3, `migrate` alone reaches **0.509x** with output tokens **0.528x**.
31
+ - **Quality**: in every pair both arms produced identical hidden assertions, all green; the saving is fewer edit-recheck loops, not fewer checks.
32
+ - **Where it does not pay**: 1-5 step repairs and data reshaping (`perfrefactor` 1.18x, `csvpipe` 1.12x), so try it once per task shape.
33
+
25
34
  ## What it does
26
35
 
27
36
  A step costs round-trip overhead × steps + decode time × output tokens + tool wait. Model and thinking strength are not in that equation, so fast mode only moves three programmatic quantities:
28
37
 
29
38
  - **Parallel fan-out.** The scheduler asks whether this step is "several reads of different targets"; only then does it go wide (up to 8) and remember that shape across turns. Write steps, single-call steps, erroring or stalled turns **stay native** — the whole batching section is not rendered, so it costs no input tokens.
30
- - **Output discipline.** It trims process narration (no call previews, no restating results, no progress reports); the final answer is untouched.
39
+ - **Output discipline.** Asks the model to skip call previews, restatements and progress reports, never to shorten the final answer. Honest scope: visible assistant prose is only **1.18 %** of the characters in (prose + tool arguments) (def15, 341 sessions / 3,327 messages / 3,569 calls, 51,366 prose vs 4,315,096 argument characters; excludes reasoning and tool results; recompute with `python3 bench/textshare.py --limit 0`), so the speedup cannot be attributed to deleted narration — it comes from fewer round trips and fewer output tokens (fewer repeat edits, re-reads and verification passes). That is not the same claim as "nothing about the work changes".
31
40
  - **Stall watchdog.** Shell/script calls get a 120–300 s adaptive cap; a stuck call is cancelled and "change the approach" is written back into the context.
32
41
 
33
42
  No model switch, no `reasoningEffort` rewrite, no request-field injection (`service_tier`, `max_tokens`, anything like them). While the mode is on, the model's input carries this plugin's own scheduling guidance and reminders — that is the mechanism; switch the mode off and they are gone. DeepSeek's official API, an OpenAI-compatible relay, a self-hosted gateway, Anthropic routes — all work the same.
34
43
 
44
+ ## Three boundaries
45
+
46
+ - **The explicit promise**: the plugin does not change the model, does not rewrite `reasoningEffort`, and does not inject request fields. That is where quality degradation would most likely come from, and the plugin stays out of it.
47
+ - **Honest mechanism**: execution-strategy hints cut redundant round trips and output. The evidence is falling steps and output tokens (whole-pool default arm: 0.806× steps, 0.818× output); no claim that the gain comes from deleting narration.
48
+ - **Quality boundary**: quality is judged by hidden tests and real evaluations, and behaviour is not claimed to be fully equivalent. When the quality gate fails, quality wins: experimental knobs ship off, and the default is only adopted from batches where both arms' hidden assertions match cell by cell.
49
+
35
50
  ## Three words, defined
36
51
 
37
- - **Wall clock**: end-to-end time from the task being issued to the headless process exiting, including model latency, tool waits and plugin overhead. Every "X s" below is this.
38
- - **Rate**: the reciprocal of wall clock (1/wall). 10 s → 8 s is a 1.25× rate.
52
+ - **Time**: end-to-end time from the task being issued to the headless process exiting, including model latency, tool waits and plugin overhead. Every "X s" below is this.
53
+ - **Rate**: the reciprocal of time (1/wall). 10 s → 8 s is a 1.25× rate.
39
54
  - **Speedup**: `wall(off) / wall(on)`. **Above 1 means faster**: 1.28× means 22 % less time; 0.8× means 25 % slower. A 95 % CI that crosses 1 means "indistinguishable from off".
40
55
 
41
56
  ## Measured
42
57
 
43
- **Toggle it on and the headline is: about 18% faster in total, and 25–30% faster on the shapes it fits.** That is every clean block with an `off` anchor pooled (12 blocks, 138 valid pairs, 10 task families; recompute with `python3 bench/pool.py`): total wall clock **0.823×** (18% less), output tokens 0.818×, steps 0.806×, per-cell median 0.799×, faster in 93 of 138 pairs (sign test p = 0.0001). Bootstrapping those 138 pairs 2000 times puts the total ratio at a 95% interval of **[0.742, 0.913]** (9% to 26% faster), the per-cell median at [0.703, 0.885], and the geometric mean at 0.825× (interval 0.753–0.900) — "about 18%" is a point estimate, not a floor. The only quality statement at the same denominator: hidden assertions matched cell for cell across the 138 pairs (`off` 2038/2038, default 2037/2038, the one difference being a 21/22 cell) — that is **assertion parity**, not "zero quality loss". Per-family stability and sign tests are in [BENCHMARK.md](BENCHMARK.md), section "合池的自助区间与逐任务稳定性", which also explains why per-family numbers cannot be quoted across blocks.
58
+ **Toggle it on and the headline is: about 18% faster in total, and 25–30% faster on the shapes it fits.** That is every clean block with an `off` anchor pooled (12 blocks, 138 valid pairs, 10 task families; recompute with `python3 bench/pool.py`): total time **0.823×** (18% less), output tokens 0.818×, steps 0.806×, per-cell median 0.799×, faster in 93 of 138 pairs (sign test p = 0.0001). Bootstrapping those 138 pairs 2000 times puts the total ratio at a 95% interval of **[0.742, 0.913]** (9% to 26% faster), the per-cell median at [0.703, 0.885], and the geometric mean at 0.825× (interval 0.753–0.900; that is the paired geometric mean, a different statistic from the pooled total ratio **0.823×**, and the two are not interchangeable) — "about 18%" is a point estimate, not a floor. The only quality statement at the same denominator: hidden assertions matched cell for cell across the 138 pairs (`off` 2038/2038, default 2037/2038, the one difference being a 21/22 cell) — that is **assertion parity**, not "zero quality loss". Per-family stability and sign tests are in [BENCHMARK.md](BENCHMARK.md), section "合池的自助区间与逐任务稳定性", which also explains why per-family numbers cannot be quoted across blocks.
44
59
 
45
60
  **When to turn it on, and when not to.** On: mid-length tasks with several artifacts and repeated re-checks — `codeimpl` 0.663x (faster in 13 of 15 pairs, sign test p = 0.007) and `shellpipeline` 0.749x (p = 0.039), with `stategraph` 0.708x, `shellops` 0.748x, `codebase` 0.808x and `migrate` 0.822x on the same side but with too few pairs to stand alone. Off: one-to-five-step repairs and data reshaping — `debugfix` 1.032x (output actually 1.29x longer) and `perfrefactor` 1.009x, where the injected reminder cannot pay for itself. `ledger` 0.915x and `csvpipe` 0.982x are a wash. The switch is the lightning bolt per conversation; it remembers your last choice and applies from the next request.
46
61
 
package/README.md CHANGED
@@ -8,8 +8,8 @@
8
8
  <h1 align="center">dsh-fast-mode</h1>
9
9
 
10
10
  <p align="center">
11
- <b>思考强度不变,只给“能并行的读”提速。</b><br>
12
- 有证据才并行,没证据就原生;不加请求字段、不改思考强度。
11
+ <b>DeepSeek Harness 自己的快速模式:用几项配置控制任务的运行速度。</b><br>
12
+ 不改模型、不改 reasoningEffort;用执行策略提示减少冗余回合与输出——收益来自步数与输出 token 下降,不是删旁白。
13
13
  </p>
14
14
 
15
15
  <p align="center">
@@ -22,37 +22,52 @@
22
22
 
23
23
  ---
24
24
 
25
+ ## 先看结论
26
+
27
+ 在 10 类任务、30 个完整单元上(隐藏测试 60/60 全绿):
28
+
29
+ - **默认配置(全池口径,不挑块)**:12 个干净块、10 类任务、138 对有效配对的整任务总量 —— 总耗时 **0.823×**(省约 18 %)、输出 token 0.818×、步数 0.806×,138 对里 93 对更快(`python3 bench/pool.py` 可复算)。之前对外说的 0.72–0.74× 只来自挑出来的三块,已撤回,原因写在 BENCHMARK.md。
30
+ - **把「省着写」提醒从第 4 步提前到第 2 步**:`debugfix` 0.535×(快约 47%)、`ledger` 0.506×(快约 49%)、`migrate` 0.751×;13 对合并 **0.625×**(快约 38%)、输出 token **0.614×**(少约 39%)。再提前到第 3 步,`migrate` 单族 **0.509×**(快约 49%)、输出 token **0.528×**(少约 47%)。
31
+ - **质量**:上述每一对里两臂的隐藏断言逐格相同、全绿;快的是少做几遍「改一版→再读一遍→再改一版」,不是少验证。
32
+ - **不承诺的地方**:1–5 步的小改修与数据整形收不回提醒成本(`perfrefactor` 1.18×、`csvpipe` 1.12×),所以按任务形状先试一次再定。
33
+
25
34
  ## 它到底做什么
26
35
 
27
36
  一步的用时 = 往返开销 × 步数 + 解码时间 × 输出 token + 工具等待。模型和思考强度不在这个式子里,所以快模式只动三个程序量:
28
37
 
29
38
  - **并行扇出**:调度器先判断这一步是不是“多个互不依赖的读”,是才要宽(最多 8),并且跨轮记住这个形状;写操作、单调用、连续报错或无进展的回合**保持原生** —— 整段批处理提示不渲染,不为它付输入 token。
30
- - **输出纪律**:只裁剪过程叙述(不预告、不复述、不写进度),最终答案完整保留。
39
+ - **输出纪律**:提示模型不预告调用、不复述结果、不写进度报告,但**不缩短最终答案**。口径要说清:实测里可见助手正文只占「正文+工具参数」字符的 **1.18%**(def15 全量 341 会话 / 3,327 消息 / 3,569 调用,正文 51,366 / 参数 4,315,096 字符;不含 reasoning 与工具返回;`python3 bench/textshare.py --limit 0` 可复算),所以提速不能归因于删旁白——它来自回合数与输出 token 变少(少重复编辑、少回头读、少验证往返),这和「内容不受影响」不是一回事。
31
40
  - **卡顿看门狗**:给 shell/脚本类调用设 120–300 秒自适应上限,卡住的调用被取消并把“换做法”写回上下文。
32
41
 
33
42
  没有模型切换、没有 `reasoningEffort` 改写、没有请求字段注入;开着时模型输入里会多出本插件自己的调度提示与提醒(这是机制本身),关掉后连这些也不存在。DeepSeek 官方 API、OpenAI 兼容中转、自建网关、Anthropic 协议 —— 走哪条路由都成立。
34
43
 
44
+ ## 三条边界
45
+
46
+ - **明确承诺**:不改模型、不改 `reasoningEffort`、不注入请求字段。降智风险最容易发生的地方,插件不碰。
47
+ - **机制诚实**:靠执行策略提示减少冗余回合与输出。证据是步数与输出 token 下降(全池默认臂 0.806× 步数、0.818× 输出);不宣称收益来自「删旁白」。
48
+ - **质量边界**:任务质量以隐藏测试判分与真实评测为准,不宣称行为完全等价。质量门槛不过就优先保质量:实验性旋钮默认关闭,默认配置只在两臂隐藏断言逐格相同的批次里采用。
49
+
35
50
  ## 先把三个词说清楚
36
51
 
37
- - **墙钟(wall clock)**:任务从发出到 headless 进程退出的端到端真实耗时,包含模型延迟、工具等待、插件开销。下文“X 秒”都是它。
38
- - **速率**:墙钟的倒数(1/墙钟)。墙钟从 10 s 降到 8 s,速率就是 1.25 倍。
39
- - **加速比**:`关掉时的墙钟 ÷ 打开时的墙钟`。**大于 1 才是变快**;1.28× 等于省 22 % 的时间,0.8× 等于慢了 25 %。95 % 区间(CI)跨过 1 就是“和没开分不出来”。
52
+ - **耗时**:任务从发出到 headless 进程退出的端到端时间,包含模型延迟、工具等待与插件开销。下文的「X 秒」都是它。
53
+ - **速率**:耗时的倒数(1/耗时)。耗时从 10 s 降到 8 s,速率就是 1.25 倍。
54
+ - **加速比**:`关掉时的耗时 ÷ 打开时的耗时`。**大于 1 才是变快**;1.28× 等于省 22 % 的时间,0.8× 等于慢了 25 %。95 % 区间(CI)跨过 1 就是“和没开分不出来”。
40
55
 
41
56
  ## 实测
42
57
 
43
- **打开小闪电,排头的一句结论:整任务总量约快 18 %,适合的形状快 25–30 %。** 这是把**所有**有 `off` 锚的干净块一起加总的结果(12 个块、138 对有效配对、10 个任务家族,`python3 bench/pool.py` 可复算):总墙钟 **0.823×**(省 18 %)、输出 token 0.818×、步数 0.806×、逐格中位 0.799×、138 对里 93 对更快(符号检验 p = 0.0001)。对这 138 对自助重抽 2000 次,总比 95 % 区间是 **[0.742, 0.913]**(即省 9 %–26 %),逐格中位区间 [0.703, 0.885],几何均值 0.825×(区间 0.753–0.900)——「约 18 %」是点估计、不是下限承诺。质量的同口径结论只有一句:138 对里两臂隐藏断言逐格完全相同(`off` 2038/2038、默认 2037/2038,唯一差的是一格 21/22),这是**断言对等**,不是「零质量损失」。逐家族稳定性与符号检验见 [BENCHMARK.md](BENCHMARK.md) 的「合池的自助区间与逐任务稳定性」一节,那里也写了为什么逐家族数字不能跨块引用。
58
+ **打开小闪电,排头的一句结论:整任务总量约快 18 %,适合的形状快 25–30 %。** 这是把**所有**有 `off` 锚的干净块一起加总的结果(12 个块、138 对有效配对、10 类固定任务,`python3 bench/pool.py` 可复算):总耗时 **0.823×**(省 18 %)、输出 token 0.818×、步数 0.806×、逐格中位 0.799×、138 对里 93 对更快(符号检验 p = 0.0001)。对这 138 对自助重抽 2000 次,总比 95 % 区间是 **[0.742, 0.913]**(即省 9 %–26 %),逐格中位区间 [0.703, 0.885],几何均值 0.825×(区间 0.753–0.900;这是配对几何均值,与全池总量比 **0.823×** 是两种口径,不可互换)——「约 18 %」是点估计、不是下限承诺。质量的同口径结论只有一句:138 对里两臂隐藏断言逐格完全相同(`off` 2038/2038、默认 2037/2038,唯一差的是一格 21/22),这是**断言对等**,不是「零质量损失」。逐家族稳定性与符号检验见 [BENCHMARK.md](BENCHMARK.md) 的「合池的自助区间与逐任务稳定性」一节,那里也写了为什么逐家族数字不能跨块引用。
44
59
 
45
60
  **什么时候开、什么时候别开(按实测形状,不按任务名)。** 开:有多份产物、要反复跑同一份检查的中长任务——`codeimpl` 0.663×(15 对里 13 对更快,符号检验 p = 0.007)、`shellpipeline` 0.749×(p = 0.039),`stategraph` 0.708×、`shellops` 0.748×、`codebase` 0.808×、`migrate` 0.822× 也在同一侧但样本撑不起单列结论。别开:1–5 步的小改修与数据整形——`debugfix` 1.032×(输出反而长 1.29×)、`perfrefactor` 1.009×,提醒本身的注入成本收不回来。`ledger` 0.915×、`csvpipe` 0.982× 打平,开不开都行。开关就是每会话的小闪电,默认记住上次选择,下一个请求生效。
46
61
 
47
62
  **1.5.0 的默认不做步数收尾。** `finishLineSteps` 与 `stepBudget` 默认都是 `0`:没有“第 10 步提醒收尾”,也没有“30 步停止”。结束由任务状态和完成证据决定 —— 同一份产物上逐字重复一条已通过的检查,建议复用结果、把这一步留给剩下的工作;连续几步没有新结果,建议换方法(只是提醒,不结束回合);工具卡住,看门狗取消它并等它真正停下。任务做完就正常报告,提醒不会替你判停。
48
63
 
49
- **1.5.1 干净重基线(10 家族、30 个完整单元、隐藏测试 60/60 全绿)**:不剔任何运行(只看产物里有没有泄漏标记)、按整任务总量加总 —— 关闭 **3434.7 s / 591 244 token / 390 步**;默认臂 **0.72× 墙钟 / 0.74× 输出 / 0.71× 步数**;逐格中位 **0.64×**、均值 0.84×、90 % 区间 [0.59, 0.79]。收益集中在反复重跑同一条检查的任务(`migrate` 0.45×、`shellops` 0.57×、`codebase` 0.55×、`stategraph` 0.64×、`ledger` 0.64×);短任务收益不稳定,`perfrefactor` 1.18×、`csvpipe` 1.12×、`debugfix` 输出反而 2.01×——短任务本来就没有可省的重复验证,提醒本身也是成本。这一轮同时修掉了两个会让旧数字失真的测量缺陷(运行目录曾在仓库内、指标曾按字节偏移切片),旧口径的 `0.86×` 已撤回,详见 [BENCHMARK.md](BENCHMARK.md) 的「1.5.1」与「1.5.0 重基线(已撤回)」。
50
- **复现块 W5(2026-10-26,19 个完整单元)**:同一块里三臂直接对照,`off` 1855.5 s,默认臂 0.73× 墙钟 / 0.77× 输出 / 0.89× 步数,与 1.5.1 的 0.72× 一致。同块的 `batchVerify` 臂记到 0.84×,但那一臂的提醒**实际一次都没下发**(20 个会话逐条检索命中 0),所以 `batchVerify` 当时是**未测**、不是变慢也不是被证伪。**后来修好接线重测,结论是淘汰**:`codeimpl` 8 对几何 1.478×、`shellpipeline` 8 对 0.956×,合并 16 对 **1.188×**、中位 1.148×、只有 7/16 更快,全绿且提醒确实下发(8/16 会话命中)—— 所以不是没测到,是家族方向不一致、合起来更慢,默认保持 `false`,见 [BENCHMARK.md](BENCHMARK.md) 的「`batchVerify` 复测块 BV8」。这一块还有 1 行泄漏(`shellpipeline` p1 的 `bv15` 读到隐藏测试与参考解),已点名剔除;跑批器现在开跑前会把 `hidden/`、`ref/` 置为不可读,详见 [BENCHMARK.md](BENCHMARK.md) 的「复现块 W5」与 [bench/README.md](bench/README.md) 协议第 12 条。
51
- **筛选块 W10 + 复现块 W11(2026-10-26,合并 23 个有效单元):`workspaceMap` 只兑现一半,默认关闭。** 1.5.2 加了一个默认关闭的旋钮:会话第一次动手前,把工作目录的清单(深度 2、≤60 条、跳过依赖与构建目录)先交给模型一次,省掉它自己 `ls`/`find` 的那一步。两块同块对照 `def15`:合并 23 个单元 **墙钟 0.927×、输出 0.896×、步数 1.099×**,逐格中位 0.791×、13/23 格更快、隐藏测试零红。方向被证实一半——输出确实少 10 %(两块分别是 0.90×、0.89×);但步数反而多 10 %,而且尾部变差:两臂 p90 是 559 s 对 369 s、最慢 714 s 对 510 s,W10 与 W11 都出现同一形状的反向格(`stategraph` 两轮都 1.4×–1.77×)。**按「长任务不能变慢」的门槛它不进默认**,1.5.2 的默认行为与 1.5.1 完全相同,旋钮与数字一起公开。这一轮同时把余量算清了:重复验证只占 6 % 墙钟、工具结果中位 1.2 kB、重复写文件占 0.8 %,所以 **0.67× 在「不动 `reasoningEffort`、不加请求字段」的约束内不可达**,细节见 [BENCHMARK.md](BENCHMARK.md) 的「复现块 W11」与「0.67× 在现有约束下的可达性」。
64
+ **1.5.1 干净重基线(10 家族、30 个完整单元、隐藏测试 60/60 全绿;这三块后来被认为挑块,只作历史记录,对外只用上面的全池 0.823×)**:不剔任何运行(只看产物里有没有泄漏标记)、按整任务总量加总 —— 关闭 **3434.7 s / 591 244 token / 390 步**;默认臂 **0.72× 耗时 / 0.74× 输出 / 0.71× 步数**;逐格中位 **0.64×**、均值 0.84×、90 % 区间 [0.59, 0.79]。收益集中在反复重跑同一条检查的任务(`migrate` 0.45×、`shellops` 0.57×、`codebase` 0.55×、`stategraph` 0.64×、`ledger` 0.64×);短任务收益不稳定,`perfrefactor` 1.18×、`csvpipe` 1.12×、`debugfix` 输出反而 2.01×——短任务本来就没有可省的重复验证,提醒本身也是成本。这一轮同时修掉了两个会让旧数字失真的测量缺陷(运行目录曾在仓库内、指标曾按字节偏移切片),旧口径的 `0.86×` 已撤回,详见 [BENCHMARK.md](BENCHMARK.md) 的「1.5.1」与「1.5.0 重基线(已撤回)」。
65
+ **复现块 W5(2026-10-26,19 个完整单元)**:同一块里三臂直接对照,`off` 1855.5 s,默认臂 0.73× 耗时 / 0.77× 输出 / 0.89× 步数,与 1.5.1 的 0.72× 一致。同块的 `batchVerify` 臂记到 0.84×,但那一臂的提醒**实际一次都没下发**(20 个会话逐条检索命中 0),所以 `batchVerify` 当时是**未测**、不是变慢也不是被证伪。**后来修好接线重测,结论是淘汰**:`codeimpl` 8 对几何 1.478×、`shellpipeline` 8 对 0.956×,合并 16 对 **1.188×**、中位 1.148×、只有 7/16 更快,全绿且提醒确实下发(8/16 会话命中)—— 所以不是没测到,是家族方向不一致、合起来更慢,默认保持 `false`,见 [BENCHMARK.md](BENCHMARK.md) 的「`batchVerify` 复测块 BV8」。这一块还有 1 行泄漏(`shellpipeline` p1 的 `bv15` 读到隐藏测试与参考解),已点名剔除;跑批器现在开跑前会把 `hidden/`、`ref/` 置为不可读,详见 [BENCHMARK.md](BENCHMARK.md) 的「复现块 W5」与 [bench/README.md](bench/README.md) 协议第 12 条。
66
+ **筛选块 W10 + 复现块 W11(2026-10-26,合并 23 个有效单元):`workspaceMap` 只兑现一半,默认关闭。** 1.5.2 加了一个默认关闭的旋钮:会话第一次动手前,把工作目录的清单(深度 2、≤60 条、跳过依赖与构建目录)先交给模型一次,省掉它自己 `ls`/`find` 的那一步。两块同块对照 `def15`:合并 23 个单元 **耗时 0.927×、输出 0.896×、步数 1.099×**,逐格中位 0.791×、13/23 格更快、隐藏测试零红。方向被证实一半——输出确实少 10 %(两块分别是 0.90×、0.89×);但步数反而多 10 %,而且尾部变差:两臂 p90 是 559 s 对 369 s、最慢 714 s 对 510 s,W10 与 W11 都出现同一形状的反向格(`stategraph` 两轮都 1.4×–1.77×)。**按「长任务不能变慢」的门槛它不进默认**,1.5.2 的默认行为与 1.5.1 完全相同,旋钮与数字一起公开。这一轮同时把余量算清了:重复验证只占 6 % 耗时、工具结果中位 1.2 kB、重复写文件占 0.8 %,所以 **0.67× 在「不动 `reasoningEffort`、不加请求字段」的约束内不可达**,细节见 [BENCHMARK.md](BENCHMARK.md) 的「复现块 W11」与「0.67× 在现有约束下的可达性」。
52
67
 
53
- **复现块 W7(2026-10-26,17 个有效单元)**:默认臂 **0.73×** 墙钟 / 0.75× 输出 / 0.59× 步数(最长一格 36 步 → 17 步),与 1.5.1 的 0.72×、W5 的 0.73× 一致:三块合并 65 个有效单元 **0.74×**。同块的 `openBatch` 是 0.91×,对默认头对头 1.136×,W6 的 0.77× 没有复现,**淘汰**。这一块还有 7 行运行崩溃(会话启动阶段 `exit=1`、1 步、0 输出),3 个单元因此作废并点名;从这一块起判据要求 `exit == 0 && 步数 ≥ 1 && 输出 > 0 && 判分完成 && 未泄漏` 才计数,所以「全绿」一律指有效单元全绿(1.5.1 29/29、W5 19/19、W7 17/17)。详见 [BENCHMARK.md](BENCHMARK.md) 的「复现块 W7」。
68
+ **复现块 W7(2026-10-26,17 个有效单元)**:默认臂 **0.73×** 耗时 / 0.75× 输出 / 0.59× 步数(最长一格 36 步 → 17 步),与 1.5.1 的 0.72×、W5 的 0.73× 一致:三块合并 65 个有效单元 **0.74×**。同块的 `openBatch` 是 0.91×,对默认头对头 1.136×,W6 的 0.77× 没有复现,**淘汰**。这一块还有 7 行运行崩溃(会话启动阶段 `exit=1`、1 步、0 输出),3 个单元因此作废并点名;从这一块起判据要求 `exit == 0 && 步数 ≥ 1 && 输出 > 0 && 判分完成 && 未泄漏` 才计数,所以「全绿」一律指有效单元全绿(1.5.1 29/29、W5 19/19、W7 17/17)。详见 [BENCHMARK.md](BENCHMARK.md) 的「复现块 W7」。
54
69
 
55
- **发版前复核(REL16,10 个单元)**:用当时的默认再对 `off` 跑一块,只查「质量有没有被发版弄坏」。结果两臂 **10/10 全绿、逐任务通过数完全相同**(14/14、22/22、18/18、10/10、14/14),无 leak;墙钟合计 **0.967×**、逐格中位 0.999×、3/5 格更快。单对 n=5,噪声带比总量宽,所以这条只作复核,不替换合池 0.798× 的总口径,详见 [BENCHMARK.md](BENCHMARK.md)。
70
+ **发版前复核(REL16,10 个单元)**:用当时的默认再对 `off` 跑一块,只查「质量有没有被发版弄坏」。结果两臂 **10/10 全绿、逐任务通过数完全相同**(14/14、22/22、18/18、10/10、14/14),无 leak;耗时合计 **0.967×**、逐格中位 0.999×、3/5 格更快。单对 n=5,噪声带比总量宽,所以这条只作复核,不替换合池 0.798× 的总口径,详见 [BENCHMARK.md](BENCHMARK.md)。
56
71
 
57
72
  **更快但没过门禁的配置只作消融臂。** `fl10`(第 10 步提醒收尾)是实测最快的配置,但它会压掉「测试通过后又改了代码」的必要复验,且出现过基线与默认都绿而它红的一格,所以只是消融臂(这些数字出自已撤回的批次,只保留决策记录)。`openBatch`(开局发一次「把独立调用并进同一块」)在 W6 筛查里以 0.77× 快过默认的 0.93×,但 W7 独立复测回到 0.91×、头对头 1.136×,**没有复现即淘汰**。`batchVerify`(改完一批文件再跑一次覆盖检查)在 W5 同块对照里记到 0.84×,但那一臂的提醒零下发,当时只能算**未测**;接线修好后在 BV8 复测块实测 16 对合并 **1.188× 更慢**(`codeimpl` 1.478×、`shellpipeline` 0.956×,方向不一致),**淘汰**,默认保持 `false`。**所以默认不变**:不按步数收尾、`stepBudget: 0`、上述机制全关。单元级数据与门禁逐条见 BENCHMARK 的 `H2`/`H3`。
58
73
 
@@ -90,7 +105,7 @@
90
105
  闪电就是开关:开/关即写,按会话独立,重启按会话恢复。右键闪电是一块四行的面板,四行都只影响当前会话:
91
106
 
92
107
  - **更快**:这个会话开不开快模式,和点闪电是同一件事。
93
- - **少写过程**:第 4 步那句「省着写」提醒的总开关,关掉后输出会变长、墙钟变慢(长任务约 2.7×),默认开。
108
+ - **少写过程**:第 4 步那句「省着写」提醒的总开关,关掉后输出会变长、耗时变慢(长任务约 2.7×),默认开。
94
109
  - **最多几步**:`15` / `30` / 不限,默认不限。到上限只先汇报,不截断任务。
95
110
  - **先列文件清单**:开启后,回合的第一次工具调用之前先收到一份工作目录清单(相对路径 + 字节数 + 可见的测试入口),省掉开头的 `ls`/`find` 探路。默认关;23 个配对实测几何 0.927×,尾部变慢、步数 +10%,没有达到进默认的门槛,只作为可试的开关。
96
111
 
@@ -102,7 +117,7 @@
102
117
 
103
118
  判断“这一步值不值得提速”的可编程信号现在有三个:请求自身的措辞(串行/扇出/shell,见 `src/shape.js`)、上一步的调用是否全为只读且目标两两不同、以及连续只读步数。前两个让单轮任务第一步就能选对宽度。
104
119
 
105
- 在线选择(UCB/Thompson)**不在**这一版里,原因不是网络 —— bandit 全是本地算术,不额外发请求、不增加往返,实测代价可以忽略。真正的问题是奖励太吵:本项目 40 对配对里,同一任务两臂的方差远大于宽度带来的效应;把这种噪声喂给 UCB,学到的会是抖动而不是策略。等请求形状分类把“该宽的样本”筛出来、每步墙钟的方差降下来,再上 bandit 才有意义;设计上会用折扣 Thompson 采样(先验 N(0, σ²),只对“扇出/未知”两类上下文分别维护宽度后验),而不是全局 UCB。
120
+ 在线选择(UCB/Thompson)**不在**这一版里,原因不是网络 —— bandit 全是本地算术,不额外发请求、不增加往返,实测代价可以忽略。真正的问题是奖励太吵:本项目 40 对配对里,同一任务两臂的方差远大于宽度带来的效应;把这种噪声喂给 UCB,学到的会是抖动而不是策略。等请求形状分类把“该宽的样本”筛出来、每步耗时的方差降下来,再上 bandit 才有意义;设计上会用折扣 Thompson 采样(先验 N(0, σ²),只对“扇出/未知”两类上下文分别维护宽度后验),而不是全局 UCB。
106
121
 
107
122
  [实测与复现](BENCHMARK.md) · [参考手册](docs/REFERENCE.md) · [改了什么](CHANGELOG.md) · [English](README.en.md)
108
123
 
@@ -4,7 +4,7 @@ This manual describes what **dsh-fast-mode 1.5.17** actually does (injection beh
4
4
 
5
5
  **The one contract: fast mode never changes reasoning effort, the thinking budget, or the model.** The `agent/request` hook does exactly one thing — `await next()`, read the `reasoningEffort` the request already carries for measurement, then return it unchanged. Whatever effort the user picked (the per-session choice in the UI, or the effort pinned by the profile's agent-default-model) is the effort every request uses.
6
6
 
7
- Speed comes from three things only: fewer round trips, less narration, less tool wait. Each gets a section below.
7
+ Speed comes from three things only: fewer round trips, less redundant output, less tool wait. Each gets a section below.
8
8
 
9
9
  ## Interaction details: the bolt switch and which conversation it applies to
10
10
 
@@ -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 trim narration only — no announcing a call, no restating a result, no progress report; 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.**
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".
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.
@@ -164,6 +164,7 @@ In the profile's `cordis.patch.yml`:
164
164
  | `openBatch` | `false` | Opening batch: one notice on the first step of each turn asking for every independent call in one block and shell commands chained with `&&`. A short task spends its time on per-call fixed cost (TTFT ≈ 0.5 s), so one fewer round trip is one fewer TTFT. It only touches the first step of a turn. In the old batches it measured no faster than the default, but **those numbers are retracted** (run directories lived inside the repo and metrics were sliced by byte offset); it has not been re-measured cleanly yet. Stays `false`, experimental only. |
165
165
  | `shapeGate` | `false` | Shape gate: once a turn has accumulated at least 4 tool calls with 60 % or more of them writes (tools outside the read-only list), **no reminder is injected for the rest of that turn** and behaviour falls back to native. It only reads tool names — no request, no request-body field. It exists so a task the scheduler cannot accelerate gets left alone: write-heavy work has nothing to batch, and every reminder is a round trip it cannot reuse. An explicit `stepBudget` outranks the gate — "don't accelerate this" must not mean "ignore my stop line". Off by default, because it also suppresses the finish-line reminder that sometimes rescues a long tail. |
166
166
  | `preferPatch` | `false` | After a whole-file write, remind the model once to send only the changed lines (an edit hunk) when it edits a file that already exists. It arms on a `write` whose body is ≥ 2000 characters, fires on the following step at most once per turn, and the text asks for a byte-identical result — it does not limit what gets written or when the turn may end. Off by default: it only changes the form of the model's output (tool-call arguments), and correctness is decided by file contents, not by how they were sent. Whether it actually saves wall clock is measured by the `pp16` arm in `BENCHMARK.md`. |
167
+ | `compactRereadHint` | false | One extra sentence in the compression reminder: do not re-read whole files this turn already wrote. Experimental, off by default |
167
168
  | `workspaceMap` | `false` | One workspace inventory per conversation: before the first tool call the harness walks the session's working directory itself (depth 2, at most `mapEntries` rows, skipping `node_modules`, `.git`, `dist` and similar) and hands the model one notice with relative paths and byte sizes plus the test entry points it can see (an `npm test` script, `tests/`, pytest config, `go`/`cargo`/`make`). It replaces exactly the "what is in the directory" round trip (35.7 % of recorded calls in W8b) — it hides nothing, limits no read, and has no say in when the turn ends; the inventory is passed through unedited and which file to open stays the model's call. Off by default: it is a hint, not a proof of speed; measured by the `map15` arm in `BENCHMARK.md`. |
168
169
  | `mapEntries` | `60` | Row cap for `workspaceMap`, 10–200. The notice text has its own ~1.4 kB ceiling; when it truncates it says "first N of M" and keeps the test-entry line. |
169
170
  | `stepBudget` | `0` | Per-turn hard step ceiling. `0` = unlimited, which is the default: a step count measures cost, not whether the task is done, so nothing is asked to wrap up on a counter. Set above 0, the line sends one "report where you are" notice without blocking tools — a hard cut would land mid-edit. |
package/docs/REFERENCE.md CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  **唯一的契约:Fast 模式从不改变思考强度、thinking 预算或模型。** `agent/request` 钩子只做一件事——`await next()`,读一下请求携带的 `reasoningEffort` 记进测量,然后原样返回。用户选的是哪一档(会话里的 UI 选择,或者 profile 里 agent-default-model 固定下来的那一档),每个请求就用哪一档。
6
6
 
7
- 快的部分只来自三件事:更少的往返、更少的过程文字、更少的卡顿等待。下面逐条说明。
7
+ 快的部分只来自三件事:更少的往返、更少的冗余输出、更少的卡顿等待。下面逐条说明。
8
8
 
9
9
  ## 交互细节:闪电开关与会话归属
10
10
 
@@ -94,7 +94,7 @@
94
94
  - **扇出目标里的数字就是调度器算出的宽度。** `auto` 下提示段和看门狗共用同一个 `autoSchedule` 结果,所以模型读到的数字和被执行的数字不会漂移。
95
95
  - **预取只在宽度 ≥ 4 时出现。** 宽度不够时多取一个调用只会挤占别的工作。
96
96
  - **有界调用那一段只在 `stallGuard !== false` 时出现**,因为它描述的规则和看门狗是一套东西。
97
- - **输出纪律有两档**:`light` 给旧档位 `fast` 用,`full` 给 `auto`/`turbo`/`max`/`ludicrous` 用。两档只裁剪过程叙述——不预告调用、不复述结果、不写进度报告;最后一步的答案仍然要完整写出。`full` 只多一句"不需要文字时只发调用"。**它不会让答案变短,更不会让模型跳过检查。**
97
+ - **输出纪律有两档**:`light` 给旧档位 `fast` 用,`full` 给 `auto`/`turbo`/`max`/`ludicrous` 用。两档要求模型不预告调用、不复述结果、不写进度报告;最后一步的答案仍然要完整写出。`full` 只多一句"不需要文字时只发调用"。**它不会让答案变短,更不会让模型跳过检查。** 口径要说清:可见正文只占「正文+工具参数」字符的 1.18 %(def15 全量 341 会话;不含 reasoning 与工具返回;`python3 bench/textshare.py --limit 0` 可复算),所以这一条不是提速的主要来源——收益来自回合数与输出 token 下降;也不能由此宣称"工作内容不受影响"。
98
98
  - **`parallel: 1` 是"开着但不要求扇出"的正式写法**:开关仍然生效于看门狗,提示段整体消失。
99
99
 
100
100
  成本是明码标价的:`auto` 下这一段约 2.7 千字符,每个请求都要付这部分输入 token。这是有意的交换——用输入 token 换更少的往返。
@@ -168,6 +168,7 @@
168
168
  | `openBatch` | `false` | 开局批量:每轮第一步发一次「把已知要做的独立调用放进同一块、shell 命令用 `&&` 串起来」。短任务的钱几乎全是每步固定成本(首字延迟 ~0.5 s/次),少一次往返就省一次;只影响第一轮第一步,长任务可忽略。旧批次里它不优于默认,但那些数字已因测量缺陷撤回(运行目录在仓库内、指标按字节偏移切片),尚无干净重测。保持在 `false`,只作实验旋钮。 |
169
169
  | `shapeGate` | `false` | 形状闸门:本轮累计调用 ≥ 4 且其中写入类(不在只读名单里的工具)占比 ≥ 60 % 时,**整轮不再注入任何提醒**,行为退回原生。判断只用已有的工具名,不发请求、不加请求体字段。它给的是「不适合就别管我」的退路:写密集型的任务没有可并行的调用,每条提醒都只是一次它用不上的往返。显式设了 `stepBudget` 时该收尾线优先于闸门——「别加速」不等于「无视我叫停」。默认关,因为它会同时关掉本可以救回长尾的收尾提醒。 |
170
170
  | `preferPatch` | `false` | 整文件写入之后提醒一次「改已有文件时只发改动的行(edit hunk),不要重发整个文件」。本步 `write` 的正文 ≥ 2000 字符才武装,之后一步发一次,每轮最多一次;文本明确要求结果与原文件逐字节相同,不限制写什么、也不影响何时结束。默认关闭:它只作用于模型的输出形式(工具调用参数),正确性由文件内容决定、与发送形式无关;实测是否真省墙钟见 `BENCHMARK.md` 的 `pp16` 臂。 |
171
+ | `compactRereadHint` | false | 压缩提醒里多一句「本回合写过的文件不要为确认整份重读」;不新增消息。实验旋钮,默认关 |
171
172
  | `workspaceMap` | `false` | 每会话一次工作区清单:在第一次工具调用之前,由 harness 自己走一遍会话的工作目录(深度 2、最多 `mapEntries` 条、跳过 `node_modules`/`.git`/`dist` 等),把「相对路径 + 字节数」和它能看到的测试入口(`npm test` 脚本、`tests/`、pytest 配置、`go`/`cargo`/`make`)作为一条通知交给模型。它只替代「目录里有什么」这一轮探索(W8b 记录里这类调用占 35.7 %),不隐藏任何东西、不限制读什么、也不影响何时结束;清单一字未改地给模型,读哪个文件仍由它自己判断。默认关闭:它是一个提示,不是加速证明,实测见 `BENCHMARK.md` 的 `map15` 臂。 |
172
173
  | `mapEntries` | `60` | `workspaceMap` 的条数上限,10–200。清单正文另有 ~1.4 kB 的上限,超出时给「前 N 条,共 M 条」并保留测试入口那行。 |
173
174
  | `stepBudget` | `0` | 单轮步数硬上限。`0` = 不限,这是默认:步数只说明成本,不说明任务做没做完,所以默认不按步数催停。设成正数时,到线只发一次「按现状汇报」,不阻断工具调用——硬截断会把任务切在编辑中途。 |
package/index.js CHANGED
@@ -73,6 +73,7 @@ import {
73
73
  noteStep,
74
74
  noteTool,
75
75
  noteTurn,
76
+ toolTargets,
76
77
  } from './src/auto.js'
77
78
  import { buildConflictMessage, buildReassertMessage, createNoticeMessage } from './src/notices.js'
78
79
  import { buildWorkspaceMap } from './src/map.js'
@@ -96,7 +97,7 @@ const TAG = '[fast-mode]'
96
97
  * Shipped identity of this build. Traced into the debug log so a stale install is
97
98
  * visible at a glance, and pinned by a test against `package.json`.
98
99
  */
99
- export const VERSION = '1.5.17'
100
+ export const VERSION = '1.5.19'
100
101
  const GLOBAL_AGENT = '__global__'
101
102
 
102
103
  /**
@@ -265,7 +266,7 @@ export function apply(ctx, rawConfig) {
265
266
  if (initial.debug) {
266
267
  appendDebug(
267
268
  initial.debugFile,
268
- `apply v${VERSION} entered enabled=${initial.enabled} gear=${initial.gear} parallel=${initial.parallel} map=${String(rawConfig?.workspaceMap)} mapEntries=${String(rawConfig?.mapEntries)}`,
269
+ `apply v${VERSION} entered enabled=${initial.enabled} gear=${initial.gear} parallel=${initial.parallel} map=${String(initial.workspaceMap)} mapEntries=${String(initial.mapEntries)}`,
269
270
  )
270
271
  appendDebug(initial.debugFile, `measure v${VERSION}: ${metrics.enabled ? `file=${metrics.file}` : 'off'}`)
271
272
  traceSettings(ctx, initial)
@@ -663,9 +664,16 @@ export function apply(ctx, rawConfig) {
663
664
  }
664
665
  const argBody = args.content ?? args.text ?? args.contents ?? args.new_str ?? args.newString
665
666
  const argChars = typeof argBody === 'string' ? argBody.length : 0
667
+ // The dedupe key is sorted and truncated, so it is not evidence about
668
+ // which files a call touched. Hand the scheduler the real targets, plus
669
+ // the session directory a relative target resolves against.
670
+ const targets = toolTargets(args)
666
671
  const observation = {
667
672
  name: typeof exec.name === 'string' ? exec.name : 'unknown',
668
673
  argsKey: stableKey(exec.arguments),
674
+ paths: targets.paths,
675
+ command: targets.command,
676
+ cwd: typeof exec.agent?.session?.header?.cwd === 'string' ? exec.agent.session.header.cwd : '',
669
677
  argChars,
670
678
  isError: result?.isError === true,
671
679
  chars: contentChars(result?.content),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-fast-mode",
3
- "version": "1.5.17",
3
+ "version": "1.5.19",
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",