feat: content-hash staleness, state.json tracked + durable across clones - #1
Merged
Conversation
The previous mtime-based staleness check broke across fresh clones:
git rewrites every file's mtime to the moment of checkout, so a freshly-
cloned repo with a committed state.json would treat every dir as
'file-modified' and re-bootstrap (~$0.40+ per cold checkout).
Switches state.directories[rel].files from mtime ints to sha256 content
hashes. Hashes survive any number of clones, file copies, line-ending
normalizations, or unrelated mtime touches. Only an actual content
change triggers re-analysis.
State schema bumped to version: 2. Old v1 states are silently dropped
on load — anyone upgrading runs one bootstrap to populate v2, then
incremental refreshes resume normally.
Also removed .docgen/state.json from .gitignore so it travels with
the repo. Committed the freshly-bootstrapped state for this repo
(three dirs: .github, .github/workflows, hooks).
Tests:
- new test: 'mtime changes but content identical → null' explicitly
asserts the fresh-clone scenario doesn't re-bootstrap
- existing 'file-modified when mtime changes' renamed to ' … when
content changes' and rewritten to mutate content via writeFileSync
- all utimesSync-as-staleness-trigger tests rewritten to mutate
content instead
- 60 tests passing (was 59; the new mtime-doesn't-matter test added)
CLI/API surface unchanged. selectFiles now returns { name, full, hash,
size } instead of { name, full, mtimeMs, size }, which is internal to
the module (tests update accordingly).
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.
Why
Previous mtime-based staleness broke across fresh clones: git rewrites every file's mtime to the moment of checkout, so a freshly-cloned repo with committed
state.jsonwould treat every dir asfile-modifiedand re-bootstrap. ~$0.40+ wasted per cold checkout — a real adoption blocker.Fix
state.directories[rel].filesnow stores sha256 content hashes instead of mtime ints. Hashes survive clones, file copies, line-ending normalization, and unrelated mtime touches. Only actual content changes trigger re-analysis.stateschema bumped toversion: 2. Old v1 states drop silently on load — one-time bootstrap on upgrade, then incremental refresh resumes..gitignoreletsstate.jsonthrough so it travels with the repo. Committed the bootstrapped state for this repo (3 dirs).Tests (60 passing, +1 net)
needsAnalysis returns null when mtime changes but content is identical— explicitly proves the fresh-clone scenario doesn't re-bootstrap.file-modified when mtime changes→file-modified when CONTENT changes; rewritten to mutate viawriteFileSync.utimesSync-as-staleness-trigger tests rewritten to mutate content.Public API
Unchanged.
selectFilesinternally returns{ name, full, hash, size }instead of{ name, full, mtimeMs, size }— only the test suite touches that shape.