fix: correctness bugs (Svelte off-by-one, watcher dropped changes, traversal defaults, context clamping)#98
Open
andreinknv wants to merge 1 commit into
Open
Conversation
Four user-visible bugs caught by an independent audit pass: 1. Svelte symbols reported on the wrong line src/extraction/svelte-extractor.ts:144 The script-block regex captures content starting with the leading newline that follows `>`, so the inner extractor sees that newline as line 1 of its 1-indexed input and the first real code on line 2. The previous `contentStartLine = scriptTagLine + openingTagLines + 1` was added to that 1-indexed line number, shifting every Svelte symbol's startLine / endLine off by 1. Drops the `+1`. Five regression tests added covering single-line, multi-line opening tag, template-offset, single-line no-newline, and dual module/instance script blocks. 2. Watcher silently dropped pending changes on sync failure src/sync/watcher.ts:177 `hasChanges = false` ran before the sync attempt, so a thrown sync (DB locked, transient FS error) left the pending batch forgotten until a NEW file event arrived. Re-set `hasChanges = true` in the catch path so a transient failure schedules a retry on its own. Regression test added (mocks fail-then-succeed, asserts the second call happens without a new file event). 3. Graph traversal default maxDepth was Infinity src/graph/traversal.ts:14, src/types.ts:301 `limit: 1000` capped returned nodes, but during traversal the visited set and BFS/DFS frontier can grow far beyond `limit` on highly connected graphs before the cap kicks in. Default is now 10. Callers who really need exhaustive traversal can still pass `maxDepth: Infinity` explicitly — the JSDoc documents this. This is a public-API behavior change; existing tests pass. Also caps `findPath`'s BFS queue at 100,000 entries (FIND_PATH_MAX_QUEUE) and returns null if exceeded — each entry holds a cloned path array, so on dense graphs the queue could otherwise consume gigabytes before either finding a path or exhausting the search. 4. `findRelevantContext` did not bound caller-supplied limits src/context/index.ts:284 `searchLimit` is multiplied by 5 in `findNodesByExactName` and feeds several other unbounded operations; a caller passing `searchLimit: 1_000_000` would pull millions of rows. Now clamped: searchLimit ∈ [1, 100], maxNodes ∈ [1, 1000], traversalDepth ∈ [0, 10]. Regression test asserts a 1e9 input is bounded. All 387 tests pass serialized; tsc clean. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This was referenced Apr 26, 2026
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
Four user-visible correctness bugs caught by an independent codebase audit. Each fix has a regression test that fails before the change and passes after.
1. Svelte symbols were reported on the wrong line — every time
src/extraction/svelte-extractor.ts:144The
<script>block regex captures content starting with the newline that follows>. The inner extractor sees that newline as line 1 of its (1-indexed) input and the first real code on line 2. The old code didcontentStartLine = scriptTagLine + openingTagLines + 1and added that to the inner extractor's already-1-indexed line numbers — shifting every Svelte symbol'sstartLine/endLineoff by 1. Dropping the+1produces correct positions. Five new tests cover single-line, multi-line opening tag, template-offset, single-line no-newline, and dual module/instance script blocks.2. Watcher silently dropped pending changes on sync failure
src/sync/watcher.ts:177If the sync threw (DB locked, transient FS error) the pending batch was forgotten until a new file event arrived. Now
hasChanges = trueis re-set in the catch path so a transient failure schedules a retry on its own. Regression test mocks a fail-then-succeedsyncFnand asserts the second call happens automatically without a new file event.3. Graph traversal default
maxDepthwasInfinitysrc/graph/traversal.ts:14,src/types.ts:301limit: 1000capped returned nodes, but during traversal thevisitedset and BFS/DFS frontier can grow far beyondlimiton highly connected graphs before the cap kicks in. Default is now10. TheTraversalOptions.maxDepthJSDoc documents that callers who really need exhaustive traversal can still passInfinityexplicitly. This is a public-API behavior change — existing tests pass with the new default.Also caps
findPath's BFS queue atFIND_PATH_MAX_QUEUE = 100_000entries (each holds a cloned path array, so on dense graphs the queue could otherwise consume gigabytes before either finding a path or exhausting the search). Cap is checked at the top of the BFS loop so it's a hard ceiling, not a near-miss.4.
findRelevantContextdid not bound caller-supplied limitssrc/context/index.ts:284searchLimitis multiplied by 5 insidefindNodesByExactNameand feeds several other unbounded operations; a caller passingsearchLimit: 1_000_000would pull millions of rows. Now clamped:searchLimit ∈ [1, 100]maxNodes ∈ [1, 1000]traversalDepth ∈ [0, 10]Regression test asserts a 1e9 input is bounded.
Files changed
src/extraction/svelte-extractor.ts+1incontentStartLinesrc/sync/watcher.tshasChangeson sync failuresrc/graph/traversal.tsmaxDepth: 10, addfindPathqueue capsrc/types.tsTraversalOptions.maxDepthdocsrc/context/index.tssearchLimit/maxNodes/traversalDepth__tests__/extraction.test.ts__tests__/watcher.test.ts__tests__/context.test.tsTest plan
npm test(serialized): 387/387 pass (was 380, added 7)npx tsc --noEmitcleanfindPathqueue cap to be a hard ceiling🤖 Generated with Claude Code