dsh-peer-range-audit
Verified@argszero/dsh-peer-range-audit Β· v0.1.1 Β· MIT
Audits declared dsh peer ranges against the two readings that decide them: the profile gate's (prereleases participate) and plain semver's (what npm's own resolver does).
Install
dsh plugin add @argszero/dsh-peer-range-audit Confirm the layer applied with dsh --profile default --dump-config β see the install guide.
Source
Tags
Readme
@argszero/dsh-peer-range-audit
A read-only tool that reports what a declared @deepseek-ai/dsh-* peer range
actually admits β under the two readers that decide it, which do not agree.
- the profile gate (
@deepseek-ai/dsh-app-boot) reads semver withincludePrerelease, so prereleases participate; - plain semver β what npm's own resolver runs, and what a dev checkout or an
out-of-profile install sees β admits a prerelease only through a comparator
that names a prerelease for the same
major.minor.patch.
The divergence is one-way and silent. A range can be satisfied inside a
profile and refused by the very next npm install, and nothing in the project
says so, because the gate only ever answers yes/no about the version it happens
to be running as.
The problem
The gate's test is one line
(packages/boot/app-boot/src/plugin-compatibility.ts):
semver.satisfies(runtimeVersion, requirement, { includePrerelease: true })
includePrerelease is the whole story. Under it, ^0.2.0 is satisfied by
0.2.0-rc.2 β the range names no prerelease, but the reader lets prereleases of
any in-range tuple through anyway. Plain semver refuses 0.2.0-rc.2 for
^0.2.0, and so does npm:
$ npm install # a checkout that declares @deepseek-ai/dsh-tools: ^0.2.0
npm error ERESOLVE unable to resolve dependency tree
So a plugin author who writes the range that looks right, tests it in a profile where it works, and publishes it has shipped a package that a plain consumer cannot install β and the failure surfaces somewhere else entirely.
Measured over the 31 published versions of @deepseek-ai/dsh-agent, against the
gate the project actually runs (0.2.0-rc.2):
| declared range | gate admits | plain admits | verdict |
|---|---|---|---|
^0.2.0 |
1 | 0 | gate-only |
^0.2.0-rc.2 |
2 | 1 | gate-only |
>=0.1.7-alpha.1 <0.2.0 |
6 | 4 | gate-only |
>=0.1.2 <0.2.0-0 |
12 | 0 | gate-only |
>=0.1.7-alpha.1 <0.2.0 || >=0.2.0-rc.1 <0.2.0 |
6 | 6 | identical |
The direction is fixed and was measured, not assumed: over that ladder nothing is admitted by plain semver and refused by the gate, and the gate admits everything plain semver does and more. The divergence is therefore always prereleases, and always in one direction.
Install
npm install @argszero/[email protected]
Then mount it. The package ships a bundle patch
(dsh.bundle.patch: ./cordis.patch.yml), so a deployment that follows the
harness's bundle convention gets it by adding one entry:
- insert:
- id: peer-range-audit
name: '@argszero/dsh-peer-range-audit'
Mounting it directly works the same way and takes the same config:
import audit from '@argszero/dsh-peer-range-audit'
ctx.plugin(audit, { versions: ['0.2.0-rc.2', '0.1.7-rc.2'] })
The declared peers are @deepseek-ai/cordis (^4.0.2) and
@deepseek-ai/dsh-tools:
>=0.1.7-rc.2 <0.2.0 || >=0.2.0-rc.2 <0.2.1
Verified against the 0.1.7-rc.2 and 0.2.0-rc.2 lines (npm run test:probe-lines pins every harness package to one line and runs the suite
against it). The range is a union of one segment per line on purpose: each
segment names its line's prerelease floor, so plain semver admits it too. That
is the shape the tool points authors at, and this package holds itself to it β
test/packaging.spec.mjs asserts that no claimed line needs the gate reading.
Usage
Inside a profile, with no arguments, it audits the dsh peer ranges of the plugins that profile mounts:
peer_range_audit {}
Everything else is named explicitly:
peer_range_audit {
"manifests": [ <a parsed package.json> ],
"versions": ["0.2.0-rc.2", "0.2.1-alpha.1", "0.1.7-rc.2"]
}
requirementsβ bare range strings, exactly as written.manifestsβ parsedpackage.jsonobjects; every@deepseek-ai/dsh/@deepseek-ai/dsh-*entry of eachpeerDependenciesis audited.versionsβ the ladder to test, for example fromnpm view @deepseek-ai/dsh-agent versions --json. Defaults to the plugin's configured ladder, then to the running version alone.runtimeVersionβ the running dsh version, when it cannot be read here. It is also what aworkspace:alias resolves to.
A report looks like this:
peer-range-audit: 1 declared dsh peer range(s), 31 version(s) tested, running version 0.2.0-rc.2 (read from @deepseek-ai/dsh-tools as resolved by this profile)
gate = the profile compatibility gate (packages/boot/app-boot/src/plugin-compatibility.ts): semver with includePrerelease, so prereleases participate.
plain = default semver, which is what npm's own resolver does: a prerelease is admitted only by a comparator naming a prerelease for the same major.minor.patch.
[1] third-party-plugin β @deepseek-ai/dsh-tools β range
declared : >=0.1.7-alpha.1 <0.2.0
gate admits : 0.1.7-alpha.1, 0.1.7-alpha.2, 0.1.7-rc.1, 0.1.7-rc.2, 0.2.0-rc.1, 0.2.0-rc.2 (6/31)
plain admits: 0.1.7-alpha.1, 0.1.7-alpha.2, 0.1.7-rc.1, 0.1.7-rc.2 (4/31)
gate only : 0.2.0-rc.1, 0.2.0-rc.2 <- prereleases: the gate reads them, plain semver does not
plain only : (none)
verdict : gate-only β the readings differ, and only over prereleases; a plain consumer (npm, a dev checkout) refuses what the gate admits
runtime : runtime 0.2.0-rc.2: gate admits, plain refuses β admission of the running version rests on the gate's prerelease reading alone
Summary: 0 range(s) read identically, 1 diverge between the two readings.
The runtime line is the one that matters for a plugin already mounted: it says
whether the row is there because the gate reads prereleases, and would therefore
disappear for anyone installing it outside a profile.
What it deliberately does not do
- It never proposes a replacement range. The honest output of a set comparison is the two sets. A synthesized range can be wider than the one it replaces and still look like a fix; an audit that quietly widens a compatibility claim is worse than no audit.
- It never refuses, cancels or rewrites anything. It is a diagnostic, and a diagnostic that changes behaviour stops being one. Nothing is logged as a refusal, a change or an error.
- It reports its coverage. The ladder is whatever the caller supplied, so "nothing is admitted" always means "nothing in this ladder". Counts are printed beside every set, and a profile scan says which rows it saw, which names resolved to a package, and which did not.
Config
| key | default | meaning |
|---|---|---|
tool |
true |
register the peer_range_audit tool |
versions |
[] |
default ladder for calls that name none |
maxVersions |
200 |
bound on the tested ladder |
maxRequirements |
50 |
bound on the ranges audited in one call |
Both bounds truncate loudly: the report says how many were dropped. With
tool: false the plugin mounts and registers nothing.
Where the running version comes from
@deepseek-ai/dsh-tools is a declared peer of this package and is always present
where the plugin runs, and the dsh packages are released in lockstep β the 31
published versions of dsh-tools, dsh-app-boot and dsh-agent are the same 31
strings β so its version stands in for the runtime's. The lookup goes through the
profile's own resolver (ctx.pluginPackages.packageOf) when that service is
present, and falls back to Node resolution from this module. When neither yields
a version, the tool says so and asks for versions or runtimeVersion rather
than guessing.
Which reading decides
Inside a profile, the reader that decides is the gate β and pnpm, the profile's
installer, agrees with it (measured: pnpm install on the same range is clean;
npm install raises ERESOLVE). The plain column describes npm's resolver, a
dev checkout, or an out-of-profile install, which is where the gate's answer does
not travel.
Notes
- Zero changes to the sandbox, the tool pipeline or any approval path: it registers one read-only tool.
semveris a runtime dependency rather than a peer: a diagnostic that cannot parse a requirement because its parser was never installed is not a diagnostic.src/gate.tsmirrors the gate's predicate, including its three conventions (only@deepseek-ai/dsh/dsh-*peers are judged, the threeworkspace:spellings resolve to the running version, and a blank or unparsable requirement admits nothing). The mirror is not asserted to be equal, it is measured to be equal:test/gate-equivalence.spec.mjsimports the gate itself and compares the two over a matrix of versions and ranges.