ADR-023: Dependency update policy
Status: accepted, 26 October 2026.
Context
Section titled “Context”Five dependency trees (Composer, and npm for the control panel, the storefront, the landing page and the docs) had no rhythm for updates. They were cleaned up once in September (STAB-03: 42 Composer and 36 npm advisories to zero) and have since moved only when CI’s audit job went red: league/commonmark on 4 October, shell-quote on 6 October, and on 25 October an npm audit fix in docs/ that took Astro past the version the server’s Node can run and stopped a deploy (deployment). Majors have accumulated unplanned: Laravel 13, Inertia 3, Astro 7.4, Vite 8, TypeScript 7, ESLint 10. Without a policy, every update is an emergency and every major is a backlog item nobody owns.
Decision
Section titled “Decision”- Advisories within two working days, with a lock-only update in its own pull request, or an
overridesentry when no in-range version fixes it. CI’s audit job stays the hard stop: no advisory at moderate or above merges. - In-range updates weekly through Dependabot, one grouped pull request per tree (
.github/dependabot.yml), merged the same week after the local gate. Majors are excluded from these proposals. - Majors quarterly, one framework at a time, each in its own pull request against the upstream upgrade guide, with the full gate and a browser pass. Packages that must move together move together (Inertia server and client; Astro and its integrations; React and its types). The list of available majors and the decision on each is kept in
PROJECT_STATE.md. - Production’s runtimes bound every version. A package that needs a newer PHP or Node than the server runs is not adopted until the server is upgraded, and CI tests on the same PHP as production. The Astro trees are pinned to the series the server’s Node runs; the pin and the Node upgrade change together.
- Nothing merges on its own. Every update, Dependabot’s included, is stamped by
scripts/check-all --stampand the release is confirmed afterwards (ADR-012).
The working procedure is on the dependencies page.
Alternatives considered
Section titled “Alternatives considered”- Auto-merge Dependabot pull requests that pass CI. Rejected: the deploy is gated on a local stamp, not on GitHub CI (ADR-012), and a merge nobody looked at would sit unstamped on
mainand block the next deploy behind it. - Let Dependabot propose majors too. Rejected: a major pull request per package per tree is noise that hides the weekly groups, and a major needs the upgrade guide, not a version bump.
- No pins, upgrade the server’s Node now. The upgrade is the right end state and is on the owner’s list; until it happens, a pin is what keeps a deploy from failing on a routine audit fix.
Consequences
Section titled “Consequences”- Dependabot’s version updates start from the configuration file; its security alerts and security pull requests need the repository setting turned on by the owner (noted on the dependencies page).
- The weekly pull requests cost about half an hour a week: a checkout, the gate, a merge and a release check.
PROJECT_STATE.md“Dependencies” becomes a maintained section rather than a September snapshot.- The CI PHP version moved from 8.3 to 8.4 to match production in the same change (local development runs 8.3; both are inside
composer.json’s^8.2).