Skip to content

feat(extraction): route Next.js App Router handlers (#759) - #2747

Merged
DeusData merged 6 commits into
DeusData:mainfrom
petterip:feat/nextjs-app-router-routes
Nov 2, 2026
Merged

DeusData merged 6 commits into
DeusData:mainfrom
petterip:feat/nextjs-app-router-routes

Conversation

@petterip

@petterip petterip commented Oct 21, 2026 •

Copy link
Copy Markdown
Contributor

What does this PR do?

Fixes #759: Next.js App Router app/**/route.ts handlers now produce Route nodes and HANDLES edges, so cross-repo intelligence has something to match against. It follows the approach approved in the issue.

Stacked on #2738, which makes the route pass read and write route properties as valid JSON. Without it, a route directory with a quote in its name gets a second Route cut at the quote and HANDLES properties that are not valid JSON, on the first full index already (pipeline_nextjs_quoted_route_round_trip_issue759 fails at ASSERT(full.read) with the route pass from before #2738). Until #2738 merges, this PR also shows its commits; the change here is the top commit, fa331ed.

  • internal/cbm/extract_nextjs.c (new), called from cbm.c after cbm_extract_openapi, sets route_path/route_method on the existing Function definition. The existing route-node pass then creates the Route nodes and HANDLES edges. It adds no pass, no Variables and no synthetic definitions.
  • Supported: export [async] function GET, export const GET[: Type] = [async] (...) => ..., and export { GET } of such a declaration, in route.ts/route.js under app/ or src/app/.
  • Paths are spelled as SvelteKit's already are (sveltekit_route_path): [id] is :id, [...slug] is *slug, a route group (x) adds no segment, and the root route is /. Route nodes carry the handler's file_path, so search_graph --file-pattern finds them (search_graph: Route nodes have empty file_path, so label=Route + file_pattern always returns 0 — and the hint blames the label #1366).
  • CBM_SEMANTIC_INDEX_VERSION 12 → 13, with a history entry, so a v12 index is rebuilt once.

Not routed by this increment (each covered by a test)

export const GET = function () {} (the TS/JS extractor makes no Function for it today); renamed export { h as GET }; re-exports (export { GET } from, export *); destructured or wrapped handlers (auth(handler), as/satisfies); optional catch-all [[...x]]; private _x; parallel @slot; intercepting (.)x; workspace roots (apps/web/app/...); route.tsx/jsx/mjs. Each file is judged on its own, so src/app/ is routed even where a root app/ makes Next.js ignore it. HEAD is not derived from GET. Pages Router, Convex and cross-repo matching stay out of scope, as agreed in the issue.

Size

+883 / −2: 288 source lines and 595 test lines added. That's over the 500-line guideline because of the lifecycle and upgrade tests; the source alone is under it. I can split the tests out if you'd prefer.

Evidence

Tested on main 3457b21b with #2738 (d9766ae8). After merging main 587f134e (6e54ac3c), the pipeline, extraction and infrascan suites were re-run: 983 passed, 0 failed (ASan+UBSan). Machine: x86_64, 16 cores, WSL2 (Linux 6.18) on Ubuntu 22.04, gcc 11.4; clang-format 20.1.8, cppcheck 2.20.0 and clang-tidy 21.1.8, the versions CI uses.

Requirement Test
Supported forms, path spelling, root, groups, dynamic and catch-all segments, JS and TS extract_nextjs_route_metadata_contract_issue759
Non-routes and the exclusions above extract_nextjs_rejects_nonroutes_issue759
Persisted Route name/QN/file/method and HANDLES handler, sequential path pipeline_nextjs_persisted_route_contract_issue759
Same on the parallel path (CBM_WORKERS=4, mode=parallel in the log) pipeline_nextjs_persisted_route_contract_parallel_issue759
full → NOOP → helper edit → remove method → body edit → add PATCH → move directory → delete; an unrelated route is kept at each step pipeline_nextjs_incremental_lifecycle_issue759
v12 index → one forced full rebuild, version 13 stored, then NOOP (fails if the version stays at 12) pipeline_nextjs_semantic_upgrade_issue759
Quoted directory: the Route is stored intact on the full index and after an incremental one pipeline_nextjs_quoted_route_round_trip_issue759 (SKIP_PLATFORM on Windows)

AI assistance

Implementation and tests were written with AI assistance (Claude Code; planning with Codex). I reviewed the change, ran the commands listed above, and take responsibility for it under my DCO sign-off.

Checklist

  • Every commit is signed off (git commit -s)
  • Tests pass locally (make -f Makefile.cbm test)
  • Lint passes (make -f Makefile.cbm lint-ci)
  • New behavior is covered by a test (reproduce-first for bug fixes)

app/**/route.{ts,js} files produced 0 Route nodes, so cross-repo
intelligence found no edges into a Next.js backend (DeusData#759).

cbm_extract_nextjs_routes fills route_path/route_method on the
existing Function definition for each exported HTTP-verb handler.
It adds no pass and no synthetic definitions; the route-node pass
builds the Route and HANDLES edge as it does for other frameworks.
CBM_SEMANTIC_INDEX_VERSION goes from 9 to 10 so existing indexes
re-extract once.

Fixes DeusData#759

Signed-off-by: Petteri Ponsimaa <petterip@gmail.com>
@github-actions

Copy link
Copy Markdown

Thanks for opening this — it has been seen, and it is queued.

This note is automated, but it is not a brush-off: it exists so you know where your PR stands instead of having to guess from silence.

Current review status: working through a backlog. 0.9.1-rc.1 is out, so the release freeze that held reviews is over — but it left a large queue of open pull requests behind it, and we are reading through them oldest-first. The background is in discussion #1144.

What that means for this PR, concretely:

  • It will not be closed for inactivity. No stale bot touches pull requests here.
  • It may still sit a while before a human reads it. That is on us, not on you.
  • Older PRs are read first, so a recent one is not being skipped — it is behind a queue.

Things that will genuinely speed it up whenever review does happen:

  • Keep it rebased on main — the tree is moving quickly right now, and a conflicting branch cannot be reviewed as the diff you intended.
  • Get CI green, or say which failures you believe are pre-existing.
  • Keep the change to one claim. Bundled features and refactors get split before they get merged, which costs you a round trip.
  • Every commit needs a sign-off (git commit -s) — CI enforces DCO.

If this fixes a bug, a reproduction we can run is worth more than a description of the symptom.

Thanks for contributing, and sorry in advance for the wait.

Signed-off-by: Petteri Ponsimaa <petterip@gmail.com>
@petterip

petterip commented Oct 24, 2026 •

Copy link
Copy Markdown
Contributor Author

Both failing CI jobs on the latest push look unrelated to this change:

  • test / test-ui: npm could not reach the registry (connect ETIMEDOUT, ENETUNREACH), so no UI test ran.
  • test / test-diag: 9927 passed, 1 failed: cp_trpc_calls_ts_procedure (test_convergence_probe.c:662). It passed in the other eight test jobs of the same run, and passes 13 of 13 times locally on the same diag build (clang-21 ASan+UBSan, same ASAN_OPTIONS). This PR never reaches that file: cbm_extract_nextjs_routes returns at once for paths outside app/ and src/app/.

Could you re-run the failed jobs when convenient? #2738, which this PR stacks on, is green.

@DeusData DeusData added enhancement New feature or request parsing/quality Graph extraction bugs, false positives, missing edges labels Oct 25, 2026
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
@DeusData

Copy link
Copy Markdown
Owner

Thank you, @petterip, for this careful, well-tested App Router change and for splitting the JSON groundwork into #2738 first. You were right that both red jobs were unrelated to this PR (a registry timeout and a known intermittent probe). Main has moved since that run, so we merged current main into your branch (one signed-off merge commit, 674b088; your commits are untouched) to get a fresh CI run on top of #2738. Please pull before pushing again. We are also running a quick route census on a real App Router app before merging.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
@DeusData

Copy link
Copy Markdown
Owner

Thanks again, @petterip. Main moved once more (another change touched tests/test_pipeline.c), so we merged current main into your branch again (one signed-off merge commit, da5db34; your commits are untouched). The route census on a real App Router app is prepared and runs on this head next; we will report the numbers here before merging. Please pull before pushing again.

Main has used semantic index versions 10 and 11 since this branch took 10,
so the Next.js App Router routes move to version 12 with their own history
entry: an index main wrote at 11 rebuilds once for the new Routes.
pipeline_nextjs_semantic_upgrade_issue759 now starts from a version-11 index.
The other conflicts were tests both sides added at the same place; both kept.

Signed-off-by: Petteri Ponsimaa <petterip@gmail.com>
@petterip

Copy link
Copy Markdown
Contributor Author

Main moved again and now conflicts with this branch, so I merged current main (a3831c97) in one signed-off merge commit, 1237096a; my earlier commits and your merges are untouched. One conflict needed a decision: main has already used index versions 10 and 11, so this PR's version moves to 12, with its own history entry, so an index main wrote at 11 rebuilds once for the new Routes. pipeline_nextjs_semantic_upgrade_issue759 now starts from a version-11 index. The pipeline, extraction and infrascan suites pass (945 tests) on an ASan+UBSan build. The diff against main is unchanged: 7 files, the Next.js part only.

Main took semantic index version 12 for its Markdown section change, so
the Next.js App Router routes move to version 13 with their own history
entry. pipeline_nextjs_semantic_upgrade_issue759 now starts from a
version-12 index.

Signed-off-by: Petteri Ponsimaa <petterip@gmail.com>
@petterip

petterip commented Nov 1, 2026

Copy link
Copy Markdown
Contributor Author

Main took index version 12 for the Markdown section change, so I merged current main (587f134e) in 6e54ac3c and moved this PR to version 13. The pipeline, extraction and infrascan suites pass (983 tests, ASan+UBSan).

If the number collides again before you merge, feel free to renumber it yourself: the history line and enum in pipeline_internal.h, plus coverage_version and ASSERT_GTE in pipeline_nextjs_semantic_upgrade_issue759.

@DeusData
DeusData merged commit f78620f into DeusData:main Nov 2, 2026
41 checks passed
@DeusData

DeusData commented Nov 2, 2026

Copy link
Copy Markdown
Owner

Merged - thank you very much, @petterip! This is a careful, well-scoped change: App Router handlers now become real Route nodes with HANDLES edges from the exported verb functions, with no new pass and no synthetic definitions, and the paths are spelled the same way as our SvelteKit routes, so they line up with everything else in the graph. Splitting the JSON groundwork into #2738 first, keeping the index version and its upgrade test current through several main merges, and listing exactly what this increment does not route made it a pleasure to review.

As promised, here is the route census we ran on this head (6e54ac3) against current main, each corpus indexed from scratch with both builds:

  • vercel/chatbot (formerly ai-chatbot) @ c2f8235e: 15 Routes with HANDLES from 10 route files, every supported handler under app/. The (chat) and (auth) route groups add no segment, [id] becomes :id, and the multi-verb files (GET/POST/DELETE on /api/document, GET/PATCH on /api/vote) each get one Route per verb. One of them, GET /api/history, is the Route the client already calls from hooks/use-chat-visibility.ts, so that caller now also gets a DATA_FLOWS edge to its handler - exactly the kind of link Next.js App Router: app/**/route.ts handlers produce 0 Route nodes, so cross-repo-intelligence finds no edges #759 was after. The [...nextauth] re-export stays unrouted, as documented.
  • vercel/next.js examples/cms-contentful @ dd904bb7: 2 Routes (GET /api/disable-draft and POST /api/revalidate), 186/257 -> 188/259 nodes/edges, the same numbers you reported.
  • honojs/hono @ bac5c4a8 (not a Next.js project): no change at all; nodes, edges and all 887 of its existing Routes are identical.

Apart from those Routes, their HANDLES edges, that one DATA_FLOWS edge and the route_path/route_method properties on the handler functions, nothing in any of the three graphs changed, and indexing again gave identical results. Thanks again for the thorough tests and for staying with this through all the main moves.

DeusData added a commit to liuchong/codebase-memory-mcp that referenced this pull request Nov 2, 2026
Brings the branch up to main 9c19b5d.

Two same-spot insertion conflicts, each resolved as the main version
of the file plus the own diff of this branch:
- src/daemon/application.c: the job struct gained watch_owners on main
  and request_started_ms plus the request facts here; both kept.
- tests/test_daemon_application.c: main added the DeusData#1818
  autoindex-skipped helpers where this branch added the worker resource
  failure test; both kept, the test placed after the doc_candidates
  test.

Everything else merged cleanly. src/main.c changed on both sides: main
(DeusData#2838) lowers the worker CPU priority inside the worker role block,
and this branch strips the publication fence flag before that block
and installs the fence after the worker role options; the hunks are
disjoint and both are kept. tests/test_pipeline.c gained the Next.js
route tests (DeusData#2747) on main and the publication gate test here, also
disjoint. No call site needed adapting: no new main code calls a
function this branch re-signed, and main re-signed none that this
branch calls.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request parsing/quality Graph extraction bugs, false positives, missing edges

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Next.js App Router: app/**/route.ts handlers produce 0 Route nodes, so cross-repo-intelligence finds no edges

2 participants