---
name: "dependency_pinning"
description: "Lock dependency versions so builds are reproducible and supply-chain safe: lockfile discipline per ecosystem (npm/pip/Go/Rust/Ruby), exact-pin policy for direct vs transitive deps, scheduled audit cadence, upgrade runbooks, and vendoring fallbacks when a registry goes down. Triggers when a project lacks a lockfile, deps drift between environments, upgrades break builds, or a CI pipeline installs at build time."
---

# Dependency Pinning

## Purpose

Make every build install the exact same code, in every environment, every time. That means a committed lockfile, installs that refuse to resolve at build time, a policy for how tight each pin is, a cadence for getting off old versions, and a plan for the day the registry is unreachable.

## Workflow

1. **Inventory.** Run `bin/dep-pinning-check.py` against the repo root. It flags: missing lockfiles, unpinned or ranged requirements in manifests, CI jobs that resolve instead of installing from a lock, and tag-pinned GitHub Actions.
2. **Lock.** For each ecosystem present, establish the lockfile per `references/ecosystem-lockfiles.md` and commit it. A lockfile only protects you if CI installs *from* it, so fix the CI install step in the same change.
3. **Set the pin policy.** Manifest (`package.json`, `pyproject.toml`, `go.mod`, `Cargo.toml`) = intent: direct deps with the narrowest range the team can live with. Lockfile = truth: exact versions, every transitive dep, ideally with hashes. Binaries and deployed services lean toward exact pins everywhere; libraries lean toward caret/compatible ranges in the manifest so downstream resolvers keep room to move.
4. **Schedule the grind.** Weekly automated update PRs (Dependabot or Renovate), a weekly audit run, and a monthly lockfile review. Details and configs in `references/audit-and-upgrade-runbook.md`.
5. **Plan the outage.** Stand up at least one registry fallback before you need it: vendored deps, a local mirror, or a proxy cache. Options in `references/registry-fallback.md`.
6. **Verify.** Re-run the check script. `npm ci`-style commands must fail loudly when the lock is stale; that failure is the feature.

## Output Contract

When this skill is used to set up or fix a repo's dependency discipline, the result includes:

- A committed lockfile for every ecosystem in the repo.
- CI that installs strictly from the lockfile (frozen/ci/deploy flags), verified to fail when lock and manifest disagree.
- A written pin policy (direct vs transitive, app vs library) the team can point at in review.
- A scheduled audit + update bot config, and a registry-fallback plan for deploys.

## Operating Rules

1. **Two files, two jobs.** The manifest declares intent (ranges allowed). The lockfile records reality (exact, hashes where the ecosystem supports them). Never resolve versions at deploy time.
2. **Lockfiles are committed.** No lockfile in `.gitignore`. Review lockfile diffs in PRs the way you review code: a lockfile-only PR with no manifest change is worth a second look.
3. **Pin for binaries, range for libraries.** Deployed services and binaries get exact pins for predictability. Published libraries use compatible ranges in the manifest so consumers are not boxed in.
4. **`latest` and `*` never ship.** Forbidden in committed manifests. Same for branch names in submodules and moving tags in CI workflows; use commit SHAs.
5. **Audit on a cadence, patch on an alert.** Weekly scans find rot; known advisories get patched immediately, out of band. CI gates on severity: no merging over unresolved High/Critical in the ecosystems the team ships.
6. **Upgrade small, never stale.** Patch/minor upgrades ride automated PRs. Major upgrades get one PR each, changelog read first, never bundled. Letting the gap grow until the upgrade is a rewrite is the failure mode this discipline exists to prevent.
7. **Own the transitive tree.** A vulnerability in a transitive dep gets pinned around it (npm `overrides`, yarn `resolutions`, Go version bump) when the direct parent will not move fast enough.
8. **The registry will be down someday.** Vendoring, mirrors, or offline caches are a deploy requirement, not a nice-to-have. A build that needs the public registry at 3am during an outage is a build you did not finish.

## Tooling

`bin/dep-pinning-check.py` — read-only repo scanner. Run it from the repo root:

```bash
python3 ~/workspace/skills/dependency-pinning/bin/dep-pinning-check.py [path]
```

Exit 0 means the pinning discipline is healthy; non-zero lists findings with severity (error/warn). It never modifies anything. See the header comment for the full check list.
