feat(ui): entry points — routes, executable files and tests as flow starting points (CG-54)

`#/entry` answers "where does anything start" at full length, and turns any row
that names a symbol into a flow.

Server. `/api/entrypoints` gains `frameworks` (from `getDetectedFrameworks`), a
`tests` list, a `routes` limit of its own, and a cache keyed on the index build
— nothing here is read from disk, so unlike `/api/source` a cached answer cannot
be stale about drift. `routes.items` is now a `WireList` like every other list on
the payload.

Routes carry where the URL is REGISTERED as well as where it is served:
`getRoutingManifest` selects the route node's id, file and line, and
`buildRoutes` splits the verb off the name against a fixed list (never "the
first word", which would take the head off a file-routed `/blog/[slug]`). All
four payroll-go routes register in one router file and three are served from
another — group by the handler file and one router becomes two groups plus an
orphan.

`isTestFile` is split into `isTestPath` (test filename and directory
conventions) + the non-production catch-all, byte-identical at every existing
call site. The Tests list uses the narrow half: an example, a benchmark or a
fixture is off-target for ranking but is not a test, and a heading that says
"Tests" must not quietly count them. Tests rank by REACH — distinct other files
touched — because Go, Rust and Java put test work inside functions where a
module-level-calls ranking sees nothing. Two read-only engine queries make that
affordable: `getFileReachCounts` (the mirror of `getFileDependentCounts`, driven
from `nodes` by path so the cost follows the files asked about rather than the
edge table) and `getFileNodes`.

Viewer. `ui/src/lib/entry-model.ts` folds the four lists into file groups —
pure, and `panel.rows` stays exactly the sections it draws. `EntryView` +
`EntrySection` render them with the caller rail's `.filegroup` / `.row` shapes
rather than a second visual language for the same idea. A row that names a
callable symbol carries a `Flow ›` chip; the other end is typed or picked with
`→ here` on another row. File and test rows carry none: `/api/flow` searches by
name, and a file has none the path finder can look up.

A project with fewer than three resolvable routes gets no Routes heading at all,
not an empty one. Typing into the search box now also returns matching entry
points under their own heading below the symbol matches, so a URL comes back
with its handler attached; rows already in the results are dropped.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Colby McHenry
2026-08-27 05:20:15 -05:00
co-authored by Claude Opus 5
parent dc7f1e590e
commit 94f4e287e6
28 changed files with 2400 additions and 81 deletions
+13 -1
View File
@@ -46,12 +46,24 @@ If the viewer ever loses touch with the server, it retries a handful of times wi
## Getting around
- **Search** with `/` or Cmd-K: every symbol and file, grouped by kind, with signature and `file:line`. Arrow keys and Enter, no mouse needed.
- **Entry points** on the opening screen: your framework's routes, the files that run code when they're imported, and the most depended-on symbols in the project.
- **Entry points** on the opening screen, and in full on the **Entry points** tab (`e`) — see below.
- **Typing a name also finds entry points.** They come back under their own heading below the symbol matches, so searching `payroll` returns the URL *with* the symbol that serves it, not just the URL.
- **A trail** records the path you walked, with an arrow per hop showing whether you stepped into a call or up to a caller. Click any hop to jump back to it. The trail lives in the URL, so you can send someone the exact route you took.
- **Keyboard:** arrow keys move within a column, left/right switch columns, Enter follows, Backspace steps back.
Clicking any file path opens the **file view**: everything that file depends on, its outline in source order, and everything that depends on it.
## Entry points
The first screen worth opening on a codebase you have never seen. Four lists, all read out of the graph rather than guessed from filenames:
- **Routes** — every URL with the symbol that serves it and the `file:line` you will find it at, grouped by the file the route is *registered* in (your router, not your handlers) and headed with the framework CodeGraph detected. A project with fewer than three routes is not a routed app, so this section is simply absent rather than empty.
- **Top-level files with calls** — the files that *do* something when they load: a CLI, a worker entry, a build script. That is a fact about the graph (a statement outside every definition is recorded as a call from the file itself), not a guess about a filename, which is why a library module correctly shows nothing.
- **Tests** — the other direction: what already exercises this code, widest reach first.
- **Most depended on** — not where the project starts, but where a change radiates furthest.
Every row opens the code. Every row that names a symbol also carries a **Flow ** chip: press it, then name a second symbol — type it, or press **→ here** on another row — and you get the path between them. "How does `POST /v1/payroll/cycles/{cycleID}/run` reach the database" is two clicks once both ends are on the screen.
## The whole file
The **Source** tab on that screen replaces the outline with the file itself, top to bottom, with the same gutter markers and the same right-hand list of what each line calls — a 6,800-line file scrolls as smoothly as a 60-line one, and the text pages in behind you.