Repository navigation
feat(vocabulary): aggregate into vocabulary.json (M19a) - #28
Open
jgrodziski wants to merge 5 commits into
Open
jgrodziski wants to merge 5 commits into
jgrodziski wants to merge 5 commits into
Conversation
A profile's lexicon gains `qualified`: the kinds a language names by a qualified path, each with the separator that ends the qualifier. Java's package by `.`, C#'s namespace, Elixir's module and protocol, Clojure's namespace and Python's module by `.`, Ruby's class, module and constant by `::`, PHP's namespace by `\`, and every file kind by `/`. Go's package and Rust's module are named by one identifier, so neither is listed. The vocabulary will weigh the words before the last separator as incidental ones: on the fixtures, full names made `acme` weigh 392 in Elixir and 468 in Ruby against about 50 for the next domain word. validateProfile reports a qualified kind the profile does not define (`lexicon-unknown-kind`) and a separator that is empty or holds a letter, a mark or a digit (`lexicon-not-a-separator`). `profiles` prints the row and carries the key in --json; METAMODEL §5.3, the CLI reference and both website pages follow. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`splitIdentifier` now reports, beside the tokens, how many of them spell the name's qualifier: what precedes the last separator of a kind the lexicon declares qualified (`AcmeOrder::Order::Line` → 3 of acme, order, order, line; `packages/order/src/basket.ts` → 3). The tokens themselves never change, a property pins it; only the boundary is new. The interface prefix now reads the own name after the qualifier. The weights gain a `qualifier` key, 1 by default: a container's words, spoken again by every name it qualifies, weigh as incidental ones. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`buildVocabulary(graph, options)` counts the words of every kept entity's name into the scopes the fold puts it in: its type (containingType), its module (containingModule) and the corpus. A stub never speaks, whatever the view: its name was chosen elsewhere. An entity's language is its model's, read from the union, never its id. A word weighs its carrier's role, or `qualifier` (1) before the last separator. Each scope keeps `[term, weight, occurrences, carriers]` rows by weight then term, its entities by role, what it skipped (unnamed, conventional, numeric) and its distinct and distinctDomain counts. Each term carries its spread: the module and type scopes that speak it, and the one module when only one does. A word two languages class apart is two terms. Kept entities no module contains count at the corpus alone and are listed. The artifact, `codegraph.vocabulary/1`, records its view, weights, classes and the technical list's size and sha256. It is compact by default; `pretty` writes one term and one scope per line. Tested on a hand-counted corpus and by properties over generated ones: closure, conservation (every kept entity in one module or unplaced), and the same bytes whatever the order entities arrive in. The package now depends on @codegraph/analyzer, as PLAN.md planned. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Each fixture's model now has its words committed beside it: `fixtures/<name>/expected/vocabulary.json` for java, csharp, typescript, elixir, ruby and unicode. They are pretty-printed, one term and one scope per line, so a change to the split or the weights reads in review as the words it moved. The vocabulary's tests rebuild them byte for byte, check their closure, and rebuild them over a fresh graph; CODEGRAPH_UPDATE_SNAPSHOTS=1 rewrites them. Each README counts one word by hand, name by name: Java's and C#'s `order` (40 and 42, through the package and namespace qualifiers), C#'s `priceable` (no word `i`), TypeScript's `legacy` and `packages` (path qualifiers), Elixir's and Ruby's `pricing` (dots, `::` and slashes). A test pins those counts, so a snapshot rewritten in one command still has to agree with them. The C# fixture gains a README for its count, pointing at docs/csharp-extractor.md for the rest. The unicode README notes that terms sort by code unit too. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
PLAN §20.1 now has the lexicon's `qualified` key and the measurement that called for it. It also covers the splitter's `qualifier` count and its properties, the `qualifier` weight row, and the aggregation as built: stubs never speak, nearest-fold scopes, per-scope counts, one term per (word, class), unplaced and withoutLexicon. The artifact section adds the technical list's sha256 and the pretty form. In §20.4 the qualifier, aggregate.ts, artifact.ts and the snapshots (the five fixtures plus unicode, hand counts pinned by a test) are checked. drift.ts moves to the command's item, the next PR. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Deploying with
|
| Status | Name | Latest Commit | Preview URL | Updated (UTC) |
|---|---|---|---|---|
| ✅ Deployment successful! View logs |
codegraph | 2b1bdf3 | Commit Preview URL Branch Preview URL |
Oct 26 2026, 07:04 AM |
This was referenced Oct 26, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Third step of the word cloud / glossary (#6), milestone M19a in PLAN.md §20. Stacked on #27 (itself on #26): review those first; this PR's diff is the five commits after #27.
It counts the words of a whole model:
buildVocabulary(graph)→ thecodegraph.vocabulary/1artifact, with committed snapshots for every fixture. Nothing in the CLI calls it yet; PR 4 adds thecodegraph vocabularycommand and the drift report.A qualified name's words weigh 1
Measured before this PR, with full names counted at their carrier's weight: Ruby's
acmeweighed 468 and Elixir's 392, against about 50 for the next domain word; TypeScript'spackagesweighed 168. A container's words are spoken again by every name it qualifies, so they drowned the words that name things.Lexicon.qualified: the kinds a language names by a path, each with the separator that ends the qualifier..for Java packages, C#/Clojure namespaces, Python modules, Elixir modules and protocols;::for Ruby classes, modules and constants;\for PHP namespaces;/for every file kind. Go's package and Rust's module are one identifier, so neither is listed.validateProfilegainslexicon-not-a-separator(empty, or holding a letter, mark or digit) and reports an unknown kind aslexicon-unknown-kind.codegraph profiles, METAMODEL §5.3,docs/cli.mdand both website pages follow.qualifier: how many of them precede the last separator.AcmeOrder::Order::Line→ acme, order, order, line with qualifier 3. C#'s interface prefix now reads the own name, soAcme.IOrderdrops itsI.qualifierkey, 1 by default.This field was offered as a commit on #26; it lives here instead so #27 needs no rebase or force-push.
After the change, the corpus's top domain words: Ruby
order147,acme96; Elixirorder99,acme49; TypeScriptorder79,acme30,packages21; Javaorder40.Aggregation
buildVocabulary(graph, {view?, weights?, technical?, name?}), pure over the analyzer's graph:Stringis a word another codebase chose.TModuleandTType. Each entity counts in the corpus, itscontainingModuleand itscontainingType(the fold's nearest self-or-ancestor). An entity no module contains counts at the corpus alone and is listed indiagnostics.unplaced.typeis a Go keyword, a Java word) is two terms.[term, weight, occurrences, carriers]rows by weight then term. An entity saying a word twice is one carrier. It also keeps its entities by role, what it skipped (unnamed, conventional, numeric),distinctanddistinctDomain.exclusive).The artifact
codegraph.vocabulary/1records its view, weights, classes, and the technical list's size and sha256, since a count means nothing without them. Terms are sorted by code unit then class; scopes are the corpus, then modules, then types, each in id order; rows address terms and parents by index.vocabularyToJsonStringis compact by default;{pretty: true}writes one term and one scope per line.Tests
toString, two stubs), plus option, unplaced, no-lexicon (cobol), Ruby word-said-twice and multi-language cases.fixtures/{java,csharp,typescript,elixir,ruby,unicode}/expected/vocabulary.json, pretty, rebuilt byte for byte, closed, and identical over a fresh graph.CODEGRAPH_UPDATE_SNAPSHOTS=1rewrites them.orderorderpriceablelegacypackagespricingpricingThe C# fixture had no README; it gains a short one that points to
docs/csharp-extractor.mdand holds the count.Worth a look in review: a few classifications follow from the profiles and read oddly in the snapshots. Java's
abstract(fromAbstractOrder) and Ruby'sclass(fromClassMethods) tag as keywords, because those profiles reserve them.Also
@codegraph/vocabularynow depends on@codegraph/analyzer, as PLAN §20 planned.drift.tsmoves to the command's item, for PR 4.Verification
pnpm -r buildandpnpm -r typecheckgreen; eslint clean on every changed.ts/.mjs/.js;pnpm run gen:schemasleavesschemas/unchanged; the website profiles page regenerates with no diff.node:sqlitewarning test noted on feat(core): language lexicons on profiles (M19a) #26.Part of #6.
🤖 Generated with Claude Code