Pinning Is the Start
Pinning matters because surprise updates are onboarding failures.
If the company cannot say which versions are expected, the newcomer becomes the version resolver. They install the latest Git, the latest Docker, the latest SDK, the latest IDE extension, and then discover the project expects something else.
Pinned baselines fix that class of failure:
ToolchainBaseline
git = 2.51.0
docker = 29.1.3
dotnet = 10.0.100
node = 22.20.0ToolchainBaseline
git = 2.51.0
docker = 29.1.3
dotnet = 10.0.100
node = 22.20.0But pinning alone is not enough. A pinned baseline that never updates becomes a museum. The team avoids change until a security patch or platform migration forces a painful jump.
The right model has two lanes:
| Lane | Purpose |
|---|---|
baseline |
stable toolchain used by newcomers and daily work |
candidate |
isolated update checks against one proposed version transition |
The baseline protects developers. The candidate lane protects the future.
The same two lanes apply to modules, not just tool versions. A module referenced by #t: or #cr: is frozen and rides the baseline; a module referenced by #b: is a moving source and is therefore a candidate — it goes through this lane before any profile is allowed to pin it. Pinning a module is pinning a version. Part VII defines the reference syntax.
Isolate, Then Matrix
Updates should be isolated whenever possible, but not forever.
Bad update practice:
update Git
update Docker
update Node
update .NET
update VS Code extensions
run setup
something failsupdate Git
update Docker
update Node
update .NET
update VS Code extensions
run setup
something failsGood diagnostic practice:
given current baseline
when Docker changes from 29.1.3 to 29.2.0
then run the complete onboarding readiness probes
and keep every other tool pinnedgiven current baseline
when Docker changes from 29.1.3 to 29.2.0
then run the complete onboarding readiness probes
and keep every other tool pinnedThis is exactly the kind of failure discussed in Typed Docker: tool versions change behavior, flags appear or disappear, output formats shift, and assumptions break at runtime. The onboarding system should catch that before the update becomes the newcomer's problem.
Isolation answers one question: "Did this one transition break the setup?"
But onboarding also has compatibility questions. Docker and WSL can break together. Node and pnpm can break together. A .NET SDK and a VS Code extension can break together. A proxy tool and a certificate bundle can break together. For those cases, the setup contract needs a matrix:
updateMatrix:
name: "runtime-baseline-2026-07"
strategy: pairwise
axes:
docker: ["29.1.3", "29.2.0"]
dotnet: ["10.0.100", "10.0.200"]
node: ["22.20.0", "22.21.0"]
pnpm: ["11.2.2"]
run:
- setup
- firstClone
- projectReadiness
acceptWhen:
- all required combinations pass
- no manual repair happened
- evidence includes every runner profile in scopeupdateMatrix:
name: "runtime-baseline-2026-07"
strategy: pairwise
axes:
docker: ["29.1.3", "29.2.0"]
dotnet: ["10.0.100", "10.0.200"]
node: ["22.20.0", "22.21.0"]
pnpm: ["11.2.2"]
run:
- setup
- firstClone
- projectReadiness
acceptWhen:
- all required combinations pass
- no manual repair happened
- evidence includes every runner profile in scopeThe rule is: isolate to diagnose, matrix to approve. The matrix does not replace the isolated lane; it prevents false confidence after the isolated lane passes.
The update model should exist for every important tool or coupled group:
- Git and OpenSSH.
- Docker, Docker Compose, Podman, LXC.
- SDKs such as .NET, Node, Java, Python, Rust.
- Package managers such as npm, pnpm, NuGet, Maven, pip.
- IDE and extension versions.
- Corporate certificate and proxy tooling.
- Security scanners and pre-commit hooks.
Each isolated lane answers one question: "Can a fresh developer still become ready if this one thing changes?" Each matrix answers the next question: "Can the supported combinations still become ready together?"
The Transition Is the Domain Object
A version update is not just a number change. It is a domain object.
VersionTransition
tool: "internal-cli"
from: "3.3.2.2"
to: "3.3.4.3"
scope: "candidate"
strategy: "isolated"
probes:
- install
- version
- authenticate
- clone
- build
- run smoke test
decision:
- accept
- reject
- quarantineVersionTransition
tool: "internal-cli"
from: "3.3.2.2"
to: "3.3.4.3"
scope: "candidate"
strategy: "isolated"
probes:
- install
- version
- authenticate
- clone
- build
- run smoke test
decision:
- accept
- reject
- quarantineThat model gives the company a vocabulary:
during update from 3.3.2.2 to 3.3.4.3
keep the baseline frozen
create fresh instances
apply only this transition
run ProjectReadiness
compare ReadinessEvidenceduring update from 3.3.2.2 to 3.3.4.3
keep the baseline frozen
create fresh instances
apply only this transition
run ProjectReadiness
compare ReadinessEvidenceThe transition can now be reviewed. It has evidence. It has failures. It has an owner. It can be retried. It can be rolled back.
A matrix is the same idea lifted one level:
during update matrix runtime-baseline-2026-07
keep the baseline frozen
generate candidate combinations
run ProjectReadiness on every required runner profile
accept only if the matrix evidence is completeduring update matrix runtime-baseline-2026-07
keep the baseline frozen
generate candidate combinations
run ProjectReadiness on every required runner profile
accept only if the matrix evidence is completeNo Newcomer Should Discover Update Drift
The worst possible update test is a first day.
When a newcomer arrives, the company is trying to build trust. If the first experience is "the setup is broken because Docker changed yesterday," the company has spent that trust on a preventable failure.
CI-ing setup changes that — and Part V is where those jobs get their fresh ground, one Packer-built machine per matrix combination, each standing up a fresh project instance:
- scheduled jobs test the baseline on fresh instances
- candidate jobs test one transition at a time
- matrix jobs test the supported combinations that matter
- failed updates remain outside the baseline
- accepted updates update one source of truth
- readiness evidence documents why the update is safe
The rule is simple:
No tool update reaches newcomer setup until its isolated transition and required matrix combinations have passed the same readiness contract as the baseline.No tool update reaches newcomer setup until its isolated transition and required matrix combinations have passed the same readiness contract as the baseline.That is how pinning and iteration coexist.
An update you have tested on fresh instances is an update no newcomer has to discover.
Test your onboarding.