Skip to content

fix(analyzer): stop hangs on large files and ground detection in evidence - #144

Merged
itsflaid merged 1 commit into
mainfrom
fix/analyzer-stability-grounding
Nov 4, 2026
Merged

itsflaid merged 1 commit into
mainfrom
fix/analyzer-stability-grounding

Conversation

@itsflaid

@itsflaid itsflaid commented Nov 4, 2026

Copy link
Copy Markdown
Owner

Summary

Audit of the static analyzer for two things: it must not hang or crash on unusual projects, and it must not report anything the project does not support. Run against 12 real repositories (Next, Nuxt, Astro, Remix, SvelteKit, Nest, Fastify, Express, Django, a library, a Turborepo monorepo) and 13 adversarial projects, comparing snapshots before and after each change.

Paths and graph edges were already sound: every path and edge in every snapshot points at a real file. The problems were elsewhere.

Stability

Problem Evidence Now
Symbol line numbers are quadratic: ts-morph getStartLineNumber() recounts newlines from the top on every call 73% of the profile; a 4 MB file with 50k functions did not finish in 170 s 2.7 s, via LineIndex (same numbers)
The four import/export ... from regexes use an unbounded [^'"]+, so a quote-free file is rescanned from every export 20k exports cost 15 s; 12k functions cost 13 s in the pipeline 1.3 s, via a shared bounded IMPORT_CLAUSE
The scanner reads every file whole six 400 MB CSV files crash with an out-of-memory error and no snapshot files over 5 MB are listed but not read, named in warnings, and their size feeds the fingerprint
camelCase splitting regex is quadratic a 100k-character class name stalled indexing for 7 s lookarounds, proven equivalent on 200k random strings
A symlink pointing outside the project is read src/passwd.ts -> /etc/passwd appeared in the map skipped when realpath leaves the root
Domain inference trusts the model answer and the cache relatedEntities returned as a string threw during merge; a bad cache repeated the crash on every run validated with zod, merged on a copy, failures mean "no domain"
40 String#localeCompare calls follow LANG 7 of 12 projects produced different snapshots under cs_CZ or tr_TR one compareText over Intl.Collator("en"); 12/12 identical under both locales

The snapshot fingerprint now includes the DevMap version, so a snapshot written by an older release is regenerated once instead of reused.

Grounding

  • Language is unknown when there are no TS/JS files (Django was reported as typescript).
  • A server framework listed only in devDependencies counts only when production code imports it (ky was reported as an Express api-service because of its test server).
  • .d.ts files are never entry points, and test-d/ is non-production source.
  • workspaceType comes from declared workspace members (workspaces, packages: in pnpm-workspace.yaml), not from a file existing or a second package.json.
  • Registry signals keep only the search terms found in the project's own evidence. 20 of 91 features listed vendors the project does not use.
  • Duplicate feature names get a folder hint (Home (homes)) and agent-navigation ids are unique, so a feature map is no longer dropped silently.
  • Domain-inferred features must name entities the project actually has.

Verification

  • Tests: 508 to 538. The 30 new ones are analyzer-stability.test.ts and analyzer-grounding.test.ts; 13 of the 18 grounding tests fail on main.
  • tsc --noEmit, pnpm build, --version, mcp-smoke and package-e2e pass locally (Linux, Node 22). The macOS/Windows and Node 24 matrix has not run yet.
  • The refactors (line index, regexes, collation, scanner) change nothing for normal projects: snapshots of the 12 repositories are byte-identical before and after, on an en-US host.
  • Analysis time stays linear in file count: 1001 files 4.1 s, 2001 files 7.2 s, 4001 files 13.7 s.

Not in this PR

  • Feature confidence measures parse quality, not evidence strength: projectMap.ts lifts any feature with two files (one parsed by ts-morph) to high, so a folder named search/ reads the same as an imported vendor package. A cap on path-only signals was tried and removed because that boost overrides it. This needs a design decision, and is noted in CLAUDE.md.
  • ts-morph builds a program and type-checker per file for getExportedDeclarations(); replacing it with syntax-level export reading would be faster but riskier.
  • snapshotEnrichment.ts parses the AI answer without stripping code fences, so a fenced answer skips enrichment (non-fatal).

Review notes

  • One commit. The changes overlap in projectMap.ts, featureDetector.ts and tsMorphAnalyzer.ts, so splitting by theme would need partial staging.
  • Version and changelog are untouched.

Generated by Claude Code

…ect evidence

Stability
- ts-morph symbol lines come from a LineIndex. getStartLineNumber() recounts
  newlines from the top of the file on every call, so a 4 MB file with 50k
  functions never finished; it now takes about 3 s.
- The four import/export regexes take their clause from IMPORT_CLAUSE. The old
  unbounded [^'"]+ rescanned a quote-free file from every `export` (20k exports
  cost 15 s).
- camelCase splitting uses lookarounds: 100k capitals cost 3.4 s, now none.
- Scanner: files over 5 MB are listed but not read (reported in warnings, size
  goes into the fingerprint), so six 400 MB data files no longer exhaust the
  heap. Symlinks that resolve outside the project are skipped, lines are
  counted without splitting.
- Every text sort goes through compareText (one Intl.Collator). localeCompare
  follows LANG and changed snapshots under cs_CZ and tr_TR.

Grounding
- Language is "unknown" for projects without TS/JS files (Django was typescript).
- A server framework that is only a devDependency counts when production code
  imports it (ky was reported as an Express api-service).
- .d.ts files are never entry points; test-d/ is non-production source.
- workspaceType comes from declared members (workspaces, pnpm-workspace.yaml
  packages), not from the file existing or a second package.json.
- Registry signals keep only the search terms found in the project's evidence.
- Duplicate feature names get a folder hint and agent-navigation ids are
  unique, so no feature map is silently dropped.
- Domain inference validates the model answer and the cache with zod, only
  keeps features that name known entities, and merges on a copy.
- The snapshot fingerprint includes the DevMap version.

30 new tests (538 total). 13 of the 18 grounding tests fail on main; the 12 stability tests need the new modules.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AgTNRWDMVo2d462doRNBUc
@vercel

vercel Bot commented Nov 4, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
devmap-web Ready Ready Preview Nov 4, 2026 11:56pm UTC

@github-actions

github-actions Bot commented Nov 4, 2026

Copy link
Copy Markdown

✅ Welcome ke DevMap!

Terima kasih sudah buat PR. Pastikan checklist ini terpenuhi sebelum review:

  • devmap doctor lolos (tanpa error)
  • Test pnpm test:cli lolos (semua pass)
  • New commands diupdate di docs/commands.md
  • Tidak ada console.log di command files
  • Registry terms sesuai standar (baca docs/guide/05-signal-registry.md)
  • Code style match dengan CONTRIBUTING.md

CI akan jalan otomatis pada setiap update. Kalau gagal, akan muncul link run di komentar ini.

Kalau DevMap membantu, tolong star ya: https://github.com/itsflaid/devmap

Terima kasih kontribusinya! 🙏

@itsflaid
itsflaid merged commit 408287a into main Nov 4, 2026
3 checks passed
@itsflaid
itsflaid deleted the fix/analyzer-stability-grounding branch November 4, 2026 23:58
@itsflaid itsflaid mentioned this pull request Nov 5, 2026

This branch was successfully deployed

1 active deployment
Preview — 03035672 Deployed Nov 4, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants