Skip to content

fix(graph): stop graph-on-stop hook crashing when tree-sitter is absent#319

Closed
efenocchi wants to merge 2 commits into
mainfrom
fix/codex-graph-on-stop-tree-sitter
Closed

fix(graph): stop graph-on-stop hook crashing when tree-sitter is absent#319
efenocchi wants to merge 2 commits into
mainfrom
fix/codex-graph-on-stop-tree-sitter

Conversation

@efenocchi

@efenocchi efenocchi commented Jul 18, 2026

Copy link
Copy Markdown
Collaborator

Problem

The graph-on-stop Stop/SessionEnd hook crashed with exit code 1 on installs where the optional tree-sitter native grammar is absent (e.g. codex / cursor with no embed-deps):

Stop hook (failed) — hook exited with code 1
ERR_MODULE_NOT_FOUND: Cannot find package 'tree-sitter'

Root cause: the hook's build path is a chain of static ESM importsrunBuildCommandextract/indeximport Parser from "tree-sitter". esbuild hoisted the external tree-sitter import to the top of the bundle, so Node resolved it at module load — before main()'s try/catch runs. main().catch(() => process.exit(0)) never fired, so the process exited 1 instead of degrading. tree-sitter is an optionalDependency, so it is allowed to be absent.

Fix

  • src/hooks/graph-on-stop.ts — defer the build path behind the gate via a lazy await import("../commands/graph.js") inside the existing try/catch. A missing extractor now degrades to a logged skip + exit 0.
  • esbuild.config.mjs — a source-level lazy import alone isn't enough (esbuild inlines it and re-hoists the external), so graph-on-stop is now built in its own splitting: true bundle via buildGraphOnStop(). The dynamic import stays a real runtime chunk load and the tree-sitter statics live only in graph-chunks/. Also clears stale hashed chunks before each build and removes graph-on-stop from the four shared harness build lists (claude-code, codex, cursor, hermes).
  • tests — a unit seam for the reject path, plus a bundle-level guard (graph-on-stop-bundle.test.ts) asserting the shipped entry has no static tree-sitter import and loads it only via graph-chunks/. The guard fails if splitting is ever dropped — the exact regression.

Verification

Reproduced before / after on the real bundle, same scenario (git repo, gate fires, tree-sitter unresolvable):

pre-fix bundle fixed bundle
tree-sitter absent exit 1ERR_MODULE_NOT_FOUND: Cannot find package 'tree-sitter' exit 0 — logs build threw: Cannot find package 'tree-sitter', session unbroken
tree-sitter present builds graph builds graph (parses source, writes snapshot) — no regression
  • Full suite: 4679 passed. The single failure (codex-hooks.test.tsplugin.json arrays) is pre-existing and unrelated (a WIP plugin.json change on main), not touched here.
  • Codex review of the diff: "functionally sound; no BLOCKER/HIGH correctness issues." Its three non-blocking notes (bundle-level test, stale-chunk cleanup, summary count) are all addressed.

Session Context

Session transcript

Summary by CodeRabbit

  • Bug Fixes
    • Improved graph-on-stop builds so missing optional native components no longer interrupt user sessions.
    • Graph build errors are now swallowed, logged, and build locks are released correctly.
  • Reliability
    • Graph-on-stop is packaged as dedicated per-harness bundles, preventing load-time dependency failures.
  • Tests
    • Added regression tests ensuring tree-sitter is deferred into lazy graph chunks and that missing-module build errors are handled safely.

The graph-on-stop Stop/SessionEnd hook statically imported the tree-sitter
extractor chain (runBuildCommand -> extract/index -> import Parser from
"tree-sitter"). esbuild hoisted the external tree-sitter import to the top of
the bundle, so Node resolved it at module load -- before main()'s try/catch --
and on an install without the optional native grammar (e.g. codex/cursor with
no embed-deps) the hook aborted with ERR_MODULE_NOT_FOUND and exit code 1.

Defer the build path behind the gate via a lazy dynamic import, and build
graph-on-stop as its own code-split bundle so the tree-sitter statics live in a
lazily loaded chunk instead of the entry. A missing grammar now degrades to a
logged skip + exit 0, honoring the hook's "never break the session" contract.
tree-sitter is an optionalDependency, so graceful degradation is by design.

- src/hooks/graph-on-stop.ts: lazy `await import("../commands/graph.js")`
  behind the gate, inside the existing try/catch.
- esbuild.config.mjs: buildGraphOnStop() builds the hook with splitting so the
  dynamic import stays a runtime chunk load; clears stale hashed chunks first;
  removed graph-on-stop from the four shared harness build lists.
- tests: unit seam for the reject path + a bundle-level guard asserting the
  shipped entry has no static tree-sitter import and loads it only via
  graph-chunks/.

Verified end to end: the pre-fix bundle exits 1 with ERR_MODULE_NOT_FOUND when
tree-sitter is absent; the fixed bundle exits 0 (graceful) in the same setup
and still builds the graph normally when tree-sitter is present.
@coderabbitai

coderabbitai Bot commented Jul 18, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 1182fbeb-3888-48f0-ad25-cc0edf4f1e13

📥 Commits

Reviewing files that changed from the base of the PR and between 0168129 and b6233ad.

📒 Files selected for processing (2)
  • tests/shared/graph/graph-on-stop-bundle.test.ts
  • tests/shared/graph/graph-on-stop-main.test.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • tests/shared/graph/graph-on-stop-main.test.ts
  • tests/shared/graph/graph-on-stop-bundle.test.ts

📝 Walkthrough

Walkthrough

Graph-on-stop is packaged separately for each harness with code-split graph chunks and deferred tree-sitter loading. The hook also lazily imports the graph command during build execution, while regression tests cover bundle structure and missing-dependency handling.

Changes

Graph-on-stop isolation

Layer / File(s) Summary
Per-harness graph-on-stop bundling
esbuild.config.mjs, tests/shared/graph/graph-on-stop-bundle.test.ts
Builds executable, code-split graph-on-stop bundles for Claude Code, Codex, Cursor, and Hermes, externalizes tree-sitter dependencies, and verifies lazy chunk loading.
Lazy graph command loading
src/hooks/graph-on-stop.ts, tests/shared/graph/graph-on-stop-main.test.ts
Loads runBuildCommand only when firing a build and verifies import failures are logged while locks are released and main() resolves.

Estimated code review effort: 4 (Complex) | ~45 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description is detailed, but it misses the required template sections for Summary, Version Bump, and Test plan. Add the template sections with a short Summary, a Version Bump note or 'no release needed', and a Test plan checklist.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly matches the main change: preventing graph-on-stop crashes when tree-sitter is absent.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/codex-graph-on-stop-tree-sitter

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@tests/shared/graph/graph-on-stop-bundle.test.ts`:
- Around line 28-37: Update the graph-on-stop bundle setup around HARNESSES and
built so every harness is required to have its expected graph-on-stop.js bundle;
remove the filtering that silently drops missing entries and assert the specific
expected bundle paths exist for all four harnesses. Keep the per-harness checks
operating on the complete HARNESSES set.

In `@tests/shared/graph/graph-on-stop-main.test.ts`:
- Around line 137-140: Update the logging assertion in the graph-on-stop test to
require the .graph-on-stop.log file and read it unconditionally, removing the
existsSync guard. Assert the specific expected “build threw” log message so
missing or incorrect logging fails the regression test.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 912f3c2a-dc66-4851-b8ba-c0215f9d4943

📥 Commits

Reviewing files that changed from the base of the PR and between c4f1ef6 and 0168129.

📒 Files selected for processing (4)
  • esbuild.config.mjs
  • src/hooks/graph-on-stop.ts
  • tests/shared/graph/graph-on-stop-bundle.test.ts
  • tests/shared/graph/graph-on-stop-main.test.ts

Comment thread tests/shared/graph/graph-on-stop-bundle.test.ts Outdated
Comment thread tests/shared/graph/graph-on-stop-main.test.ts Outdated
- bundle guard: require ALL four harness bundles (assert the exact harness
  set) instead of filtering to whichever exist, so a per-harness packaging
  regression can't pass unnoticed.
- main test: assert the .graph-on-stop.log exists and contains the specific
  "build threw: Cannot find package 'tree-sitter'" message unconditionally,
  rather than skipping the check when the file is absent.
@efenocchi

Copy link
Copy Markdown
Collaborator Author

Closing: this branch was based on a stale main (v0.7.106, ~302 commits behind). The tree-sitter graph-on-stop crash still exists on current main and is being fixed in a fresh PR branched off up-to-date origin/main (with the openclaw ClawHub define adjustment that the stale base was missing). Superseded.

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.

1 participant