| id | dependency-release-policy | ||||
|---|---|---|---|---|---|
| title | Dependency and Release Policy | ||||
| kind | reference | ||||
| version | 1.3.0 | ||||
| last_updated | 2026-07-30 | ||||
| last_verified | 2026-07-30 | ||||
| review_cadence_days | 30 | ||||
| status | stable | ||||
| tags |
|
This workspace keeps application code, published npm packages, and infrastructure images on one reviewed dependency baseline.
- Node.js 24.11 or newer for repository development, CI, and releases
- pnpm 10
- TypeScript 7.0.2 native compiler, with the official TypeScript 6 compatibility API for API-dependent tools
- Oxlint for TypeScript linting
CI, conformance, documentation, and release workflows run on Node.js 24.
Root and package lint are warning-free and use blocking warning denial. A new
warning is a regression, not baseline debt to be accepted or ratcheted later.
The root .oxlintrc.json supplies the common correctness policy and
environment-specific overrides; package lint scripts select owned source
paths and inherit that policy. Node built-ins must use the explicit node:
protocol. Orphaned ESLint configuration is removed rather than allowed to
suggest a second, unenforced policy.
Every published package declares engines.node: ">=22". Node.js 22 is the
consumer runtime floor; Node.js 24.11 is the stricter contributor and release
toolchain. The repository-health check enforces the exact public-package
contract so new packages cannot silently omit or weaken it. Browser and React
Native entry points retain their declared non-Node runtime targets; the Node
engine field describes supported Node consumers and package tooling, not a
requirement that browsers provide Node APIs.
TypeScript 7.0.2 is the authoritative compiler for every direct tsc build and
workspace typecheck. TypeScript 7.0 deliberately ships without a stable
JavaScript compiler API, so the repository follows the TypeScript team's
supported side-by-side migration: @typescript/native provides tsc, while
the typescript dependency aliases @typescript/typescript6 for ts-jest,
ts-node, tsdown, and other API consumers. The peer range remains valid
without an override, and native TypeScript 7—not the test transformer—owns the
type-correctness gate. The exact contract, removal condition, and verification
commands are documented in
TypeScript Compiler and Tooling Boundary.
Dependabot proposes one coordinated multi-ecosystem maintenance PR each month.
Patch and minor updates remain automated across the root workspace, standalone
infrastructure lockfiles, deployment images, code generators, and GitHub
Actions. The single open version-maintenance slot does not apply to security
updates, so an old monthly PR cannot block immediate advisory remediation.
Security updates are grouped only within a package-manager ecosystem and are
never delayed into the monthly cross-ecosystem version update. First-party
@bsv/* versions remain owned by the release graph.
Major changes that alter a runtime, compiler, or persisted-data contract are held from the routine monthly PR until their focused migration is ready:
- Future TypeScript compiler majors are held in both root and standalone infrastructure npm scopes; TypeScript 7 patch and minor maintenance remains eligible for the monthly update.
- Node majors are held in the governed container-base manifest so CI, release, and every runtime image move together.
- MySQL and MongoDB majors are held until backup/restore, supported upgrade paths, application compatibility, live validation, and rollback have been exercised.
These holds delay only semver-major updates; compatible maintenance releases continue to flow. Removing a hold requires updating its supported-version documentation and completing the relevant migration evidence in the program tracker. GitHub Actions are pinned to immutable commit SHAs with accurate release-version comments; a generated action bump is still reviewed for permissions, runtime changes, and behavior before merge.
Dependabot is a proposal mechanism, not a reviewer or release manager. A maintainer must establish why each change is needed, inspect changelogs and runtime/deployment effects, remove obsolete dependencies, and require the same tests, security analysis, and package checks as human-authored work. Bot noise, conflicting single-package bumps, and first-party version PRs are consolidated or closed rather than merged piecemeal. CI recognizes dependency-shaped diffs and requires the pull request's dependency-evidence section to record release notes and necessity, runtime/build/peer compatibility, lockfile deduplication, audit and CodeQL results, package and consumer tests, bundle/performance impact, and affected public versions.
pnpm-workspace.yaml is the source of truth for installation controls:
- dependency build scripts are denied unless explicitly listed in
allowBuilds; - peer dependencies must be declared explicitly instead of being installed implicitly (including unused optional tooling peers);
- ordinary releases must age for 24 hours before installation;
- first-party
@bsv/*packages are exempt so coordinated releases can complete; - registry provenance downgrades are rejected for recent packages; and
pnpm audit --audit-level=highblocks high and critical advisories in CI and release jobs.
Parallel test lanes install with lifecycle scripts disabled. The wallet lanes
then rebuild only the explicitly allowlisted better-sqlite3 binding they need;
the full build lane remains the single place that runs the workspace's approved
installation scripts.
The version-consistency gate rejects public package manifests that place test
runners, test clients, linters, documentation generators, or TypeScript build
tools in dependencies. Type packages remain development-only unless the
governed project inventory names one as a declaration dependency because the
published .d.ts surface imports that external module. A governed declaration
dependency must be shipped in dependencies, its corresponding runtime module
must be a dependency or peer, and clean packed consumers must typecheck it.
This keeps build-only advisory trees out of consumer installs without shipping
unresolvable public declarations.
The root workspace carries two narrow audited dependency overrides:
- Jest 30.4.2 still constrains its reporting and coverage graph to minimatch
releases that require
brace-expansion1.x/2.x, while GHSA-mh99-v99m-4gvg is fixed only inbrace-expansion5.0.8. The workspace substitutes 5.0.8 until Jest adopts minimatch 10.2.5 or newer. - Stryker 9.6.1's current
typed-rest-client@2.3.1dependency pins vulnerableqs@6.15.1exactly. A parent-scoped substitution selects 6.15.3 until upstream accepts 6.15.2 or newer. This replaces a fragile lock-only substitution that an unrelated graph refresh could silently undo.
Both substitutions are verified through the affected Jest/mutation paths and
have owners, evidence, review dates, and upstream removal conditions in the
repository-health exception registry. Standalone service locks also need
temporary gaxios substitutions, and Message Box plus UHRP cloud storage need
uuid; the Wave 37 review removed every substitution from a service where the
frozen graph stayed clean without it. The machine-readable registry now maps
every remaining selector and exact value to its exception. CI rejects a new,
changed, stale, expired, unowned, or upstream-unlinked override.
Wave 38 repeated the removal rehearsal by regenerating every standalone lock
without its gaxios, uuid, and brace-expansion substitutions and checking
the natural dependency graph. It also rechecked the root Jest/minimatch,
typed-rest-client/qs, and isolated Redocly closures. All 19 remaining
selectors still prevent a reproduced vulnerable resolution or preserve the
governed reproducible generator, so none can be removed safely yet. The
method, result, count, and next rehearsal are enforced in
governance/dependency-release-policy.json.
The independently locked OpenAPI generator also carries a narrow Redocly compatibility override. It is isolated from runtime packages, registered with the same owner/review/removal fields, and must regenerate identical checked-in output. These three exceptions are not permanent policy: their review dates are removal deadlines unless fresh evidence justifies an explicit extension.
The former AsyncAPI generator override was eliminated by replacing that
dependency with a deterministic
renderer built on the maintained yaml parser. This removes the legacy parser,
brace-expansion, and jsonpath-plus chains instead of masking them with
transitive substitutions.
governance/dependency-release-policy.json is the machine-readable authority
for cadence, evidence, release ownership, temporary substitutions, and
scheduled verification. On the first day of every month and after successful
npm or infrastructure release workflows, the read-only verification workflow:
- generates a direct-versus-latest inventory and separates compatible updates from major migration projects, versions younger than the repository's 24-hour release-age floor, supported peer ranges, coordinated runtime/tool migrations, the TypeScript compiler-API bridge, and forward vendor builds;
- reconciles all 30 source manifests, recorded published baselines, and npm
latest, explicitly reporting source candidates held by an operator's publication decision; - installs every published package with lifecycle scripts disabled and runs npm registry signature/provenance verification;
- pulls every checked-in deployment image by its immutable digest; and
- verifies generated package, support, conformance, coverage-reporting, and version facts.
The resulting JSON and frozen install evidence is retained for 90 days. A
registry version ahead of or divergent from source ownership, a missing
integrity/provenance record, an unavailable immutable image, or documentation
drift fails the workflow. A source candidate ahead of the recorded npm
baseline is not silently presented as published; it remains visibly
first-party-release-held until an operator authorizes the release.
The verified 2026-07-27 frozen root and infrastructure dependency graphs have no known audit findings after the registered compatibility substitutions. All other advisory paths were removed at their causes:
- the unused message-box
webpack-dev-serverdependency and its vulnerableuuidpath were deleted; - package builds use the maintained
esbuildrelease directly instead of inheriting the older release pinned bytsup; - the documentation site uses React Router 8 and a repository-owned static renderer instead of the incompatible React Router 6 SSG adapter; and
- lockfile normalization selects the patched Express/body-parser graph.
Do not hide a future advisory with a broad override or dismissal. Remove an unused dependency, upgrade or replace the owning tool, and verify the frozen consumer and development graphs first.
- Refresh mature direct dependencies within their declared semver ranges.
Never bypass the 24-hour release-age floor merely to make the inventory
report
current. - Remove obsolete or unused packages before considering overrides.
- Run the frozen install, version checks, audit, lint, build, tests, conformance, and documentation build.
- Review Dependabot changes as grouped maintenance work. Do not merge a bot PR merely because its diff is generated: check runtime relevance, changelogs, peer compatibility, audit impact, and full CI.
- Patch-bump every publishable workspace package whose source or published manifest changed.
- Publish through the OIDC release workflow. Never publish from a workstation.
- Merge the generated version-sync PR, then release any infra images whose first-party dependency ranges changed.
- Run or inspect the dependency/release verification workflow and retain its package versions, integrity/provenance records, image digests, and direct/latest inventory with the release evidence.
The generated sync PR may refresh infrastructure lockfiles with
npm install --package-lock-only --ignore-scripts. That command names no
package, executes no lifecycle script, and only produces a reviewed committed
lock for later npm ci and audit use. OpenSSF Scorecard alerts #200 and #221
are the same governed false positive after workflow line movement; retain the
check and the exception evidence rather than deleting the deterministic lock
refresh.
Breaking major upgrades are handled as focused migrations with an explicit compatibility and rollback plan. They are not forced into the workspace through transitive overrides.
See Release and Operations Guide for the complete preflight, publication, reconciliation, image, failure, and rollback path.