ADR-061: Dependabot Version-Pin Auto-Sync

Give Dependabot PRs a pre-synced version:check and auto-heal the versions.json/versions.yml pins on main after manifest-changing merges, so dependency bots stop failing a gate they can never satisfy

Last updated

Status

Accepted — extends ADR-059 (which made versions.json a cross-repo contract consumed by the docs site’s drift gate)

Context

version:check (in quality:ci) fails the build when the exact pins in versions.json / versions.yml drift from package.json. The write side is pnpm versions:sync, which a human runs before committing a dependency change.

Dependabot cannot run repo scripts. Every Dependabot PR that bumps a pinned dependency (astro, @playwright/test, @biomejs/biome, …) therefore fails version:check by construction — the bot bumps package.json and can never commit the synced pins. In practice (PRs #41, #44) every grouped-minor wave arrived red and had to be replicated by hand on a maintainer branch. A gate that a legitimate, wanted change can never pass is not enforcing anything; it is routing bot work to humans.

Decision Drivers

  • Enforcement must stay real for humans: a maintainer who edits package.json and forgets versions:sync should still fail CI (ADR-039)
  • versions.json on main must stay accurate: the docs repository’s drift gate fetches it raw from main (ADR-059)
  • No unreviewable automation against PR branches: pull_request_target + bot-triggered pushes is a known injection foot-gun, and commits pushed with GITHUB_TOKEN do not re-trigger CI, leaving the PR’s head commit unvalidated

Considered Options

Option 1: Auto-commit the sync to the Dependabot PR branch

A pull_request_target workflow checks out the bot branch, runs versions:sync, and pushes the fix-up commit.

  • Pros: the merged commit is complete in itself; no follow-up commit on main
  • Cons: GITHUB_TOKEN pushes don’t re-trigger pull_request workflows, so the synced head commit would carry no green CI; pull_request_target with a head checkout widens the attack surface; rejected

Option 2: Pre-sync in CI for bot PRs + auto-heal on main (chosen)

Two small pieces:

  1. ci.yml runs pnpm versions:sync before quality:ci only when github.actor == 'dependabot[bot]' — the gate then validates the synced state, which is the only state the bot could ever be asked to produce. For human actors nothing changes; the gate stays strict.
  2. A versions-sync.yml workflow on push to main (paths: package.json) runs versions:sync and, if the pins drifted, commits and pushes the regenerated versions.json / versions.yml.
  • Pros: bot PRs judged on what they can control; human enforcement unchanged; main converges to accurate pins within one CI run of any merge; no PR-branch automation
  • Cons: a Dependabot merge briefly (≈1 min) leaves main with drifted pins until the heal commit lands; the heal commit itself is bot-authored history

Option 3: Drop the pins or the gate

  • Pros: deletes the problem
  • Cons: ADR-059’s docs drift gate consumes versions.json from main; without the in-repo gate the manifest rots silently — the exact failure mode ADR-059 retired; rejected

Decision

Option 2. The gate’s meaning becomes: the pins match package.json at every human commit, and main self-heals after bot merges.

Implementation Details

  • ci.yml build-test gains a Pre-sync version pins (Dependabot PRs) step gated on github.actor == 'dependabot[bot]', immediately before quality:ci
  • New .github/workflows/versions-sync.yml: pushmain, paths: [package.json], permissions: contents: write; commits as github-actions[bot] only when git diff shows real drift; rebases before push to tolerate racing merges
  • The heal commit touches only versions.json / versions.yml, so it cannot re-trigger the workflow (paths filter) even if bot pushes triggered workflows (they don’t)

Consequences

Positive

  • Dependabot waves can merge green without a human replicating them
  • versions.json on main stays trustworthy for the ADR-059 cross-repo gate
  • Human drift is still a hard failure — enforcement, not cadence

Negative

  • Sub-minute pin-drift window on main after a bot merge (docs-repo CI compares only at its own build times; acceptable)
  • Two extra bot commits per Dependabot merge wave in main history

Validation

  • A Dependabot PR bumping a pinned dep passes version:check without human intervention
  • Merging it produces an auto-heal commit that restores versions.json accuracy on main
  • A human PR with drifted pins still fails quality:ci

References


Date: 2026-08-12
Participants: Template maintainers
Outcome: Accepted