> ## Documentation Index
> Fetch the complete documentation index at: https://internal.softcrum.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Standard — Dependencies and Versions

> Scope: every package.json in the monorepo, plus the Node and pnpm versions themselves. The goal is a stack that is current and reproducible. Those pull against each other, and this document is where the tension is resolved.

## 1. The quarantine rule

**A published version MUST be at least 2 days old before it may be installed or committed.**

No exception for convenience, urgency, or "it is just a patch". The reason is specific: the
supply-chain attack vector *is* the freshly published version. A compromised release is typically
detected and yanked within hours to days, so the days-old window is where the danger lives.
Waiting costs nothing and removes the entire class.

**The one exception:** a version that fixes a security advisory affecting us skips the quarantine
and ships immediately. Security wins over caution about security — but the bypass is recorded in
the PR with the advisory id.

Enforced by `renovate.json` (`minimumReleaseAge: "2 days"`). Enforcement is not a substitute for
the rule: manual `pnpm add` of a same-day release violates this standard even though nothing
mechanically blocks it.

## 2. Pinning

* Exact versions everywhere. No `^`, no `~`, no ranges. (`save-exact=true` in `.npmrc`.)
* The lockfile is committed and is reviewed like code. An unexplained lockfile diff blocks a PR.
* `packageManager` and `.nvmrc` pin the toolchain itself; they are updated by the same process.

## 3. Adoption

**Latest stable at the moment of adoption.** When a dependency enters the repository it enters at
the current stable release — never at an older version "to be safe", never at a prerelease,
alpha, beta, canary, rc or `next` tag.

Two qualifications that "latest" alone does not capture:

* **Peer-major alignment beats absolute latest.** `@types/node` tracks the Node major we actually
  run (Node 24 ⇒ `@types/node` 24.x), regardless of what `latest` points at. The same logic
  applies to any package whose major is coupled to a runtime or framework.
* **A `0.x` dependency has no stable API.** In `0.x`, a minor bump may break. Any `0.x` package
  on a critical path (today: `drizzle-orm`) is upgraded deliberately, with its own PR and its own
  test pass — never grouped, never automerged.

A dependency that is unmaintained, or whose latest stable is old enough to be abandoned, is not
adopted. That judgement belongs in the ADR that introduces it.

## 4. Introducing a dependency

The stack is closed. A **new** dependency or vendor requires an **ADR before the first import**
(`AGENTS.md` §2.2). The ADR states what it replaces or enables, why the alternatives lost, its
maintenance signals, its license, and — for anything touching personal data — whether it becomes
a sub-processor requiring an entry in `docs/internal/legal/subprocessors.md`.

Upgrading an existing dependency never needs an ADR. Replacing one always does.

## 5. Keeping current

| Change            | Path                                                                       | Review           |
| ----------------- | -------------------------------------------------------------------------- | ---------------- |
| Patch, minor      | Grouped Renovate PR, automerged when CI is green and quarantine has passed | none needed      |
| Major             | Its own Renovate PR                                                        | human, always    |
| `0.x` minor       | Its own PR                                                                 | human, always    |
| Security advisory | Immediate PR, quarantine bypassed                                          | human, expedited |
| Node / pnpm       | Its own PR, `.nvmrc` + `packageManager` + CI matrix together               | human, always    |

A quarterly window handles the accumulated majors with a named owner. Majors do not drift: an
unapplied major upgrade older than two quarters becomes a tracked debt item, because the cost of
catching up grows faster than the cost of keeping up.

## 6. CI gates

* `pnpm audit --audit-level=high` fails the build. A finding is fixed or explicitly waived with
  an expiry date — never waived indefinitely.
* Lockfile drift check: the lockfile must be in sync with the manifests.
* No prerelease specifiers anywhere in a manifest.
* License allowlist: copyleft licenses incompatible with a proprietary product are rejected at CI,
  not discovered at diligence.

## 7. Current toolchain and its pending upgrades

Recorded so the gap is visible rather than forgotten:

|               | Pinned  | Latest stable | Status                                                                                             |
| ------------- | ------- | ------------- | -------------------------------------------------------------------------------------------------- |
| Node          | 24      | —             | current                                                                                            |
| pnpm          | 9.15.9  | 11.x          | **pending major** — two majors behind, lockfile format change; own PR                              |
| turbo         | 2.10.10 | 2.10.10       | current                                                                                            |
| TypeScript    | 5.9.3   | 7.x           | **pending major** — needs a compatibility spike across Next, Biome and drizzle-kit before adoption |
| Biome         | 2.5.8   | 2.5.8         | current                                                                                            |
| `@types/node` | 24.13.3 | 26.x          | intentionally held at the Node 24 major (§3)                                                       |

Adopting TypeScript 7 is a spike, not a bump. "It is the latest" is not a reason to adopt a
compiler rewrite before the toolchain that consumes it has certified it.
