Skip to content

feat(serve): the glossary in the page (M19c) - #39

Open
jgrodziski wants to merge 6 commits into
feat/m19c-drift-insightsfrom
feat/m19c-glossary-page
Open

jgrodziski wants to merge 6 commits into
feat/m19c-drift-insightsfrom
feat/m19c-glossary-page

Conversation

@jgrodziski

Copy link
Copy Markdown
Contributor

Fifth step of milestone M19c in PLAN.md §20 (word cloud / glossary, #6). Stacked on #38 (itself on #37, #36 and #35): review those first. This PR's diff is the six commits after #38.

It completes §20.4's Readers item: codegraph serve gains /glossary.json, and the page shows what the explanations said a word means, in both panels, each entry marked as a model's reading.

What the page shows

  • Vocabulary tab: click a word and, under the names that carry it, the entries of the explained entities in the current scope (corpus, module or type). Each module's reconciliation comes first, with the types that disagree on the word when they do. Then each entity's entry: its concern, sense and confidence, and the identifiers the word is in. The block wears the explanation panel's violet border and badge (LLM glossary) and names the models that wrote it. An entry is one click from its node in Navigate; an operation reveals its type.
  • Navigate panel: a selected type's or module's words and glossary now read as order (domain) — a customer's request. What a customer asks the shop to deliver. In OrderService · 0.60. The generic renderer had put the identifiers where a definition reads, and dropped a glossary entry's disagreeing types.
  • No store, the app daemon, a static host: nothing is shown, and the page looks as it did. The prompt-1 records the store holds are counted in a footnote.

The code

  • @codegraph/insights: answerGlossary(store, { term, scope, limit }, placement) and GLOSSARY_ANSWER_KIND (codegraph.glossary/1), beside wordReport.
  • CLI: /glossary.json?term=WORD[&scope=ID][&limit=N] (glossary-route.ts). The store opener moves out of insightLookup into storeReader(path), which both routes share.
  • Navigator UI: glossary.ts (parser and loader), the list in VocabularyView, and the named reading in blockFacts. The insight and carriers loaders each had their own "ask once, stop when there is no route" logic; it is now one helper, lookup.ts, in its own refactor commit.

Worth a look in review:

  • Which entities are "under" a scope is the model's, and it is injected. answerGlossary takes a Placement (an id → its containing module and type). The CLI builds it from the analyzer's fold over the entities the view keeps, the same rule /carriers.json uses, so a word's carriers and its glossary cover the same entities. The insights package still opens no model.
    • Under a module: everything that folds into it, its own record included.
    • Under a type: the type and its members. A module's reconciliation shows under its module and the corpus, not under a type.
    • A scope must be a module or type the view keeps; anything else is unknown-scope, 404.
  • Quiet like /insight.json. No store is {status: "no-store"}, 404. No term, or a limit that is not a whole number, is 400. The placement is built at the first request a store can answer; then the route lets the graph go, as the carriers route does.
  • The tab shows no meaning per entity. The word index doesn't hold it, a decision from feat(insights): the store indexes the words (M19c) #36; the Navigate panel shows it from the block. The modules' reconciliations come whole, meaning included.
  • The panel names two fields. blockFacts stays generic except for words and glossary, read by name; an item of any other shape falls back to the generic reading. This is the one exception to "the panel names no field".
  • Order and cut. Entries come module, then types, then operations, by id within a level, as insights --word orders them. Each list is cut at 200, with its total, so the tab says "the first 200 of 1,908 entries".

Docs

  • docs/cli.md: the third lookup, and the daemon's lack of it.
  • docs/insights.md: answerGlossary and its placement.
  • Website: the serve page (three lookups, the --insights row) and the vocabulary page.
  • --insights help: now names both panels it feeds.
  • PLAN §20.3: an "as built" note. §20.4's Readers item is ticked.

Verification

  • Gate.
  • Tests.
    • insights 158 (6 new: corpus, module and type scopes, unknown scopes, lowercase, the cut and its totals);
    • cli 1,037 (a new glossary-route.test.ts of 6 on the Java fixture: the fold, the view, 404 and 400, no store with no file created and no placement built, a store that appears later, the placement built once; plus the wiring in serve-command.test.ts);
    • navigator-ui 114 (9 new: the kind pin, the parser, the relative URL, asked once, and the panel's two readings);
    • vocabulary 345, core 544, analyzer 614, viz 143, city 94, extractor-typescript 66, scm 56, navigator 40, llm 34, all green.
  • Deliberate mutations. I broke the code 31 times, one change at a time. 30 were caught. The one missed keeps the graph referenced after the placement is built: it costs memory, not behaviour, and no test can see it (the carriers route has the same shape). One of my own mutants created a store beside the shared Java fixture, which then failed the next full run. The mutation script now removes what a mutant writes.
  • Screenshots. Reviewed at 1600×1000 on two stores:
    • the Java fixture with a hand-written store over its ids: the tab on the corpus and on OrderService, and the panel reached from a glossary entry;
    • Broadleaf with the synthetic store from feat(cli): vocabulary --drift-insights (M19c) #38: the corpus order, cut and scrolled.
  • Scale. Broadleaf with the store from feat(cli): vocabulary --drift-insights (M19c) #38 (29,098 records, 109,208 entries, no model run):
    • the first request takes 0.21 s, the placement included; later ones 0.11 s;
    • order over the corpus is 1,908 entries, of which 200 are sent (53 KB);
    • the server's RSS after a click is the same within noise (605 MB, against 619 MB on feat(cli): vocabulary --drift-insights (M19c) #38's build).

Still owed for M19c:

  • docs/vocabulary.md and the README.
  • A live run on the Java fixture, and the per-word cost measured from it.
  • The real Broadleaf side-car checks, the --word order timing, and these readers on a re-explained Broadleaf store.

Part of #6.

🤖 Generated with Claude Code

https://claude.ai/code/session_01PKCVgyop39vhNa45r7zM8w

jgrodziski and others added 6 commits October 28, 2026 22:24
answerGlossary(store, { term, scope, limit }, placement): the entries
of the entities under a module or a type, and the modules'
reconciliations, for serve's /glossary.json. Which entity is under a
scope is the model's, so the placement is injected: this package still
never opens a model. GLOSSARY_ANSWER_KIND is the page's pin.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PKCVgyop39vhNa45r7zM8w
The third lookup of codegraph serve: what the explained entities under
a scope said a word means. It reads the store /insight.json reads (one
reader, opened on first request, now shared) and places entities by
the fold the carriers use, built at the first request a store can
answer. No store is a quiet 404, as for /insight.json.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PKCVgyop39vhNa45r7zM8w
The insight and carriers loaders each carried the same "ask once per
URL, stop asking a server with no such route" logic; lookup.ts holds
it once, before a third lookup needs it too.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PKCVgyop39vhNa45r7zM8w
The Vocabulary tab lists, under a clicked word's carriers, what the
explained entities of its scope said the word means: the modules'
reconciliations, then each entity's entry, in the explanation panel's
inference colour and badge. An entry reveals its entity in Navigate (a
member, its type). No store, no route: nothing is shown.

The Navigate panel reads a block's words and glossary by name, as
"order (domain) — sense. Meaning. In Order · 0.90": generically the
identifiers stood where a definition reads, and the types that
disagree on a word were dropped.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PKCVgyop39vhNa45r7zM8w
serve's third lookup, /glossary.json, in docs/cli.md and the website's
serve page (and the daemon's lack of it); the Vocabulary tab's
glossary on the vocabulary page; answerGlossary and its placement in
docs/insights.md. The --insights help now names both panels it feeds.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PKCVgyop39vhNa45r7zM8w
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PKCVgyop39vhNa45r7zM8w
@cloudflare-workers-and-pages

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Preview URL Updated (UTC)
✅ Deployment successful!
View logs
codegraph bbdf663 Commit Preview URL

Branch Preview URL
Oct 28 2026, 10:48 PM

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