Add --group-depth (directory rollup) and make --architecture's scan reproducible - #25
Merged
Merged
Conversation
…eproducible --architecture draws one box per file, which is unreadable on any repo past a handful of files. --max-render was the only answer and it is lossy: it drops the least-busy files outright, taking every edge that touched them with it. --group-depth <n> rolls files up into their first n directory segments and draws those instead, summing the call weights of every file pair that collapses into the same group pair. Cross-module calls are all preserved; calls that become intra-group are dropped, exactly as same-file calls already are, because this diagram is about coupling between modules rather than inside them. Measured on a real repo (honeyslate): 20 file nodes -> 2 groups at depth 1, 4 at depth 2. The rollup runs after probing, never during — probes must stay file-exact (parseNodeCalls' expectedFile check, isTestRef, duplicate-name attribution all key off the real path), so this stays a pure, unit-testable view over the same data instead of a second scan mode. It composes with --max-render (which then caps groups) and claims its own --embed marker id (codeshot:arch-d<n>) so the per-module picture cannot silently overwrite a committed per-file one. Separately: enumerateSymbols now sorts by (filePath, name) before the --max-symbols slice. codegraph's own result order for the enumeration query is unspecified, so a repo larger than the cap was probed on a different subset run to run — the same unchanged repo could render a different diagram, and a committed diagram guarded by --check could fail CI for no code reason. The sort is by code point, deliberately not localeCompare, whose implementation-default locale varies with the environment and the Node binary's ICU build and would reintroduce the same variance one layer down. This makes the kept subset deterministic, not representative: it is a path-sorted prefix, so symbolBudgetWarning now says that unprobed late-path files can appear to call nothing when they do. 126 tests pass (+18). Both committed diagrams still pass --check unchanged.
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.
Why
--architecturedraws one box per file, which is unreadable on any repo past a handful of files.--max-renderwas the only answer and it is lossy: it drops the least-busy files outright, taking every edge that touched them with it.What
--group-depth <n>(--architectureonly) rolls files up into their firstndirectory segments and draws those instead, summing the call weights of every file pair that collapses into the same group pair. Every cross-module call is preserved; calls that become intra-group are dropped, exactly as same-file calls already are, because this diagram is about coupling between modules rather than inside them.Measured on a real repo (
honeyslate, 200 symbols probed):--group-depth 1backend/,frontend/)--group-depth 2backend/alembic/ → backend/app/weight 11Design notes:
parseNodeCalls'expectedFilecheck,isTestRef, duplicate-name attribution all key off the real path), so this is a pure, unit-testable view over the same data rather than a second scan mode.--max-render(which then caps groups;renderTruncationNotesaysgroups).--embedmarker id (codeshot:arch-d<n>), so the per-module picture cannot silently overwrite a committed per-file one. Unset--group-depthkeeps the originalcodeshot:archid, so existing docs are untouched.--architecturerather than silently ignored — a silently-dropped flag reads as "grouping applied" in exactly the diagram you would then trust.groupCollapseWarningdistinguishes the two blank-diagram causes, which want opposite advice: one group (depth too coarse, go deeper) vs several groups with no calls between them (genuinely no cross-module coupling; deeper stays blank).Reproducible enumeration.
enumerateSymbolsnow sorts by(filePath, name)before the--max-symbolsslice. codegraph's own result order for the enumeration query is unspecified, so a repo larger than the cap was probed on a different subset run to run — the same unchanged repo could render a different diagram, and a committed diagram guarded by--checkcould fail CI for no code reason. The sort is by code point, deliberately notlocaleCompare, whose implementation-default locale varies with the environment and the Node binary's ICU build and would reintroduce the same variance one layer down.This makes the kept subset deterministic, not representative: it is a path-sorted prefix, so
symbolBudgetWarningnow says that unprobed late-path files can appear to call nothing when they do.Verification
npm test→ 126 passed, 0 failed (+18), including 4 CLI-level tests run against this repo's real codegraph index.test/run.js → render/callgraph.js(31) becomestest/ → render/(31), test group still dashed.honeyslateas above.Docs updated in all three files, including the honest caveat that a grouped diagram's summed weights are legitimately lower than the per-file one's (that difference is intra-module traffic, not a lost edge).
🤖 Generated with Claude Code