overengineeringstudio / overengineeringstudio/effect-utils
tracking(nix-cli): replace heuristic staleness metadata with builder-native depsBuildFingerprint
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 82
- Forks
- 2
- Avg merge
- 1d 8h
- Merged PRs (30d)
- 121
Description
Summary
Track the hard-break migration from shell-computed staleness metadata (lockfileHash, packageJsonDepsHash) to a builder-native depsBuildFingerprint contract for nix-cli / mkPnpmCli consumers.
This is the architectural follow-through for #445.
Problem
Today the fast staleness path and the authoritative build path answer different questions.
- The authoritative path ultimately trusts the actual Nix deps build.
- The fast path uses heuristic metadata computed from a subset of downstream files.
That split creates false negatives. #445 is one concrete example: an upstream change can alter the deps build recipe without changing the downstream pnpm-lock.yaml, so the quick check says "up to date" even though the stored pnpmDepsHash is stale.
More generally, the existing metadata is too narrow:
lockfileHashonly fingerprints the lockfile bytes.packageJsonDepsHashonly fingerprints selected dependency fields frompackage.json.
Those signals do not represent the full deps build recipe. They miss classes of changes such as:
- upstream
mkPnpmCli/mkPnpmDepsbuild-logic changes - workspace materialization changes
- toolchain changes that affect the prepared deps build
- other builder-level inputs that can change the fixed-output derivation without a matching local lockfile diff
This becomes more important as the packaging/toolchain story evolves. The contract should describe the actual deps build recipe, not a partial downstream approximation.
Goal
Make the quick path reuse the same abstraction as the real build.
The long-term contract should be:
pnpmDepsHash: authoritative fixed-output hash of the deps artifactdepsBuildFingerprint: cheap fingerprint of the effective deps build recipe
And not:
pnpmDepsHashlockfileHashpackageJsonDepsHash
Proposed solution
Introduce a builder-native depsBuildFingerprint and make it the only quick-check input.
Target shape
mkPnpmCli should expose a first-class fingerprint for the effective deps build recipe, for example via passthru.depsBuildFingerprint.
nix-cli should then:
- read the current
depsBuildFingerprintfrom Nix - compare it against the stored value
- only declare the deps hash up to date when the fingerprint matches
- otherwise fall through to the authoritative rebuild/update path
Semantics
depsBuildFingerprint should change whenever any input that can change pnpmDepsHash changes.
That includes the meaningful build-recipe inputs, such as:
- staged workspace manifests / lockfiles
- package dependency metadata that participates in deps materialization
- patch inputs used by the deps build
- workspace assembly / prepared-tree behavior from
mkPnpmCli/mkPnpmDeps - relevant toolchain inputs used by the deps build
The important part is where the fingerprint is defined: it should come from the builder layer that already owns deps materialization, not from a second shell-side model that re-lists files heuristically.
Implementation direction
The intended implementation is builder-native, not derivation-scraping in shell:
- expose
depsBuildFingerprintfrom themkPnpmClipackage itself - make
nix:hashpersist that value alongsidepnpmDepsHash - make
nix:check:quick/ related staleness checks compare that value vianix eval - remove
lockfileHash/packageJsonDepsHashfrom the public contract
A derivation-identity-backed fingerprint is a good candidate because it tracks the actual deps recipe while staying cheap to query when surfaced directly via passthru.
Why a hard break is the right shape
This should be a clean contract change, not a backward-compatible additive migration.
Reasons:
- keeping old and new metadata in parallel preserves dual authority
- the current fields encode the wrong abstraction, not just an incomplete one
- downstream consumers should not keep reasoning about the old heuristic model once the builder-native model exists
- a clean break makes the docs, examples, and failure messages honest
Scope
This issue covers the upstream contract change in effect-utils and the downstream migration work required to adopt it across existing consumers.
That includes:
- replacing the old metadata shape in upstream examples and docs
- updating the
nix-clitask contract - updating downstream consumers that currently persist or pass the legacy fingerprint fields
- updating automation/documentation that still explains the old quick-check model
The downstream rollout details should stay in the relevant downstream repos / PR stacks, but the upstream contract and migration rationale belong here.
Alternatives considered
1. Extend the existing heuristic with more file/rev hashes
Examples:
- hash selected flake input revs
- add
flakeInputsHash - include more files in the shell-side fingerprint
Why not chosen:
- still keeps a second authority outside the builder
- requires manually deciding which upstream inputs "count"
- tends to grow ad hoc over time as more misses are discovered
- remains conceptually weaker than fingerprinting the actual deps build recipe
2. Store an explicit upstream rev in downstream metadata
Examples:
upstreamRev- per-input rev fields in
cliPackages
Why not chosen:
- pushes upstream/build semantics into downstream config
- creates avoidable alignment burden for consumers
- still does not model the actual deps recipe directly
- breaks source-of-truth boundaries
3. Force the full rebuild path more often
Examples:
- disable the quick check in CI
- add an env var to skip the heuristic
- always rebuild on automation runs
Why not chosen:
- operational workaround, not a principled fix
- leaves local false negatives intact
- gives up the value of a fast check instead of making it correct
4. Wait for dynamic/computed derivations to remove manual hash management entirely
See also: #411
Why not chosen:
- good long-term direction, but not a reason to keep a broken contract in the meantime
- we still need the current model to be correct until that future architecture is production-ready
Acceptance criteria
nix-clino longer depends onlockfileHashorpackageJsonDepsHashmkPnpmCliexposesdepsBuildFingerprintas the quick-check contract- the quick check is builder-native rather than a separate shell-side file heuristic
- the quick check detects the class of stale-hash problem described in #445
- docs/examples refer to
depsBuildFingerprint, not the legacy fields - downstream consumers can migrate without reintroducing per-repo restatements of upstream build semantics
Related
- #445
- #353
- #411
Filed by an AI assistant on behalf of @schickling
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with #445, then trace the nix-cli quick-check path through nix:hash and nix:check:quick and inspect the mkPnpmCli package's passthru contract. Map every current lockfileHash/packageJsonDepsHash consumer, including examples, docs, and downstream metadata. Done means the builder-native fingerprint is used consistently and the acceptance criteria no longer mention the legacy fields.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- shell, typescript
- Domain
- build-system, cli, tooling
- Issue type
- Refactor
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100