Invalidate the eval cache when local path: inputs change#2929
Open
domenkozar wants to merge 3 commits into
Open
Invalidate the eval cache when local path: inputs change#2929domenkozar wants to merge 3 commits into
domenkozar wants to merge 3 commits into
Conversation
…issing A tracked path that did not exist during evaluation (e.g. a devenv.local.nix probed via pathExists) is stored with no content hash and a fallback snapshot timestamp instead of a real mtime. When the file was later created within the same second as the snapshot, the mtime equality shortcut in check_file_state declared it unchanged and the cache went stale. A stored input with no content hash that is readable now is always a modification: existence itself is the change. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Local path: inputs were materialized with fetchTree into an immutable store copy, and nothing content-derived made it into the eval cache key: Nix strips narHash from local inputs when writing devenv.lock, the lock fingerprint is computed from the on-disk lock, and a path input without a narHash has no fingerprint. Edits to the input directory were invisible until .devenv was deleted, and devenv update did not help. Resolve path inputs without a pinned narHash to the live filesystem path instead. The eval cache then tracks every file the evaluation actually reads (module imports, readFile, pathExists probes), so edits invalidate it precisely, direnv watches the input's files via input-paths.txt, and the input tree is no longer copied to the store on every cache miss. outPath stays path-typed so string interpolation still copies to the store with context, keeping derivation references to input files working. Inputs that do carry a narHash keep going through fetchTree so pinned snapshots remain verified. Reported in https://evantravers.com/articles/2026/06/10/but-our-devenv-is-in-another-repo/ Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Deploying devenv with
|
| Latest commit: |
d148a54
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://8afffebc.devenv.pages.dev |
| Branch Preview URL: | https://fix-path-input-eval-cache.devenv.pages.dev |
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes edits to local
path:inputs (e.g.url: path:../shared-config) never taking effect: the evaluation cache served the old configuration until.devenvwas deleted, anddevenv updatedid not help. Reported in But our devenv is in another repo.Root cause
Local
path:inputs were materialized withfetchTreeinto an immutable store copy, and nothing content-derived made it into the eval cache key:narHash(and other volatile attributes) from local inputs when writingdevenv.lock, and the locker never computes one for relative path inputs in the first place.narHashhas no fingerprint, so it is silently skipped.With the cache key constant and evaluation reading from the store copy (ignored by input tracking as immutable), nothing ever invalidated.
Fix
resolve-lock.nixnow resolves path inputs without a pinned narHash to the live filesystem path instead of a fetchTree store copy. The eval cache then tracks every file the evaluation actually reads (module imports,readFile,pathExistsprobes), so:input-paths.txtand reloads on changes.git) is no longer copied to the store on every cache missoutPathstays path-typed so string interpolation still copies to the store with context, keeping derivation references to input files working. Inputs that do carry anarHashkeep going throughfetchTree, so pinned snapshots remain verified.The new integration test also flushed out a second, pre-existing cache bug: a tracked path that was missing during evaluation (e.g. a probed
devenv.local.nix) and created within the same second as the snapshot was declared unchanged by the mtime shortcut incheck_file_state. A stored input with no content hash that is readable now is always treated as modified.Test plan
tests/eval-cache-path-input: sibling config repo viapath:input; asserts edit invalidation, input tracking, anddevenv.local.nixappearance (all three steps run sub-second, covering the mtime collision).devenv-eval-cachefor the missing-then-created file and directory cases (46/46 pass).eval-cache-git,nixpkgs-config(relative path input with?dir=), and theoverlaysexample (flake: truepath:./subflake) all pass.devenv-nix-backendunit tests 33/33, fmt and clippy clean.Known gaps (follow-ups, pre-existing)
"${./file}") do not invalidate the eval cache; this affects the project root identically (CopiedSource ops are not reaching the input tracker).git+file://local inputs have the same staleness for the same reason (alsoisLocal, rev/narHash stripped) and need a different treatment.🤖 Generated with Claude Code