docs/adr/ADR-265-ruview-npm-distribution-strategy.md
| Field | Value |
|---|---|
| Status | Accepted — D1–D4 implemented: .github/workflows/npm-packages.yml (matrix gate: tests, version-literal grep, pack-content/size gate, tarball-install smoke test, README claim-check), .github/workflows/ruview-npm-release.yml (publish-from-CI with npm publish --provenance), version single-sourcing (all three packages read package.json), ruview bin owned by @ruvnet/ruview (@ruv/ruview-cli bin renamed ruview-cli), ci.yml NODE_VERSION 18→20. D5 (no workspace) stands as recorded |
| Date | 2026-07-02 |
| Deciders | ruv |
| Codename | RUVIEW-NPM-DIST |
| Supersedes / amends | none (cross-cutting layer above ADR-263 and ADR-264; complements ADR-182 P3/P4) |
The monorepo now ships (or stages) three Node packages with no shared distribution engineering:
| Package | Dir | Published | Bin(s) | Tests in CI |
|---|---|---|---|---|
@ruvnet/ruview | harness/ruview/ | 0.1.0 (live) | ruview | none |
@ruvnet/rvagent | tools/ruview-mcp/ | 0.1.0 (live) | rvagent, ruview-mcp | none |
@ruv/ruview-cli | tools/ruview-cli/ | private | ruview (collides) | none |
Cross-cutting facts established during the ADR-263/264 reviews:
.github/workflows/ references any of
the three directories. Two of the packages are live on the registry and were
published from a laptop state CI never saw. Meanwhile the Rust side has a
1,031+-test gate and a witness-bundle culture (ADR-028) — the npm surface is
the only shipped artifact class with no verification gate at all.ci.yml pins NODE_VERSION: '18' while all three packages declare
engines.node >= 20.SERVER_INFO, rvagent
PACKAGE_VERSION, cli .version("0.0.1")).ruview bin.require target, 33% dead map weight — MEASURED, tarball listing — and a phantom
CHANGELOG.md in files) are exactly the defect class an
npm pack --dry-run assertion catches in seconds.Adopt one distribution layer for all Node packages. Per-package code fixes live in ADR-263/264; this ADR fixes the machinery around them.
npm-packages.yml CI workflow (the gate)Matrix over [harness/ruview, tools/ruview-mcp, tools/ruview-cli] ×
Node [20, 22]:
npm ci where a lockfile is committed (the TS packages); the harness
installs with npm install — repo policy gitignores lockfiles under
harness/, and the package is dependency-free after ADR-263 O3 so there is
nothing to pin.npm test (harness: node --test test/*.test.mjs — pin the glob form,
the directory form fails on Node 22; TS packages: build + jest or node:test
per ADR-264 O8).npm pack --dry-run --json asserted against a checked-in
expected file list + a max unpacked-size budget per package (harness ≤ 60 kB;
rvagent ≤ 130 kB post ADR-264 O2). Any new/missing/renamed shipped file is a
reviewed diff, not a surprise.ruview --version, ruview doctor, rvagent --help-equivalent, and a
Node import() of each declared export condition — this is the test that
would have caught ADR-264 F1 (require → nonexistent dist/index.cjs).ci.yml NODE_VERSION to '20' (independent of the matrix above).Manual npm publish from laptops stops. A tag-triggered workflow
(ruview-npm-release.yml, mirroring the firmware release discipline) runs the
D1 gate, then npm publish --provenance --access public under the GitHub OIDC
token. Consequence: every published version is attested to a public commit +
workflow run — the npm-side analogue of the ADR-028 witness bundle. The
prepublishOnly script in each package runs the pack gate locally as a
belt-and-braces (publishing outside CI fails loudly, not silently).
Rule: package.json is the only place a version string lives. Runtime code
reads it (createRequire(import.meta.url)('./package.json').version or a
build-time define for the TS packages). CI greps for \d+\.\d+\.\d+ literals in
src/ of each package and fails on match (allowlist: test fixtures). This
retires ADR-263 F6 and ADR-264 F9 permanently instead of per-incident.
@ruvnet/ruview owns the ruview bin (it is the published front door,
ADR-182). @ruv/ruview-cli renames its bin or folds into rvagent
(ADR-264 O9) — decided here so neither package ADR relitigates it.@ruvnet/ scope (the @ruv/ scope
holds rvcsi legacies; do not grow it).npx ruview claim-check — enforced in the D1 gate. The guardrail package
linting its sibling packages' claims is the cheapest dogfooding we have
(ADR-264 F3 is the standing example of why).Do not introduce an npm workspace or a shared runtime package yet: three
packages, two of which may merge (ADR-264 O9), do not justify workspace
machinery, and the harness's zero-dep property is load-bearing. Revisit if a
fourth package appears or if the http/cog/config duplication survives the
ADR-264 O9 fold. Record the duplication as intentional in each file header (the
CLI already does this).
require condition, dead maps) become CI-impossible.version-adjacent identifiers before weakening it.