Skip to content

fix(build): fall back to WASM grammars when native tree-sitter is unavailable (#119) - #214

Closed
Frankie-Xu wants to merge 1 commit into
trailhq:mainfrom
Frankie-Xu:fix/119-arm64-wasm-fallback
Closed

Frankie-Xu wants to merge 1 commit into
trailhq:mainfrom
Frankie-Xu:fix/119-arm64-wasm-fallback

Conversation

@Frankie-Xu

Copy link
Copy Markdown
Contributor

Summary

  • graft init / graft build no longer crash on linux/arm64 when tree-sitter@0.21.1 has no native prebuild.
  • Native parsers still load when they exist (darwin, linux-x64). If require("tree-sitter") fails, depth languages (TypeScript/JavaScript, Python, Go, Java, Kotlin, PHP, R) extract through the existing WASM breadth path.
  • The build prints native parser unavailable — falling back to WASM (reduced binding fidelity). graft check and the statusline do not treat that as an error.

Closes #119.

Root cause

Failure is at require("tree-sitter") on CLI load, not npm install and not scripts/postinstall.mjs (that script is a one-line nudge and never throws). tree-sitter@0.21.1 ships prebuilds for darwin-arm64/x64, linux-x64, and win32-x64 only — no linux-arm64. With install scripts blocked, install succeeds and every command then dies with No native build was found for platform=linux arch=arm64.

Bumping to tree-sitter@0.25 (#40) would pick up linux-arm64 prebuilds, but that PR is still OPEN, CONFLICTING, last Ubuntu CI failed, and Alpine/musl / “no compiler” would still lose. Mixing 0.25 with today’s tree-sitter-typescript@0.23.2 is not a small bump.

Fidelity (not papered over)

WASM fallback has no bindings / receiver-type resolution. Call edges come from tags.scm when the query compiles (symbols only if it does not). Depth extensions stay owned by languageOf; genericLangOf still does not claim .ts/.py/.go/etc., so darwin/linux-x64 native extraction is unchanged.

Tests

  • test/native-fallback.test.ts: mock native throw → empty grammars; { wasm: true } still emits function nodes with origin: "generic"; default path on darwin stays origin: "ast" and warnings: []; graft check stays OK.
  • Full npm test on darwin: 919 pass. The 2 failures (PageRank timing, viz --tabs missing a local graph) are unrelated flakes/environment.

Docker evidence (linux/arm64, Node 22)

tree-sitter@0.21.1 --ignore-scripts then require("tree-sitter"):

REQUIRE_FAIL
No native build was found for platform=linux arch=arm64 runtime=node abi=127 uv=1 armv=8 libc=glibc node=22.23.2

Prebuilds in the tarball: darwin-arm64, darwin-x64, linux-x64, win32-x64 — no linux-arm64.

After this change, graft build of a tiny hello.ts (hello() → world()):

NATIVE_FAIL
No native build was found for platform=linux arch=arm64 ...
✓ wiring: 3 nodes (2 function, 1 file), 1 edges, 1 cards [typescript]
⚠ native parser unavailable — falling back to WASM (reduced binding fidelity)
graph check: OK

Nodes are origin: generic (hello.ts, hello.ts#hello, hello.ts#world).

Made with Cursor

@github-actions

github-actions Bot commented Aug 25, 2026 •

Copy link
Copy Markdown
Contributor

🌱 graft blast radius

2 areas changed → 6 areas can be affected. 20 dependent symbols, depth 2.
Tests: 1 area updated its tests.
Tag: @anirudhkumar-nanonets — 3 of 8 areas · @shhdwi — 4 of 8 areas · @Mult1Hunter — Graph Operations

flowchart TB
  A0(("Review Process<br/>8 symbols"))
  A1(("Workspace Federation<br/>5 symbols"))
  A2(("Tool Invocation<br/>3 symbols"))
  A3(("Graph Management<br/>2 symbols"))
  A4(("build-viewer.mjs<br/>1 symbol"))
  AX(("1 smaller area<br/>1 symbol"))
  classDef reached fill:#D9EDF3,stroke:#3AA7C9,stroke-width:1.5px,color:#0E313C;
  class A0,A1,A2,A3,A4 reached;
  classDef tail fill:#EEF2F3,stroke:#9AA4A9,stroke-width:1px,color:#3A4247;
  class AX tail;
Loading
Can be affected Symbols Nearest hop Reached from
Review Process 8 src/app/brain-build.ts:L251-L358 readRepository — calls, depth 1 Graph Operations
Workspace Federation 5 src/graph/container.ts:L151-L208 extractContainer — calls, depth 1 Graph Operations
Tool Invocation 3 src/mcp/tools.ts:L216-L244 callTool — calls, depth 2 Graph Operations
Graph Management 2 src/engine.ts:L91-L101 graph — calls, depth 1 Graph Operations
build-viewer.mjs 1 scripts/build-viewer.mjs:L1-L45 build-viewer.mjs — calls, depth 2 Graph Operations
Sync Execution 1 src/claude/sync-run.ts:L19-L33 runSync — calls, depth 2 Graph Operations
Who knows this code — 6 people across 8 areas
Area Who knows it
Graph Operations · changed @Mult1Hunter — 1 commit, last 11d ago · @tpoignonec — 1 commit, last 15d ago
Command Line Interface · changed @anirudhkumar-nanonets — 5 commits, last yesterday
Review Process · affected @anirudhkumar-nanonets — 11 commits, last yesterday
Workspace Federation · affected Buseong Kim — 1 commit, last 11d ago · @shhdwi — 1 commit, last 15d ago
Tool Invocation · affected @anirudhkumar-nanonets — 1 commit, last 9d ago · @afeddersen — 1 commit, last 11d ago
Graph Management · affected @shhdwi — 1 commit, last 15d ago
build-viewer.mjs · affected @shhdwi — 1 commit, last 15d ago
Sync Execution · affected @shhdwi — 1 commit, last 15d ago

Ownership is git history over each area's own files, weighted towards recent work (120-day half-life). Merge commits and bots are dropped, and you are dropped from your own PR. A name with no @ has no GitHub handle in its commit email — tag them by hand, or add a .mailmap entry. A suggestion from history, not a CODEOWNERS rule.

All 20 dependent symbols, grouped by area

Review Process — 8 symbols in 6 files

  • src/app/brain-build.ts:L251-L358 — readRepository (calls, depth 1)
  • src/app/review.ts:L45-L99 — reviewPullRequest (calls, depth 1)
  • src/app/brain-build-worker.ts:L1-L83 — brain-build-worker.ts (calls, depth 2)
  • src/app/brain-build-worker.ts:L29-L32 — DoneMessage (references, depth 2)
  • src/app/brain-build.ts:L237-L239 — buildRepoIntoBrain (calls, depth 2)
  • src/app/review-process.ts:L179-L183 — childReviewer (references, depth 2)
  • src/app/review-worker.ts:L67-L87 — run (calls, depth 2)
  • src/app/server.ts:L34-L46 — AppSeams (references, depth 2)

Workspace Federation — 5 symbols in 3 files

  • src/graph/container.ts:L151-L208 — extractContainer (calls, depth 1)
  • src/graph/refresh.ts:L150-L227 — ensureFreshGraph (calls, depth 1)
  • src/graph/workspace.ts:L630-L655 — federateCheck (calls, depth 1)
  • src/graph/workspace.ts:L709-L722 — splitWorkspace (calls, depth 1)
  • src/graph/refresh.ts:L235-L261 — ensureFreshChildren (calls, depth 2)

Tool Invocation — 3 symbols in 1 file

  • src/mcp/tools.ts:L216-L244 — callTool (calls, depth 2)
  • src/mcp/tools.ts:L247-L329 — callSingleTool (calls, depth 2)
  • src/mcp/tools.ts:L153-L199 — callWorkspaceTool (calls, depth 2)

Graph Management — 2 symbols in 1 file

  • src/engine.ts:L91-L101 — graph (calls, depth 1)
  • src/engine.ts:L82-L84 — checkGraph (calls, depth 1)

build-viewer.mjs — 1 symbol in 1 file

  • scripts/build-viewer.mjs:L1-L45 — build-viewer.mjs (calls, depth 2)

Sync Execution — 1 symbol in 1 file

  • src/claude/sync-run.ts:L19-L33 — runSync (calls, depth 2)
Test signal per changed area — 1 ✓ · 1 –

Reached = a node under a test path has a resolved edge into the changed symbol. It undercounts anything called indirectly — through a CLI, a spawned process or a dynamic import — so read a low ratio as “look here”, never as a coverage gate.

  • ✓ Graph Operations — 5 of 11 reached · 1 test file changed here: test/native-fallback.test.ts
    • not reached: asParserCtor, one, wasmFallbackLangs, parseSource, wasmRow, buildChild
  • – Command Line Interface — no function, method or class changed here
37 test suites also reference this code

48 symbols, kept out of the diagram and the table so they cannot crowd out the areas a reviewer has to look at.

  • test/ask-index.test.ts
  • test/ask.test.ts
  • test/container-extract.test.ts
  • test/context-only-dir.test.ts
  • test/context.test.ts
  • test/covers.test.ts
  • test/generic-extract.test.ts
  • test/generic-node24-probe.ts
  • test/graph-bindings.test.ts
  • test/graph-extract-dedup.test.ts
  • test/graph-go.test.ts
  • test/graph-incremental.test.ts
  • test/graph-invariants.test.ts
  • test/graph-java.test.ts
  • test/graph-languages.test.ts
  • test/graph-php.test.ts
  • test/graph-posix-paths.test.ts
  • test/graph-python.test.ts
  • test/graph-r-classes.test.ts
  • test/graph-r-phase3.test.ts
  • …17 more

⚠️ 6 changed files not in the graph (CHANGELOG.md, src/graph/queries/go.scm, src/graph/queries/python.scm, src/graph/queries/r.scm, src/graph/queries/tsx.scm) — no parser claims the extension, or the index predates the file.

graft blast · refs/graft/base...HEAD · depth 2 · 13 changed files

Open the interactive graph → — click an area to see the code that changed, and the line that reaches it.

github-actions Bot added a commit that referenced this pull request Aug 25, 2026
github-actions Bot added a commit that referenced this pull request Aug 25, 2026
@Frankie-Xu
Frankie-Xu marked this pull request as draft August 26, 2026 09:49
@Frankie-Xu

Copy link
Copy Markdown
Contributor Author

Rebased onto current main. Kept --only-dir fingerprint checking, added Swift to the WASM fallback set (it is depth-tier now), and kept strip/container warmup from main. Waiting on CI before marking ready.

@Frankie-Xu
Frankie-Xu force-pushed the fix/119-arm64-wasm-fallback branch from 597e2ae to de29268 Compare September 11, 2026 13:04
…vailable (trailhq#119)

linux/arm64 has no tree-sitter@0.21.1 prebuild, so require() crashed every
CLI command. Catch the native load, extract depth languages via the existing
WASM path, and warn about reduced binding fidelity. Darwin/linux-x64 native
behavior is unchanged.

Co-authored-by: Cursor <cursoragent@cursor.com>
github-actions Bot added a commit that referenced this pull request Sep 11, 2026
@Frankie-Xu
Frankie-Xu marked this pull request as ready for review September 11, 2026 13:13
@Frankie-Xu

Copy link
Copy Markdown
Contributor Author

Rebased onto current main. CI is green — please review when you can.

Ercaner1988 added a commit to Ercaner1988/Graft that referenced this pull request Sep 13, 2026
… depth language (trailhq#337 trailhq#217 trailhq#214 trailhq#325)

Four PRs were rewriting the same code path in extract.ts — one function now
does what they each asked for:

- Lazy loading (trailhq#217): a grammar (and the core tree-sitter binding itself) is
  required at most once, the first time entryFor()/depthExtensions() is asked
  about that language, not for all nine at module-import time. `graft mcp`
  never asks before the client's `initialize` reply, so a client can no
  longer see that handshake stall behind nine native loads.
- Per-language isolation (trailhq#337, unchanged): a grammar that will not load
  costs its own language, not the CLI.
- WASM fallback for every depth language, not just Kotlin/Java (trailhq#214): the
  breadth tier already had a "FALLBACK row" mechanism (a GENERIC_LANGS row
  reachable only when the matching depth grammar failed); tree-sitter-wasm
  ships a .wasm for all nine depth languages already, so the remaining seven
  now have one too. None has a queries/<name>.scm, so on the rare machine
  that actually reaches one it degrades to the node-kind walker (symbols
  only) instead of leaving the language unindexed.
- optionalDependencies (trailhq#325's core ask): the eight native grammar packages
  moved out of dependencies, so a platform lacking a prebuild for one no
  longer fails `npm install` for the other eight (core tree-sitter stays
  required — a missing core is a bigger question than one language, left for
  a follow-up).

New test: `graft mcp` answers `initialize` with tree-sitter-typescript broken
and never touches grammar loading at all (no warning on stderr) — the
concrete claim trailhq#217 exists for.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@shhdwi

shhdwi commented Sep 29, 2026

Copy link
Copy Markdown
Collaborator

Thanks for this! We tested it against a mixed TS/Python/Kotlin repo. With a single grammar broken it falls back to WASM with no warning at all: Python silently dropped from 11 to 9 edges. After the grammar is restored, the extract cache keeps replaying the fallback parse, so graft check stays STALE. The branch also conflicts with main. We are taking the narrower per-grammar isolation in #337 plus the optionalDependencies change in #325. A small follow-up that adds only the core tree-sitter WASM fallback for #119 on top of those would be very welcome.

@shhdwi shhdwi closed this Sep 29, 2026
github-actions Bot added a commit that referenced this pull request Sep 29, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ARM64 Incompatibilies

2 participants