Repository navigation
Releases
v0.3.16
安装 · Install
npm install -g github:wuwaka/dsh-rehearsal#v0.3.16也可锁定到字节 / or pinned to bytes: npm install -g https://github.com/wuwaka/dsh-rehearsal/releases/download/v0.3.16/dsh-rehearsal-0.3.16.tgz
(用本 Release 附带的 dsh-rehearsal-0.3.16.tgz.sha256 校验 / verify with the attached .sha256。)
变更
- npm 发布通道迁移到 OIDC trusted publishing:
publish.yml不再读取NPM_TOKEN。npm 已开始限制 bypass-2FA 的 granular token(发布时的官方提示原文:"npm tokens that bypass 2FA are being restricted for account changes and direct publishing"),该 secret 属于被限类别。发布改由 GitHub Actions 的 OIDC 身份直接授权,npm 包侧把wuwaka/dsh-rehearsal的publish.yml登记为受信发布方;依赖该 secret 的 gate 前置任务一并移除,publish 前升级 npm 以满足 OIDC 客户端要求(≥ 11.5.1)。NPM_TOKENsecret 保留作回退通道——恢复 publish 步骤的NODE_AUTH_TOKEN环境变量即可退回。
Changed
- The npm publishing channel moves to OIDC trusted publishing:
publish.ymlno longer readsNPM_TOKEN. npm has started restricting bypass-2FA granular tokens (the official notice, verbatim: "npm tokens that bypass 2FA are being restricted for account changes and direct publishing"), and that secret is in the restricted class. Publishing is now authorized directly by GitHub Actions' OIDC identity, withwuwaka/dsh-rehearsal'spublish.ymlregistered as the trusted publisher on the npm package; the gate job that depended on the secret goes with it, and npm is upgraded before publish to satisfy the OIDC client requirement (>= 11.5.1). TheNPM_TOKENsecret stays as a fallback — restoring theNODE_AUTH_TOKENenv on the publish step reverts to the old channel.
完整历史 / Full changelog: v0.3.15...v0.3.16
v0.3.15
安装 · Install
npm install -g github:wuwaka/dsh-rehearsal#v0.3.15也可锁定到字节 / or pinned to bytes: npm install -g https://github.com/wuwaka/dsh-rehearsal/releases/download/v0.3.15/dsh-rehearsal-0.3.15.tgz
(用本 Release 附带的 dsh-rehearsal-0.3.15.tgz.sha256 校验 / verify with the attached .sha256。)
变更
- npm 包内文档与仓库对齐:README 安装 pin 更新到
v0.3.15(0.3.14包内的 pin 停在v0.3.13,照抄会把 GitHub tag 安装指到旧版本),安全摘要补上 URL userinfo 凭据剥离;docs/architecture双语在写回合抑制第 2 层记录--dump-configfail-closed 前置校验。CHANGELOG 底部链接补齐0.3.13/0.3.14/0.3.15三个缺失定义,[Unreleased]compare 区间重置。本版不含代码变更——发布它就是为了这件事。
Changed
- In-package docs catch up with the repository: the README install pin moves to
v0.3.15(the0.3.14package still pinnedv0.3.13, pointing tag installs at an older release), the security summary gains the URL-userinfo credential line, and the architecture docs record the--dump-configfail-closed pre-check in suppression layer 2. The changelog's bottom link definitions gain the missing0.3.13/0.3.14/0.3.15entries and the[Unreleased]compare range resets. No code changes in this release — it exists to make that true.
完整历史 / Full changelog: v0.3.14...v0.3.15
v0.3.14
安装 · Install
npm install -g github:wuwaka/dsh-rehearsal#v0.3.14也可锁定到字节 / or pinned to bytes: npm install -g https://github.com/wuwaka/dsh-rehearsal/releases/download/v0.3.14/dsh-rehearsal-0.3.14.tgz
(用本 Release 附带的 dsh-rehearsal-0.3.14.tgz.sha256 校验 / verify with the attached .sha256。)
修复
run期间收到 SIGINT/SIGTERM/SIGBREAK/SIGHUP 不再遗留影子目录:清理此前只依赖finally块,而阻塞型spawnSync期间到达的信号会直接终止进程——明文会话副本留在磁盘上,正是 SECURITY.md『每条退出路径都会删除』的保证没有覆盖到的那一条。现在信号处理器先删除影子 home(与安装前缀)再退出;此外每次run启动时按 ownership 标记清扫遗留的dsh-rehearsal-home-*目录(名字相似从不清删,只认本工具的标记)。- 写回合对工具抑制改为 fail-closed(
src/lib/shadow.js):writeReplayPatch此前不检查--dump-config是否成功,抑制 0 行只体现为一行日志。dump 命令失败、输出解析不出任何行、或请求抑制却匹配到 0 个执行器行时,写路径直接中止——阶段详情报告replay patch fail-closed (<原因>),不再带着未经验证的执行器继续;coverage.writeRounds.toolSuppression记录校验结果。 - 写入补丁的每个 id 都经过
JSON.stringify——disable 行此前裸拼 id,与 P2-7 起就对会话派生行加引号的规则不一致。 - 环境剥除覆盖 URL 内嵌凭据:
http://user:token@host/形态的代理与 registry 值此前能穿过名字过滤原样传给子进程。名字过滤保留的值现在会剥掉 userinfo 部分。名字过滤的"误删"方向(AUTH同时匹配XAUTHORITY)有意保留——错误方向是给孩子更少,绝不更多;SECURITY.md 的保证措辞已改为与实现精确一致。
变更
release.yml的 tag 改经环境变量传入 shell,不再用${{ }}插值;三个 workflow 的actions/checkout与actions/setup-node全部按 commit SHA 锁定,persist-credentials: false让用不到 git 凭据的 job 不再持有驻留凭据。test/fixtures/新增 README,说明两个.asar是手工构造、仅包裹事实性包清单(名称与版本号)的容器,不含官方应用的任何代码、二进制或素材——再分发疑虑就此了结。回归锁:测试数 129 → 140。
Fixed
- SIGINT/SIGTERM/SIGBREAK/SIGHUP during
runno longer leave the shadow home behind: cleanup used to live only in afinallyblock, which a signal arriving during a blockingspawnSyncoutflies — the process died with the plaintext session copies still on disk, the one exit path SECURITY.md's every-exit-path-deletes guarantee did not cover. The signal handler now removes the shadow home (and the prefix) before exiting; in addition, everyrunsweeps leftoverdsh-rehearsal-home-*directories at startup, gated on this tool's ownership marker (a name that merely looks ours never authorises a delete). - The write round is fail-closed about tool suppression (
src/lib/shadow.js):writeReplayPatchused to parse--dump-configoutput without checking that the command succeeded, and zero suppressed rows was visible only as a log line. A failed dump, a dump that parses to no rows, or a suppression pass matching zero executor rows now aborts the write path — the stage details reportreplay patch fail-closed (<reason>)instead of continuing with unverified executors;coverage.writeRounds.toolSuppressionrecords the outcome. - Every id written into the replay patch is JSON.stringify'd — disable rows previously interpolated the id bare, inconsistent with the session-derived rows quoted since P2-7.
- Environment scrubbing covers URL-embedded credentials: a proxy or registry value like
http://user:token@host/used to pass the name filter untouched and reach the child. Values that survive the name filter now have their userinfo stripped. The name filter's over-removal side (AUTHalso matchesXAUTHORITY) is kept deliberately — the error direction is giving a child less, never more — and SECURITY.md's guarantee now states exactly what is and is not covered.
Changed
release.ymlpasses the tag into the shell through the environment instead of${{ }}interpolation; all three workflows pinactions/checkoutandactions/setup-nodeby commit SHA, andpersist-credentials: falsekeeps git credentials from standing in jobs that never need them.test/fixtures/gains a README stating that the two.asararchives are hand-built containers around a factual package manifest (names and versions), holding no code, binary or artwork from the official application — settling the redistribution question. Regression locks: tests 129 → 140.
完整历史 / Full changelog: v0.3.13...v0.3.14
v0.3.13
安装 · Install
npm install -g github:wuwaka/dsh-rehearsal#v0.3.13也可锁定到字节 / or pinned to bytes: npm install -g https://github.com/wuwaka/dsh-rehearsal/releases/download/v0.3.13/dsh-rehearsal-0.3.13.tgz
(用本 Release 附带的 dsh-rehearsal-0.3.13.tgz.sha256 校验 / verify with the attached .sha256。)
修复
- 候选安装的 npm 过程改在剥除凭据的环境下运行:开启
--run-scripts时,依赖生命周期脚本此前继承完整宿主环境——授予脚本执行的逃生门顺带重新暴露了宿主凭据。现在与候选运行同规则剥离全部凭据形状变量。 - CLI 旗标统一到一份契约(
src/lib/args.js):每个--dashed-flag同时以连字符与 camelCase 两种拼写存储——--shadow-dir、--prefix-dir与--skip-write从命令行传入时此前被静默忽略(解析器存一种拼写、命令代码读另一种)。开关类旗标(--run-scripts、--allow-tools、--keep、--skip-write、--yes、--full)显式接受=true/=false,其余取值直接报错,且不再吞掉下一个参数——--yes=false不再被读作"已确认"。 clean的无标记兼容路径改为结构判定:.dsh-rehearsal布局只有在每个生成子目录携带自己的报告(report.json/report.md)或被中断运行为空目录时才可清理。
变更
- README 中指向 npm 包外维护者文档(PUBLISHING、AUDIT)的相对链接改为 GitHub 绝对地址,随包 README 不再有失效相对链接。
- 生产代码注释的最后一批评审/日期残留清除:注释只陈述"为什么必须如此",审计轨迹留在 AUDIT.md 与本文件。回归锁:测试数 125 → 129。
Fixed
- The npm install of the candidate now runs under a credential-stripped environment: with
--run-scripts, dependency lifecycle scripts used to inherit the full parent environment, so the escape hatch that grants script execution also re-exposed host secrets. It now strips every credential-shaped variable, same rule as the candidate's own runs. - CLI flags parse under one contract (
src/lib/args.js): every--dashed-flagis stored under both its dashed and camelCase spelling —--shadow-dir,--prefix-dirand--skip-writefrom the command line used to be silently ignored because the parser stored one spelling while the command code read the other. Switches (--run-scripts,--allow-tools,--keep,--skip-write,--yes,--full) accept=true/=falseexplicitly, reject any other value, and never consume the next argument —--yes=falseno longer reads as "confirmed". clean's pre-marker fallback now checks structure, not only naming: a.dsh-rehearsallayout is cleanable only when each generated child carries its report (report.json/report.md) or is an interrupted run's empty directory.
Changed
- README relative links that pointed at maintainer documents outside the npm tarball (PUBLISHING, AUDIT) are absolute GitHub URLs, so the shipped README has no dead relative links.
- The last review/date residue in production comments is gone; comments state why a rule holds, the audit trail stays in AUDIT.md and this changelog. Regression locks: test count 125 → 129.
完整历史 / Full changelog: v0.3.12...v0.3.13
v0.3.12
安装 · Install
npm install -g github:wuwaka/dsh-rehearsal#v0.3.12也可锁定到字节 / or pinned to bytes: npm install -g https://github.com/wuwaka/dsh-rehearsal/releases/download/v0.3.12/dsh-rehearsal-0.3.12.tgz
(用本 Release 附带的 dsh-rehearsal-0.3.12.tgz.sha256 校验 / verify with the attached .sha256。)
修复
- 版本参数在命令边界校验(
--candidate/--current/--to):实测check --candidate nonsense曾以 exit 0、upgrade-ok收场——比较根本没执行,结论从零拼出;现在非法版本直接报错(exit 3),且发生在任何安装与产物写入之前。 clean增加 ownership 与危险路径守卫:只删除带本工具标记的产物目录(或旧版默认布局);任意目录、HOME、工作目录、文件系统根即便带--yes也一律拒绝。- profile-local 运行时标记与桌面描述符同规则三态:存在但不可用(损坏、非 semver)记
untrusted、current 保持未知——不再回落到机器级 bundle 以另一套安装的版本作答。 --writeRounds与--preset-mode严格校验(正整数 /skip|patch):非数字预算曾变成 NaN 使上限失效、写回合无界执行;未知模式值曾静默落入默认。- 脱敏 invariant 结构化:
privacy.redactionGapCount记录漏网数,扫描覆盖全部字段(含 warnings),gap 警告不再回显泄漏的 token 本身。 - 单段 POSIX 绝对路径(
/tmp、/etc)纳入脱敏与 invariant 检查;“任意 POSIX 绝对路径”现在字面成立。 --shadow-dir/--prefix-dir写入前需 ownership:空目录接管并标记,非空且无标记的目录拒绝使用。
新增
- npm tarball 边界测试(
test/pack.test.js):用户文档随包(SECURITY 双语在内),维护者文档(AUDIT、PUBLISHING、内部方案)被明确排除。 dsh-rehearsal --help现在列出clean命令及其 ownership 行为。
变更
- 生产代码注释改为陈述“为什么必须如此”,不再记录改动出自哪一轮评审;审计轨迹留在 AUDIT.md 与本文件。回归锁:测试数 117 → 125。
Fixed
- Version-valued options are validated at the command boundary (
--candidate,--current,--to): measured,check --candidate nonsenseused to exit 0 withupgrade-ok— the comparison never ran and the verdict was assembled from nothing. An invalid version now fails with a readable error (exit 3) before any install or artifact write. cleangained ownership and dangerous-path guards: it removes only artifact directories bearing this tool's marker (or the pre-marker default layout); arbitrary directories, the home, the working directory and filesystem roots are refused even with--yes.- Profile-local runtime markers follow the same three-state rule as desktop descriptors: a marker that exists but is unusable (corrupt, non-semver) resolves to
untrustedand current stays unknown — it no longer falls through to a machine-level bundle answering with another install's version. --writeRoundsand--preset-modeare validated (positive integer /skip|patch): a non-numeric budget used to become NaN, which disabled the cap and let the write round run unbounded; an unknown mode used to fall through to the default silently.- The redaction invariant is structured:
privacy.redactionGapCountrecords the surviving-token count, the scan now covers every field (warnings included), and the gap warning no longer echoes the leaked tokens themselves. - Single-segment POSIX absolutes (
/tmp,/etc) are scrubbed and invariant-checked; "any POSIX absolute path" now holds literally. --shadow-dir/--prefix-dirrequire ownership before anything is written: empty directories are adopted and marked, non-empty unmarked directories are refused.
Added
- An npm-tarball boundary test (
test/pack.test.js): user documentation ships with the package (SECURITY included, both languages), maintainer documents (AUDIT, PUBLISHING, internal plans) are pinned out. dsh-rehearsal --helpnow lists thecleancommand and its ownership behaviour.
Changed
- Production comments state why a rule must hold instead of which review round introduced it; the audit trail lives in AUDIT.md and this changelog. Regression locks: test count 117 → 125.
完整历史 / Full changelog: v0.3.11...v0.3.12
v0.3.11
安装 · Install
npm install -g github:wuwaka/dsh-rehearsal#v0.3.11也可锁定到字节 / or pinned to bytes: npm install -g https://github.com/wuwaka/dsh-rehearsal/releases/download/v0.3.11/dsh-rehearsal-0.3.11.tgz
(用本 Release 附带的 dsh-rehearsal-0.3.11.tgz.sha256 校验 / verify with the attached .sha256。)
修复
- 报告脱敏收口(评审第五轮 P1-1/P1-2):home 路径规则此前在空格处截断,
C:\Users\John Smith\.dsh会漏出姓氏,干净 home 路径也会留下整段尾巴(~\AppData\Local\Temp\run-1,均实测);现在 home 字段按字段边界整段归一(干净路径仍归一为~,含空格的字段升级为<abs-path>)。writeReport()同时改为 fail-closed:invariant 仍发现 path-shaped 文本时拒绝写盘(此前先警告再照写,与"落盘前已脱敏"的承诺相矛盾)。 - 运行时证据补全(P2-1):描述符存在但校验不过的
untrusted标记进入b1证据的currentRuntime.untrusted与coverage.host.runtimeUntrusted,"current 为什么是 null"可诊断。 - 非字符串 peer 范围(
null、数字、对象)产出peer-range-unparseable警告,不再被读作"无约束"(P2-2);真缺席(undefined、空白串)语义不变。 - 收紧:
looksLikeDshHome要求dsh.profile.bundles数组(P2-5);candidate 版本不可读时 adoption-gate 拒绝打补丁而不是猜测备份基准(P2-4);README、--help与 SECURITY 写明--shadow-dir/--prefix-dir不被自动清理、也不归clean管(P2-3)。 - 上述每项修复都带回归锁(测试数 114 → 117):含空格 home 的尾巴、fail-closed 拒写、untrusted 进入报告、带类型的 peer 范围、版本未知时的闸门。
Fixed
- Report redaction closes its last two gaps (review round 5, P1-1/P1-2): the home-path rules stopped at whitespace, so
C:\Users\John Smith\.dshleaked the surname and even an unspaced home kept everything after it (~\AppData\Local\Temp\run-1— both measured); home fields now consume to the field boundary with no tail (a clean home still normalises to~, a spaced field escalates to<abs-path>). AndwriteReport()is now fail-closed: when the invariant still finds path-shaped text it REFUSES to write (the earlier behaviour warned and wrote the leak anyway, contradicting the sanitised-before-disk promise). - Untrusted runtime markers reach the report (P2-1): a descriptor that exists but fails validation is listed under
currentRuntime.untrustedin theb1evidence andcoverage.host.runtimeUntrusted, so a nullcurrentexplains itself. - Peer ranges that are not strings (
null, numbers, objects) produce thepeer-range-unparseablefinding instead of reading as "no constraint" (P2-2); truly absent ranges (undefined, blank strings) stay unconstrained. - Hardening:
looksLikeDshHomerequires thedsh.profile.bundlesarray (P2-5); the adoption-gate patch refuses to run when the candidate version is unreadable rather than guessing which backup is valid (P2-4); README,--helpand SECURITY spell out that--shadow-dir/--prefix-dirare never auto-cleaned and are not covered byclean(P2-3). - Regression locks for each item above (test count 114 → 117): spaced-home tails, the fail-closed refusal, untrusted propagation into the report, typed peer ranges, and the unknown-version gate.
完整历史 / Full changelog: v0.3.1...v0.3.11
v0.3.1
安装 · Install
npm install -g github:wuwaka/dsh-rehearsal#v0.3.1也可锁定到字节 / or pinned to bytes: npm install -g https://github.com/wuwaka/dsh-rehearsal/releases/download/v0.3.1/dsh-rehearsal-0.3.1.tgz
(用本 Release 附带的 dsh-rehearsal-0.3.1.tgz.sha256 校验 / verify with the attached .sha256。)
修复
- 桌面 bundle 探针的平台过滤失效:平台门读取的是不含该字段的对象,任何平台上都会探测全部宿主的安装路径(macOS 上会先试 Windows 路径)。现在按条目自身的平台过滤,并有"darwin 下不得泄漏 win32 路径"的回归测试。
- 官方 macOS 探针多拼一层
resources(Contents/Resources/resources/app.asar),真实布局永远探不到,macOS 用户稳定得到current = null与保守高的噪音。探针改为按平台声明档位置(archive),含 darwin 命中与"双拼必须 miss"两组测试。 - 官方回退探针(档内
dsh/node_modules/@deepseek-ai/dsh/package.json)是死代码:所有 asar 探针都被当描述符解析。探针新增kind:描述符缺失时回退到档内 package.json;描述符存在但校验不过时保持未知并记入untrusted——此处回退会绕过描述符的完整性交叉校验。 writeReport()不再把privacy.scrubbed当跳过令牌:每次落盘都重新finalize()。实测缺口:finalize 之后被追加的字段会原样入库,而脱敏标志仍声称干净。- 非法 peer 范围不再读作"两侧都不满足"的既有失配:新增
peer-range-unparseable警告发现项说明真实原因;satisfies 包装的兜底分支返回 false 而不是 true。 --home=(空值)报可读错误,不再静默落到自动发现;home 门禁要求 profile manifest 带 harness 的dsh字段(无关项目的 package.json 不再算数);--prefix-dir跨 candidate 复用时 adoption-gate 备份按版本重播种;npm 前缀探测的契约(仅npm_config_prefix存在时生效)写进代码与测试。- 上述每项修复都带回归锁(测试数 106 → 114):跨平台探针泄漏、darwin 命中与双拼 miss、描述符缺席回退与不可信不回退、finalize 后追加字段、非法 peer 范围、空
--home。
Fixed
- Desktop bundle probes were not platform-filtered: the gate read a
platformfield off an object that does not carry one, so every platform probed every host's install paths (on macOS the Windows paths would be attempted first). The gate now filters catalog entries themselves, with a regression test that fails if win32 roots leak under a darwin platform. - The official macOS probe joined an extra
resourcessegment (Contents/Resources/resources/app.asar) and could never hit the realContents/Resources/app.asarlayout, leaving macOS users withcurrent = nulland conservative-high noise. Probe entries now declare their archive segments per platform, with darwin hit and doubled-path miss tests. - The official fallback probe (
dsh/node_modules/@deepseek-ai/dsh/package.jsoninside the archive) was dead code: every asar probe was parsed as a descriptor. Probes now declare akind: a missing descriptor falls back to the in-archive package.json, while a descriptor that exists but fails validation stays unknown and is listed underuntrusted— falling back there would bypass the descriptor's integrity cross-check. writeReport()no longer treatsprivacy.scrubbedas a skip token: it re-runsfinalize()on every write. Measured gap: a field appended after a previous finalize reached disk unscrubbed while the flag still claimed clean.- Unparseable peer ranges no longer read as a settled "excluded by both versions": a dedicated
peer-range-unparseablewarn finding says what is actually wrong, and the satisfies wrapper returns false on its belt-and-braces path instead of true. --home=(blank) now fails with a readable error instead of silently falling through to auto-discovery; the home gate requires the harnessdshfield in a profile manifest (an unrelated project'spackage.jsonno longer qualifies); the adoption-gate backup re-seeds when a reused--prefix-dirgets a different candidate version; the npm-prefix probe's contract (only whennpm_config_prefixis present) is stated in code and tests.- Regression locks for each fix above (test count 106 → 114): cross-platform probe leakage, the darwin hit and the doubled-path miss, descriptor-absent fallback versus untrusted no-fallback, post-finalize mutation, unparseable peer ranges, and a blank
--home.
完整历史 / Full changelog: v0.3.0...v0.3.1
v0.3.0
安装 · Install
npm install -g github:wuwaka/dsh-rehearsal#v0.3.0也可锁定到字节 / or pinned to bytes: npm install -g https://github.com/wuwaka/dsh-rehearsal/releases/download/v0.3.0/dsh-rehearsal-0.3.0.tgz
(用本 Release 附带的 dsh-rehearsal-0.3.0.tgz.sha256 校验 / verify with the attached .sha256。)
新增
- 桌面宿主兼容:
check与run无需--home即可发现官方 DeepSeek Harness Desktop 与社区桌面(anywhere-labsDSH Desktop、dataelementDSHDesktop)的 home——--home与DSH_HOME缺省、默认 home 不存在或不是真实数据时,按登记顺序逐宿主探测并验证(必须存在 profile manifest 或真实会话日志);报告以target.homeOrigin标签记录来源,不写路径。 - 版本探测走探测表:官方运行时整树在
resources/app.asar档内,由零依赖只读读取器读取dsh/desktop-runtime.json(sharedPackages为数组,取@deepseek-ai/dsh条目并与release.version交叉校验);桌面来源的 home 只信自己宿主的内置运行时,共享 home 上多宿主版本冲突返回 null 并以currentAmbiguous列出各宿主的版本,npm 全局前缀只在其他来源全部落空时参与。 - 桌面来源的
run预演附带范围警告与coverage.host(desktopRuntimeTested: false):判定覆盖 npm 依赖闭包与会话数据格式,不覆盖桌面应用自身的更新通道;纯桌面歧义(无有效默认 home 且 ≥2 个桌面 home 验证通过)时run要求显式--home,check全自动。 - 发现、探测与范围判定附带 25 条回归锁:从探测表顺序漂移锁定、真实工具生成的 asar 夹具,到双宿主版本冲突用例(测试数 81 → 106)。
Added
- Desktop-host compatibility:
checkandrundiscover the homes of the official DeepSeek Harness Desktop and community desktops (anywhere-labsDSH Desktop, dataelementDSHDesktop) without--home— when neither--homenorDSH_HOMEis set and the default home is absent or not a real home, catalog candidates are probed and validated in registered order (a profile manifest or real session logs must exist); reports record the origin as atarget.homeOriginlabel, never a path. - Version detection goes through the catalog: the official runtime lives inside
resources/app.asar, read by a zero-dependency read-only asar reader fromdsh/desktop-runtime.json(thesharedPackagesarray is searched for@deepseek-ai/dshand cross-checked againstrelease.version); a desktop-origin home trusts only its own host's bundle, conflicting bundled versions on a shared home resolve to null with the contenders listed ascurrentAmbiguous, and the npm global prefix only fills in when every other source misses. - Desktop-sourced
runrehearsals carry a scoping warning and acoverage.hostblock (desktopRuntimeTested: false): verdicts cover the npm dependency closure and the session data format, not the desktop app's own update channel; with pure-desktop ambiguity (no valid default home and ≥2 validating desktop homes)rundemands an explicit--home, whilecheckproceeds automatically. - Discovery, probing and scoping come with 25 regression locks, from the catalog order drift-lock to a real-tool asar fixture and a dual-host version-conflict case (test count 81 → 106).
完整历史 / Full changelog: v0.2.2...v0.3.0
v0.2.2
安装 · Install
npm install -g github:wuwaka/dsh-rehearsal#v0.2.2也可锁定到字节 / or pinned to bytes: npm install -g https://github.com/wuwaka/dsh-rehearsal/releases/download/v0.2.2/dsh-rehearsal-0.2.2.tgz
(用本 Release 附带的 dsh-rehearsal-0.2.2.tgz.sha256 校验 / verify with the attached .sha256。)
文档、徽章与安装元数据,外加一处内部常量收敛。check 与 run 的行为没有任何改动。
变更
- 假设的模型上下文窗口
256000从两处魔数(extractRoutes造默认模型条目、影子 settings 序列化的兜底)收敛为util.js的命名常量ASSUMED_CONTEXT_WINDOW_TOKENS,并加回归锁:该字面量只允许出现在常量定义处(测试数 80 → 81)。 SECURITY.md补充工具抑制名单(TOOL_ROW_NAME_PREFIXES)的失效方向:名单缺了新工具家族只会让抑制不完整,不构成放行路径——写回合只回放会话录制中出现且全部通过只读白名单的调用,--allow-tools默认关闭仍是最外层闸门。引用断言同步加入文档测试。- README 首屏正面陈述核心差异:迁移成功不等于写回(含"读侧静态检查无法判定该类损坏"的依据,
及七个同类工具逐项对照的链接);新增「为什么是独立 CLI,而不是插件」;使用场景由反问改为直陈;
删除硬编码的测试数;report与clean两个命令显名。 SECURITY.md同一边界只说一次:删除重复段落与拟人表述。PUBLISHING.md收敛为发布操作手册,
维护者知识(目录投稿调查、仓库图片、文档治理、新增签名的步骤)移入新增的docs/MAINTAINING.md;
docs/FAILURE_MODES.md的「新增一条签名」一并移入,正文收敛为纯 reference。- README 安装节增加 npm registry 路径(
dsh-rehearsal@0.2.1已于 2026-10-03 发布);徽章区新增
npm 版本徽章与listed in awesome-deepseek-harness收录徽章(Dominic789654/awesome-deepseek-harness#579已合并)。 - npm
keywords移除dsh-plugin,与 GitHub topics 的口径对齐(生态反爬虫清单会剔除蹭该标签的非插件)。
Documentation, badges and install metadata, plus one internal constant consolidation. The behaviour of check and run is unchanged.
Changed
- The assumed model context window
256000is consolidated from two magic literals (the default entry inextractRoutesand the fallback in the shadow settings serializer) into one named constant,ASSUMED_CONTEXT_WINDOW_TOKENSinutil.js, with a regression lock: the literal is allowed only at the constant's definition (test count 80 → 81). SECURITY.mdnow states the failure direction of the tool-suppression list (TOOL_ROW_NAME_PREFIXES): a list missing a new tool family leaves suppression incomplete but opens no execution path — the write round only replays tool calls that appear in the session's own recording and all passed the read-only allowlist, and--allow-tools(off by default) remains the outermost gate. A citation assertion backs it in the docs test.- The README states its differentiators up front: a successful migration does not prove write-back (with the reason read-side static checks cannot decide this class, and a link to the seven-tool comparison); a new "why a standalone CLI" section; use-case questions rewritten as statements; the hard-coded test count removed;
reportandcleannamed. SECURITY.mdstates each boundary once: duplicate paragraphs and anthropomorphic phrasing removed.PUBLISHING.mdis narrowed to the release runbook, with maintainer knowledge (the catalogue investigation, repository images, documentation governance, the add-a-signature procedure) moved into the newdocs/MAINTAINING.md; the procedure moved out ofdocs/FAILURE_MODES.md, which is now pure reference.- The install section gains the npm registry path (
dsh-rehearsal@0.2.1was published on 2026-10-03); the badge row gains the npm version badge and alisted in awesome-deepseek-harnessbadge (Dominic789654/awesome-deepseek-harness#579merged). - The npm
keywordsdropdsh-plugin, aligning with the GitHub topics convention (anti-spam crawlers strip non-plugins carrying that tag).
完整历史 / Full changelog: v0.2.1...v0.2.2
v0.2.1
安装 · Install
npm install -g github:wuwaka/dsh-rehearsal#v0.2.1也可锁定到字节 / or pinned to bytes: npm install -g https://github.com/wuwaka/dsh-rehearsal/releases/download/v0.2.1/dsh-rehearsal-0.2.1.tgz
(用本 Release 附带的 dsh-rehearsal-0.2.1.tgz.sha256 校验 / verify with the attached .sha256。)
文档与发布可核验性。check 与 run 的行为没有任何改动。
变更
- README 从 247 行压到约 130 行:只保留定位、安装、快速开始、真实输出示例、报告、覆盖范围、
安全摘要、兼容性、限制。移出的内容进入新增的docs/architecture.md。 - 首页不再承担"为什么值得存在"的论证:外部 CLI 的理由、与同类工具的分工表、写回合三层抑制的
实现细节、多帧 zstd 的测量数字,全部下沉到 architecture 与 SECURITY。 - 新增一句核心边界:迁移成功不等于迁移后的会话还能写回。
- "全程无需 API 键"改为准确表述:写回合走
@deepseek-ai/dsh-llm-replay的录制回放路径,
不发起真实模型请求。 - 术语统一:预检 / 预演 / 迁移 / 写回(pre-flight / rehearsal / migration / write round);
状态词统一为 Tested / Inferred / Not tested / Unsupported / Not covered。 - 删除"由报告自证""逐条核对对方源码"这类审计腔措辞。
SECURITY.md重排为:代码保证 / 显式危险选项 / 报告是敏感数据 / 范围之外 / 自查方法 / 上报方式。AUDIT.md标注为历史基线文档(基线7c374bc),新增逐条状态总览表;FIXES.md并入其
"修复与验证"一节后删除,避免与 AUDIT / CHANGELOG / git 历史三处重复。PUBLISHING.md改为清单优先,事故叙述移出。- 全部文档双语对;
test/docs.test.js断言对开存在、小节数一致、file:line引用与 issue 号与
提交哈希集合一致、语言切换行指向真实文件、无第一人称(测试数 61 → 68)。 - 新增仓库海报
assets/poster.jpg与assets/social-preview.jpg。
修复
-
a-inventory的details原先把DSH_HOME原值拼进字符串,靠finalize()的正则遮罩。该正则识别
C:\Users\<n>、/home/<n>、/Users/<n>与任意盘符路径,但相对路径、UNC 共享、以及
/srv/users/<n>这类非标准 Unix 家目录会原样进入报告(A/B 实测:修复前home=dsh-home泄漏,
修复后为home=custom)。该字段改为只写形状,路径不再进入字符串。 -
report.tool.version硬编码为0.1.0,已与package.json脱节;现改为从package.json读取。 -
回归锁:
homeShape的返回值不得含路径分隔符;端到端用相对--home跑check,断言该路径
不出现在报告任何位置(测试数 68 → 70)。 -
上一条只是让
home这一个调用点不再经过滤器,scrubText本身的缺口仍在:/root/.dsh(容器与
root 运行时的默认家目录)、/var/lib/<服务名>、/srv/<团队>、/tmp/<影子目录>、UNC 共享与../
相对路径全部原样穿过。本轮按类修:过滤器补齐这些形状(URL 与仓库相对路径各有测试保证不被误遮);
scrubValue原先只清值不清键,以路径作键会整体绕过,现已一并清;finalize()增加不变量,用同一
检测器复扫完整报告,任何漏网的 path-shaped 文本写入warnings[](redaction gap: …),于是将来新
阶段拼出新式路径时报告自己承认,而不是安静发布。测试数 70 → 73。 -
privacy.scrubbed: true原先写在newReport()里,等于构造时就宣称已脱敏;而 redaction-gap
不变量在finalize()内部,所以"某阶段绕过finalize()"这一场景并未被覆盖(评审指出,判断正确)。
现在该字段由finalize()依据不变量结果写入:没扫干净就是false。同时新增writeReport()
作为唯一落盘入口,未 finalize 的报告会被就地补做,绕不过去。 -
check不带--candidate时,0 newly-broken high是空转出来的数字却按结果呈现。现在该阶段判
warn、details写明"comparison NOT exercised"、并写入一条warnings[],退出码为1而非0。 -
defaultHome()在USERPROFILE与HOME同时缺失时(裸容器)会走到
path.join(undefined, '.dsh')抛TypeError;现回退到os.homedir()再到cwd,不再抛。
顺带修掉一处同源漂移:homeShape()自己重算默认位置,与加固后的defaultHome()得出不同结果,
会把真实默认值判成custom;现由调用方传入单一事实源。测试数 73 → 78。 -
文档里的行号引用会随代码移动而失效,且此前不可检测。本轮修掉四处真实失效:
SECURITY引用的
src/lib/report.js的第 136 行在finalize()上移后指向无关代码(实为第 199 行);作为"不该被误遮"示例写出的src/lib/x.js(第 12 行)指向不存在的文件;docs/FAILURE_MODES里src/lib/drill.js的第 311-313 行差一行
(应为 312-314);指向 gating-hub 第 875 行的那条外部引用与本仓库自己的同名文件撞名,读者无从分辨归属,现已带上 owner 前缀。 -
新增两条断言(测试数 78 → 80):活文档引用的每个仓内路径必须存在、外部引用必须在同一行标明归属;
每个仓内file:line引用都必须配一条"该行确实写着被引用的东西"的断言,没有断言的新引用会让测试失败。
AUDIT.md有意排除——它描述的是自己声明的基线提交,拿 HEAD 去断言它的行号会在两个方向上出错。
Documentation and release verifiability. The behaviour of check and run is unchanged.
Changed
- The README was cut from 247 lines to roughly 130, keeping only positioning, install, quick start, a real output sample, reports, coverage, a safety summary, compatibility and limitations. Everything moved out went into the new
docs/architecture.md. - The front page no longer argues for its own existence: the external-CLI rationale, the comparable-tools table, the three-layer write-round internals and the multi-frame zstd measurements now live in the architecture and security documents.
- Added the boundary the whole tool turns on: a successful migration does not prove the migrated session can still be written to.
- "No API key required" was made precise: the write round replays through
@deepseek-ai/dsh-llm-replayand issues no live model request. - Terminology unified (pre-flight / rehearsal / migration / write round); status vocabulary fixed to Tested / Inferred / Not tested / Unsupported / Not covered.
- Removed audit-voice phrasing such as "self-evidenced by the report" and "verified line by line against their source".
SECURITY.mdrestructured into: guarantees, explicitly dangerous options, reports are sensitive data, out of scope, self-check procedure, reporting.AUDIT.mdis now marked as a historical baseline document (baseline7c374bc) with a per-finding status overview;FIXES.mdwas folded into it as a remediation section and deleted, removing the overlap with AUDIT, this changelog and git history.PUBLISHING.mdbecame a checklist first, with the incident narrative moved out.- Every document is a bilingual pair.
test/docs.test.jsasserts the pair exists, section counts match,file:linecitations, issue numbers and commit hashes agree, language switch lines resolve, and no document drifts back into first person (test count 61 → 68). - Added repository images
assets/poster.jpgandassets/social-preview.jpg.
Fixed
-
The
a-inventorydetailsstring interpolated the rawDSH_HOMEand relied on thefinalize()
regexes to mask it. Those regexes coverC:\Users\<n>,/home/<n>,/Users/<n>and any
drive-rooted path, but a relative--home, a UNC share, or a Unix home outside/homesuch as
/srv/users/<n>reached the report verbatim (measured A/B:home=dsh-homeleaked before the
change,home=customafter). The field now records a shape, so no path enters the string. -
report.tool.versionwas hard-coded to0.1.0and had drifted frompackage.json; it is now read
frompackage.json. -
Regression locks:
homeShapemust never return a value containing a path separator, and an
end-to-endcheckwith a relative--homeasserts that the path appears nowhere in the report
(test count 68 → 70). -
The change above only kept one call site away from the filter; the gap in
scrubTextitself
remained./root/.dsh(the default home for root, and the normal case in containers),
/var/lib/<service>,/srv/<team>,/tmp/<shadow>, UNC shares and../relative paths all passed
through untouched. Fixed as a class this time: the filter now covers those shapes, with tests
pinning that URLs and repository-relative text are not over-masked;scrubValueredacted values but
not object keys, so a path used as a key bypassed redaction entirely and now does not; and
finalize()gained an invariant that re-scans the whole report with the same detector and records
any surviving path-shaped text inwarnings[](redaction gap: …), so a future stage that composes
an unknown style admits it instead of publishing the path silently (test count 70 → 73). -
privacy.scrubbed: truewas set innewReport(), i.e. the report claimed to be scrubbed at
construction time. The redaction-gap invariant lives insidefinalize(), so the scenario raised in
review — a stage that bypassesfinalize()— was not covered by it. The field is now written by
finalize()from the invariant's own result, so a report that did not fully pass reportsfalse,
andwriteReport()became the single writer that finalizes an un-finalized report on the way to disk. -
checkwithout--candidateprinted0 newly-broken highas if it were a result. That number is
now labelled: the stage reportswarn, its details say the comparison was not exercised, a
warnings[]entry says the same, and the exit code is1rather than0. -
defaultHome()reachedpath.join(undefined, '.dsh')when bothUSERPROFILEandHOMEare absent
(a bare container), throwing a rawTypeError; it now falls back toos.homedir()and then the
working directory. Fixing it exposed a drift in the same area:homeShape()recomputed the default
location itself and disagreed with the hardeneddefaultHome(), labelling a genuine default as
custom; the caller now passes one source of truth in (test count 73 → 78). -
Line citations in documentation go stale as code moves, and until now nothing could detect it.
Four real failures fixed this round:SECURITYpointed at line 136 ofsrc/lib/report.js, which was
unrelated code afterfinalize()moved (it sits at line 199 now); the "must not be over-masked"
example was written as line 12 ofsrc/lib/x.js, a file that does not exist; the
docs/FAILURE_MODESrange onsrc/lib/drill.jswas 311-313, off by one (312-314); and a citation of
line 875 of the gating-hub changelog collided with the repository's own file of that name, so it now
carries thedsh-plugin-gating-hub/owner prefix. -
Two assertions added (test count 78 → 80): every path cited by a living document must either exist
in this repository or be qualified with its owner on the same line, and every in-repofile:line
citation must be backed by an assertion that the cited line really contains what the docs claim it
for — a new unasserted citation fails the suite rather than drifting silently.AUDIT.mdis
deliberately excluded: it describes the baseline commit it names, so checking its numbers against
HEAD would be wrong in both directions.
完整历史 / Full changelog: v0.2.0...v0.2.1