Repository navigation
feat(bench): slim the report, shrink the fixtures, rewrite the manual (#379) - #381
Merged
Merged
Conversation
bench/cli.mjs spawns bench/run.mjs as a child and cannot inject a callback, so the runner's requirement of an injected io.seal made every interactive sweep exit 3 before spawning anything unless --no-seal was passed. Sealing is now opt-in with --seal, and every sweep - sealed or not - keeps each arm's results/ and summary.json, which bench report renders the report from.
--seal resolves bench/run/seal.mjs (an injected io.seal still wins), fingerprints both arms by hashing \dsh --profile <name> --dump-config\ into the arms[].profileHash that sealRun requires, and refuses before any session when a fingerprint cannot be read. sealSweep adapts the seal context's {base, cand} directory map to the directory list sealRun folds; that mismatch was invisible to the injected doubles and only a real sweep could expose it.
…mplate Add bench/README-NEW.md (preparation, full and single-task sweeps, reports, every task and gate with the ops_* tools they exercise, and where temporary files land) and bench/README-mini.md (the short preparation path). Rewrite bench/llm.patch.example.yml's header in English and add the credentials-local insert the working copy needs, since the base/headless bundles ship no credentials service.
DEFAULT_REPEAT drops 3 -> 1 and every task in bench/tasks/ declares "repeat": 1, so an ab sweep without --repeat now runs each task once per arm (12 sessions, 6 interleaved pairs) instead of twice per arm. --repeat N still overrides everything; the default only covers a task with no repeat field of its own. The default-value assertion in test/bench-schedule.test.mjs follows so reverting the constant fails the suite.
…weep report The default group folder is now benchmark-YYYY-MM-DD-NN — the local calendar date, numbered from -00, incrementing past anything already present — instead of the ever-growing benchmark-test-NN. Allocation still happens exactly once per sweep, still skips any same-named entry case-insensitively, and the clock is injectable so suites assert exact names. A finished sweep now renders its own verdict through compare.mjs's renderReport and writes "DSH-OPS Benchmark Report <UTC>.md" into the group folder (or the --out directory) — the bench report artifact without a second command, re-renderable at any time. --no-report skips it; an interrupted sweep writes none, because a partial run is not a comparison; a failed write is a loud note, not a failed sweep. The ab help text and the three bench READMEs follow, along with the repeat-2-to-1 leftovers in README-NEW's task table. api-rename's corpus shrinks to one fifth: symbolTree files 60 -> 12 (seed unchanged, all five module shapes present, the look-alike-only decoys included), so the model searches, reads and edits 11 touched files instead of 51. The task's title and the README-NEW row follow.
Durations were already recorded per session; compare-core now aggregates them like steps — durationMsMedian / durationMsMin under the same --aggregate median|min, per-task delta.durationPct, and totals summed over the task medians. The per-task pairs table gains a "time base→cand (Δ%)" column (seconds, one decimal, — when unmeasured), and its Δ% is painted like every other percent. The bench docs' "never judge time" principle statements are gone, and the report footer now says time is reported like any other metric. The gate itself is unchanged: it judges pass rate and paired tokens, and duration stays out of every verdict — the new core assertions pin that gate outcomes do not move with duration.
README-NEW.md and README-mini.md are gone: bench/README.md now carries the short preparation-and-usage guide on its own. No script, manifest or doc referenced the two removed files.
…air-loop
The generated report's heading is now "# DSH-OPS Benchmark Report (beta)";
the file name keeps its old shape ("DSH-OPS Benchmark Report <UTC>.md"),
which the report subcommand, the runner help and the bench README all say.
Every rendered "cand" label becomes "ops" — the first table, the (ops only)
marker, the tool table, the three diagnostic headers and the Evidence line —
matching the bench-ops arm label. JSON keys, CLI flags and warning codes are
unchanged: they are the machine contract.
gate-repair-loop's pipeline is 12 stages instead of 24; every stage is still
broken, so the repair loop halves with it. Seed, probes and linesPerGate are
untouched, and the fixture regenerates with all invariants intact.
test/bench-cli drops the README ↔ run.schema.json cross-check — the bench
docs are now one short README — and keeps the schema self-checks.
The report gains "Per-request overhead (what every request costs before any work)": one first-request cell per task (base → ops with the absolute delta and a painted Δ%). Real sweeps never recorded the data it needs, so the session fold now derives metrics.prompt exactly as the aggregator defines it — per-step prompt = input + cacheRead + cacheWrite, the first request set only by step 1, per-turn firsts, reported all-or-nothing — and new sweeps light the table up while archives without the data render the section's Skipped note instead of inventing numbers. Everything else got smaller: Warnings, Conclusion, Evidence, the steps and tool calls table, the usage split table and the closing footer are gone, so the report is four data tables and nothing else. The gate's verdict still travels through the exit code and the comparison document, unchanged. Section headings lose their parenthetical qualifiers (Tool calls, Per-task bucket detail), per-column headers read "steps: base→ops (Δ%)" to separate the metric from the direction, and renderReport drops its sources parameter, which only the removed Evidence ever used.
bench/README.md gains a task table: fixture size, what each task measures (symbol rename vs decoys, paged reads against stale-comment bait, layered config priorities, shell-side aggregation, the run-fix-rerun repair loop, exact log extraction) and which ops_* tool each step maps to. The numbers match the current fixtures — api-rename 12 modules and gate-repair-loop 12 stages included — and the expectations are the generators', recomputed per run.
The bench README becomes the Benchmark Manual: setup says what it prepares (the two headless profiles and the locally built plugin), the LLM overlay section is reworded, and the group-folder naming plus the auto-written report move into their own "结果在哪看" section.
This was referenced Oct 24, 2026
T-Auto
approved these changes
Oct 25, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #379
What was wrong
优化后的 Benchmark Report:
继续加强可读性
增加任务时间对比
优化任务和流程,自动生成 MD 格式报告(目前执行一次全量 Benchmark,时间在 10 分钟以内)
默认太贵:
node bench/cli.mjs ab默认每个任务重复 2 次,且封存run.json才是默认态。多数时候只想要一份能读的报告,却先付了 2 倍的 token 与时间。报告把自己当叙事文档:四张数据表之外还挂着 Warnings / Conclusion / Evidence / steps-and-tool-calls 表 / 用量拆分表 / footer,读者要自己分辨哪段是数据、哪段是叙述;同一份判定在正文里又重复一遍。
读不出固定成本与墙钟:只有 paired tokens / steps / tool calls,没有「每个任务花多久」,也没有「每次请求在干活之前先付多少」——而后者正是 DSH 0.2.0-rc.2 · dsh-ops 0.3.1 Linux x64(WSL2) 实测:PTC 批处理在 PTC-only 会话里省 token 为零,启用反而每请求 +9,462 字符固定成本(DeepSeek-v4.1flash 实测后由它所写) #148 / [Benchmark] Native DSH vs dsh-ops: -17.4% uncached+output tokens, +27.5% elapsed time #292 争论的那一项。
cand字样仍在渲染输出里:[Benchmark] 计划:增强报告可读性,增加 steps 和 tool calls 的差异显示,增加更多测试用例 #262 第 3 点要求改成ops,PR [Benchmark] 增加 Benchmark Report 的可读性,修复 LLM 配置项问题 (#262) #321 关闭了该 issue,但main的列头仍是cand (Δ%)、任务后缀仍是(cand only)。文档层级不对:
bench/README.md是 197 行的实现说明(三种入口形态、退出码、成本口径、五个子命令、run.json契约、接线现状……),操作者真正要知道的「怎么准备、怎么跑、结果在哪看」得自己从里面拼。夹具偏大:
api-rename60 个模块、gate-repair-loop24 个 stage(每个约 70 行),单次 sweep 的 token 与时间集中在少数任务上。The change
bench/的操作面与文档一次收口,判定口径一行未动(pass-drop 先行、只比配对成本、A/A 噪声底、不裁时间、--fail-on全部保持)。bench/run.mjs:封存从默认改为显式--seal(--no-seal保留为兼容开关);算出对比后调用renderReport把报告写进组目录,新增--no-report关闭,单臂 / 中断 / 无对比时显式记 skipped,而不是写一个看起来完整的文件;ab的 usage 不再列3 sealing is not wired yet(EXIT.pending仍留给未落地的切片)。bench/lib/next-folder.mjs、bench/cli.mjs:默认组目录benchmark-test-NN→benchmark-YYYY-MM-DD-NN(本机日历日期、当天从-00起、跳过任何已存在的名字),planAb接受注入时钟,套件可断言确切名字。bench/tasks/*.task.json:六个任务repeat: 2 → 1;gate-repair-loop24 → 12 个 stage;api-rename60 → 12 个模块(生成器参数、不变量与判卷不变)。bench/compare.mjs、bench/lib/compare-core.mjs、bench/run/session.mjs:标题# DSH-OPS Benchmark Report (beta);cand字样退出渲染输出(列头ops (Δ%)、steps: base→ops (Δ%)、(ops only)、Tool calls 表的ops列);首表加time: base→ops (Δ%)墙钟列(沿用--aggregate median|min的durationMsMedian/durationMsMin,TOTAL 按时长求和);新增## Per-request overhead (what every request costs before any work),口径是新算的metrics.prompt(每步 prompt = input + cacheRead + cacheWrite,首个请求只由 step 1 决定、按 turn 取首),全有才报,旧归档缺数据时打印 Skipped;删掉 Warnings / Conclusion / Evidence / steps-and-tool-calls 表 / 用量拆分表 / footer,renderReport的sources参数随之删除。bench/README.md、bench/llm.patch.example.yml:README 197 → 44 行,收敛成中文「Benchmark Manual」(准备 → 怎么跑 → 结果在哪看 → 任务一览),overlay 模板同步改写。test/bench-{schedule,run-session,compare-core,compare,cli}.test.mjs:同步更新,新增断言 267 行(实现侧非测试插入 397 行)。刻意没做:不动
run.json的字段名与 schema 结构;不碰setup/profile.mjs的组件行;不做多模型 / 跨平台 / 开关矩阵(#294 / #295 / #296);不把 bench 放进 CI 或门禁(#205 边界)。一处有意的收窄:test/bench-cli里「README ↔run.schema.json交叉检查」随 README 重写被删除(只留 schema 自检),理由与替代做法写在 #379 的「需要评审确认」一节。默认不写run.json之后,要可比证据得显式--seal——这一点也记在同一节。Validation
本 PR 有一次
pull_request事件触发的 run:https://github.com/T-Auto/dsh-ops/actions/runs/40086100093npm run verify的实际输出(含 passed / skipped / failed 计数)撤掉本次实现后相关检查会变红(见下方红绿读数)
docs/CHANGELOG.md的## [Unreleased]未加条目:本 PR 只改bench/(仓库内测量工具,不随包发布、不参与构建、不进verify:release,见 [设计] 合并 173/103/183 为一套 bench + 数据面板(数据只存 JSON;测试期可用、构建期不构建) #205 的边界),不改随包行为与发行物文档;同一区域的 PR [Benchmark] 增加 Benchmark Report 的可读性,修复 LLM 配置项问题 (#262) #321(报告可读性,[Benchmark] 计划:增强报告可读性,增加 steps 和 tool calls 的差异显示,增加更多测试用例 #262)14 个文件同样没有动 CHANGELOG行为变化已同步
bench/README.md(跑法、结果位置、任务一览)未新增依赖,未改
package.json的版本与optionalDependencies未改面向模型的描述或参数:本 PR 不触碰
src/,schema 与提示词字节零变化Environment
Windows 11 (10.0.26100) x64;Node v24.11.0;npm 11.12.1
基于上游
mainb963fb2(fix(policy): name the shell tool by its published name (#306))安装通道:本地 checkout,
npm ci --ignore-scripts;lib/与 native worker 由npm run build、npm run build:ptc本地构建