SHAPE

SHIFT

Back to homepage

The Lockfile Is A Causal Graph

Lockfiles look like generated text, so teams often treat them as annoying merge debris.

That is the wrong model.

A lockfile is dependency evidence. It records the result of a resolver running under a set of constraints: manifests, version ranges, peer sets, platform filters, registry metadata, integrity hashes, package-manager rules, and sometimes install scripts. The text is just the serialization.

If two branches edit dependency state at the same time, a line conflict is often the least interesting part of the problem.

A lockfile is the resolver output for many causes, not a flat list of package versions.

The Lockfile Has Parents

A source file usually has one obvious parent: the previous source file.

A lockfile has many.

package.json
workspace manifests
version ranges
peer dependency constraints
engine and platform fields
registry metadata
package manager version
resolution policy

Those parents matter because the same visible version can mean different things under different causes. A package can resolve because a direct dependency changed, because a peer set changed, because a transitive package was deduped differently, or because the package manager changed its resolver.

Semantic merge should not ask only whether the lockfile text can be combined.

It should ask what resolution changed and why.

Concurrent Dependency Edits Are Not All The Same

Two dependency changes can commute.

One branch adds a dev-only linter. Another branch adds a runtime package in a separate workspace. If the resulting graph has no shared peer set, no shared transitive path, and no install-policy conflict, the merge can probably be admitted after a re-resolution and install proof.

Other edits do not commute.

One branch upgrades React. Another branch adds a package with a narrow React peer range. A text merge might succeed, but the resulting graph can be invalid, duplicated, or subtly different from either branch's tested environment.

Lockfile MatrixWhat A Dependency Merge Has To Prove
SurfaceClaimRequired proofCurrent evidenceRoute
RequiredDirect dependencyTwo manifest edits commuteManifest and workspace ownershipDifferent packages, compatible rangesRe-resolve
RequiredPeer setRuntime peers remain satisfiablePeer dependency graphResolved peer ranges and consumersGate install
RequiredTransitive graphDeduping did not change runtime shape unsafelyGraph delta with package identitiesAdded, removed, replaced, and duplicated nodesReview if shared
ProvedIntegrityResolved artifacts are reproducibleRegistry and integrity bindingPackage URL, checksum, lockfile hashPreserve
MissingLifecycleInstall effects are acceptableScript and native build surfacePostinstall, optional deps, platform gatesReview
The merge unit is not the line. It is the resolved dependency graph and the proof attached to each changed surface.

Re-Resolve, Then Compare

The safest semantic dependency merge is usually not:

merge lockfile text
hope install works

The safer shape is:

merge manifest intent
run the resolver from the merged head
compare the graph delta
run install and package gates
record the receipt

That is closer to how humans already fix lockfile conflicts. They do not carefully reason through every line by hand. They re-run the package manager, inspect the surprising changes, and run the project.

The missing piece is making that process explicit enough for a coordinator to admit or reject.

Dependency ReceiptNeeds install proof
Lockfile Merge Candidate
Intent
Add @acme/charts and upgrade viteThe manifest changes are the human-readable dependency intent.
Parents
base lock, worker lock, current head lockThe candidate has to say which dependency graph it was derived from.
Graph delta
12 added, 4 replaced, 1 peer set changedThe review surface is compacted into semantic dependency changes.
Proof
install, audit policy, typecheck, focused package testsThe output is admissible only after the resolved graph has been exercised.
A useful lockfile merge receipt binds intent, parents, graph delta, and install evidence.

Package Managers Could Help More

Package managers already know most of this structure.

They know which manifests participated. They know why a package was selected. They know which peer dependencies were satisfied. They know whether a package is optional, platform-specific, linked, hoisted, or isolated. They know which package-manager version produced the file.

Semantic merge gets easier if package managers expose those facts as merge evidence instead of leaving every tool to reverse-engineer generated text.

The lockfile does not need to stop being text.

It needs a better receipt beside the text.

Dependency RoutesLockfile Admission
RouteSignalRequiresProduces
AdmitIndependent graph changes and install proofMerged resolver output matches manifest intentUpdated lockfile and dependency receipt
Re-resolveText conflict or stale lockfileRun package manager from current headFresh graph delta
ReviewPeer set, engine, script, or native dependency changedHuman or policy decision on dependency riskAccepted or blocked dependency boundary
BlockResolver output contradicts manifest intentNarrow dependency task or version policy decisionNo shared-state mutation
A lockfile conflict should route through resolver evidence, not through line ownership alone.

The Mental Model

A lockfile is a causal graph rendered as text.

The merge should preserve the rendered text, but it should review the graph.

That is the difference between "the conflict went away" and "the dependency world is still the one we meant to create."