Repository navigation
grove v0.63.25: audit fixes (stale files, compiler inputs, cgo, retries, verdict history) - #42
Merged
Merged
Conversation
A file whose stat no longer matched its stored record but whose content could not be hashed (over the 10 MiB cap, not a regular file, unreadable) stayed in currentFiles, so the prune pass kept the symbols of its last readable version. Those facts were served as current with nothing marking them stale (stack audit 2026-10-20, finding 1: a removed Target stayed indexed after a.go grew past the cap). Drop the facts rather than keep them labelled stale: Grove already documents this contract for the sibling case (CHANGELOG "Parse failures invalidate stale symbols: a changed file that fails to parse now has its previously stored symbols pruned instead of serving the last successfully parsed version"), and there is no per-file staleness label anywhere in the store or query results to carry a "last good" claim. Mid-edit syntax errors are unaffected: those files still parse partially and index what parsed. The error remains in IndexResult.Errors, and is reported again on every run until the file indexes, because the file is no longer stored. Unreadable directories keep their existing shield (walkFailed): there the files are not seen at all, which says nothing about their content. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
goListPackage did not decode CgoFiles, so the project importer and packageFiles saw only GoFiles. A local package whose files all import "C" (GoFiles=[], CgoFiles=[binding.go], astkit's thirdparty/tsobjc) loaded as an empty package named "": importers lost its declarations, interface dispatch through it failed, and its own files were never type-checked or given import edges (stack audit 2026-10-20, finding 4). cgo files are now parsed with the ordinary Go files in the project importer, in packageFiles (import edges, per-package analysis), and the per-package check sets FakeImportC — the handling the dependency source importer in the same file already uses. The files are checked as written, not the cgo-generated versions, so every position stays in the user's source (test pins New->helper at 13:2 in binding.go). Type errors located in cgo files are expected under FakeImportC (C.x is untyped) and no longer mark the package as partially checked; an error elsewhere still does. Verified against astkit's thirdparty/tsobjc: now name "tsobjc" with GetLanguage declared, not partial. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…part
carryForwardVerdict re-read the previous merged verdict and, for every
analyzer whose edges were carried forward, appended all of its previous
lines and then the current ones. A failed attempt ("no edges produced —
previous native edges carried forward") is itself a carry line that is not
"skipped:", so it was carried again on the next run: one two-line
diagnostic grew to 4, 6, 8, 10 lines, and Grove's own index already held
repeated CSharp pass/no-edge messages (stack audit 2026-10-20, finding 10).
The lines of each analyzer's last pass that produced edges now live in
their own meta key (native-last-good). The persisted verdict per analyzer
is:
- ran and produced edges: this run's lines (they become the last good);
- carried forward: the last good lines once, then this run's lines, which
say why nothing replaced them — "no changed files" (unchanged) versus
"<tool> not found"/"no edges produced"/"timed out" (unavailable/failed).
Older attempts are superseded, not accumulated;
- disabled, or no files of its languages: no last-good claim, since its
native edges are dropped.
Line formats are unchanged, so Prism's readiness and completion checks read
the same prefixes. An index written before the key existed derives the last
good lines once from its stored verdict (deduplicated, without skip/carry
lines or pass timings).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Build configuration is plaintext (tsconfig.json, pom.xml, Cargo.toml) or not indexed at all (go.mod, go.sum), and native scope was the set of changed source languages. Editing only tsconfig.json after a completed TypeScript pass therefore gave "js-ts: skipped: no changed files in its languages", and a go.mod-only edit was a no-op index: the graph kept import/type resolution computed under the old configuration (stack audit 2026-10-20, finding 2). The walk now records compiler inputs per analyzer (native.CompilerInputAnalyzers): go.mod/go.sum/go.work(.sum); tsconfig*.json, jsconfig*.json, package.json; the Maven/Gradle files the javac classpath cache already keys on; Cargo.toml/Cargo.lock; compile_commands.json; *.csproj/Directory.Build.props; composer.json; pyproject.toml/setup.cfg/requirements.txt (availability inputs). They are fingerprinted even when gitignored (a generated compile_commands.json usually is). Content hashes are stored in meta (native-inputs) and reused while size+mtime match, so a touch is not a change. On an added, removed or changed input: - the analyzer is removed from native-complete, so it gets a full run-to-completion pass (on this run and until it completes); - the no-op shortcut is skipped for that run; - resident worker state is dropped for that analyzer only (TypeScript worker programs, javac worker, rust-analyzer server); - result.Native says "<analyzer>: compiler inputs changed (<files>); running a full pass". The first run that records fingerprints reports no change (a cold index runs everything anyway). Inputs under an unreadable subtree keep their stored state. Tested end to end for TypeScript (tsconfig.json) and Go (go.mod edit reruns go; touch-only stays a no-op; settles afterwards). The other languages share the same path; their file lists are unit-tested only, as no Java/Rust/C#/PHP end-to-end fixture runs the decision here. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…unded
The no-op return ("no file changes since last index") came before the
logic that gives an analyzer without a completed pass a full run. After a
failed first pass (compiler unavailable, timeout, no edges), installing
the missing tool and running a plain index changed nothing until --force,
while Prism's readiness advice said a plain `prism index` would fix it
(stack audit 2026-10-20, finding 5).
native.Result now reports Incomplete: analyzers due a full pass that had
files and did not complete it, split into "unavailable" (never started)
and failed. The indexer keeps a retry record per analyzer (meta
native-retry: failures, next-due time, reason):
- unavailable: rechecked after 15s, doubling to at most 10 min. A
no-change index runs only the cheap availability probe when due, and the
full pass only once the probe reports available;
- ran and failed: re-run after 1 min, doubling to at most 6 h, since a
failed compiler pass can cost up to the 30 min completion cap;
- completed: record cleared;
- compiler inputs changed (finding 2): due immediately, and durable, so an
interrupted run is retried by the next index with or without changes.
The same schedule now also applies to runs where other languages changed:
an incomplete analyzer whose own languages are unchanged is re-run when
due, not on every edit. Its languages changing still runs it in full.
The no-change message is accurate: "skipped: no file changes since last
index; js-ts has no completed pass (<reason>); next retry in 12s, or use
--force to retry now", or "... still unavailable (<reason>); rechecked
now, next check in 30s". A retry is reported as "<analyzer>: retrying its
incomplete full pass (last attempt: <reason>)". The bare "(use --force to
re-run analyzers)" form remains for a settled index.
Analyzers with no recorded attempt (no files of their languages, or an
index from before this record) are not retried on no-change runs; the
next run with changes attempts them as before.
Tests: TypeScript unavailable -> pending -> recheck with a fake clock;
Go unavailable (go off PATH) -> PATH restored -> a plain no-change index
after the wait completes the pass and records it -> settles to a no-op.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Picks up the call-column fix for multibyte text before a call, Engine.Validate rejecting syntax errors, and .mts/.cts detection. The eval module's indirect requirement moves with it, so grove-eval builds against the same astkit. grove-eval unchanged (gin, cobra, socket.io, flask: identical P/R/F1). Co-Authored-By: Claude Opus 5.5 <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.
Fixes five findings from the 2026-10-20 stack audit (prism docs/research/stack-audit-2026-10-20/REPORT.md), plus astkit v0.16.10.
IndexResult.Errorsevery run until it indexes.Gates:
GOWORK=off go test ./...,go vet ./...green. grove-eval (gin, cobra, socket.io, flask) identical to main and within baseline.json.🤖 Generated with Claude Code
https://claude.ai/code/session_011Ai1qs5rPZ1cXyu74s4cdB