overengineeringstudio / overengineeringstudio/effect-utils

tracking(nix-cli): replace heuristic staleness metadata with builder-native depsBuildFingerprint

Open
#458 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

area:nix area:nix-hash origin:agent type:chore
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:

  • lockfileHash only fingerprints the lockfile bytes.
  • packageJsonDepsHash only fingerprints selected dependency fields from package.json.

Those signals do not represent the full deps build recipe. They miss classes of changes such as:

  • upstream mkPnpmCli / mkPnpmDeps build-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 artifact
  • depsBuildFingerprint: cheap fingerprint of the effective deps build recipe

And not:

  • pnpmDepsHash
  • lockfileHash
  • packageJsonDepsHash

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:

  1. read the current depsBuildFingerprint from Nix
  2. compare it against the stored value
  3. only declare the deps hash up to date when the fingerprint matches
  4. 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 depsBuildFingerprint from the mkPnpmCli package itself
  • make nix:hash persist that value alongside pnpmDepsHash
  • make nix:check:quick / related staleness checks compare that value via nix eval
  • remove lockfileHash / packageJsonDepsHash from 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-cli task 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-cli no longer depends on lockfileHash or packageJsonDepsHash
  • mkPnpmCli exposes depsBuildFingerprint as 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

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.