Skip to content

feat(viewer): read the exported graph as a graph, and fix hierarchy edge cases - #334

Merged
forhappy merged 2 commits into
mainfrom
codex/hierarchy-quality-and-viewer-ux
Sep 25, 2026
Merged

forhappy merged 2 commits into
mainfrom
codex/hierarchy-quality-and-viewer-ux

Conversation

@forhappy

Copy link
Copy Markdown
Contributor

Summary

Two changes that came out of building graphs for real repositories and reading the exported pages:

  1. The hierarchy no longer publishes a level that does not decompose the repository. A coarsening step that collapsed every group into one was published as the root, so an export opened on a single node; the builder now refuses that level and stops at the achieved partition. The location cut had the same defect on wide repositories: it escaped a single unexpandable bucket once, into the directories the repository actually publishes, inside a bounded multiple of the target, and records escapedSingleBucket with an honest budgetSatisfied.
  2. The export reads as a graph. A one-view workbench folds its rail instead of rendering a one-item menu; communities are named with the repository's own words from the embedded hierarchy when a build published no labels.json; selecting a node or opening a community dismisses the community list and hands the column to the inspector (Escape returns); a selected community reports its own evidence — symbols, sub-groups, couplings, cohesion, conductance, boundary kinds, durable group id — instead of a bubble's drawn degree.

Motivation

Reading a real pallets/flask export and a colinhacks/zod export surfaced the feedback this change answers:

  • TheAlgorithms/Python published 3 root communities, one of them a single bucket named after a hub member holding 16,637 of the repository's 16,858 symbols — the overview was effectively one node.
  • A tiny repository (one community) published one level holding one group, and the export opened on it: one node for the whole repository.
  • The export carried a "Code graph" card with nothing behind it, communities read Community 0 … Community 111, selecting a node left the community list occupying the inspector's space, and a selected community reported degree 0 / incoming 0 / outgoing 0 because the bubble's couplings are not drawn in a narrowed canvas.
  • A coarse group offered "Open community" using its group index, so flask's level-0 src/flask would open community 0's symbols.

Verification

cargo fmt --all -- --check
cargo clippy -p compass-graph -p compass-cli --all-targets --all-features --locked -- -D warnings
cargo clippy --workspace --lib --bins --locked -- -D warnings
cargo test --workspace --lib --bins --locked
cargo test -p compass-graph -p compass-output -p compass-semantic-diff --locked
cargo test -p compass-cli --locked
cargo test -p compass-cli --test compass_product --test viewer_export_cli --locked
sh scripts/check_product_boundary.sh
CARGO_TARGET_DIR=… TSLP_PARSER_SOURCE_DIR=… ./scripts/qualify_code_graph_v1.sh --fixtures-only
cargo run -p compass-graph --example community_hierarchy_qualification   # all acceptance entries true
npm run typecheck:js
npm run test:js        # 321 viewer unit, 143 other, 106 Playwright browser tests
node scripts/check_viewer_assets.mjs

Real-repository qualification (26 extractions across flask, zod, axum, claude-code, kache, litestream, scrcpy, gson, ripgrep, serde, anyhow, graphify, mdBook, browser-use, fastapi, stable-diffusion-webui, v2rayN, spec-kit, TheAlgorithms/Python, and others): 17 hierarchies are byte-identical before and after; TheAlgorithms/Python changes from 3 root groups to 49 named directory groups with every member preserved. Exports were rendered in Chromium for flask, zod, and TheAlgorithms/Python.

One pre-existing failure is unrelated and reproduces on a clean checkout at the base commit: compass-core --test code_graph_v1_publication_resilience → missing_dotnet_references_are_external_and_do_not_abort.

Compatibility and documentation

  • community-hierarchy.json keeps schema compass.community-hierarchy/1 and its identity strings; what changes is the level composition a build publishes. COMPATIBILITY.md now states that a level's composition is data no consumer may assume, and that mergeEvidence.escapedSingleBucket is additive. Existing artifacts stay readable — an export still opens on a level an older artifact published.
  • The standalone page's opening rule, rail, community naming, and inspector behaviour are documented in docs/reference/outputs.md, with the export html help text and docs/reference/commands.md updated for the opening rule.
  • CHANGELOG.md records both changes.

Checklist

  • The change is focused and excludes unrelated formatting or generated files
  • Tests cover changed behavior, or this pull request changes documentation only
  • User-facing commands, flags, limits, and examples are documented
  • Compatibility or migration effects are described
  • No credentials, private source code, or sensitive report details are included
  • I agree to license my contribution under MIT OR Apache-2.0
  • I followed the Compass code of conduct

A level that holds one group is the whole repository drawn as a single node:
it is the coarsening step that merged everything, a bucket that falls back from
the directory label rules to a hub member, and an export that opens on it shows
one blob. The builder now refuses it — the achieved level below stays the root —
and coarsening keeps the smallest cut that still decomposes the level instead of
the smallest cut overall.

The location cut grew the same defect for wide repositories: TheAlgorithms/Python
publishes 49 top-level directories against a root target of 24, so the exact cut
could not expand at all and the root held 3 groups, one of them named after a hub
member and holding 16,637 of the repository's 16,858 symbols. A single bucket
that cannot expand now escapes once into the directories the repository publishes,
inside a bounded multiple of its target, and records `escapedSingleBucket` with
the achieved count. The same repository now opens on 49 named directories and
reports `budgetSatisfied: false` instead of hiding the structure.

The qualification report gains `levelsDecompose` and `locationEscapeQualified`,
a fixture whose layout is wider than its budget, and a fixture the escape cuts;
the CLI product test asserts that only the published partition may hold one
group. 18 real repositories were compared before and after: 17 are byte-identical
and TheAlgorithms/Python changes as described. COMPATIBILITY.md records that a
level's composition is data no consumer may assume.
Reading a real `compass export html` page surfaced four things:

- A workbench with one view rendered a one-item "Code graph" menu. It no longer
  renders the view list and folds its rail to the brand, the snapshot identity,
  and the disclosure, so the canvas takes the width.
- A build without `labels.json` named every community `Community 0`,
  `Community 1`, … in the panel, on aggregated bubbles, and in the inspector.
  The embedded hierarchy holds one group per community at its finest level, so
  the viewer names them with those labels and never replaces a label the export
  published. flask now reads `tests`, `src/flask`, `src/flask/sansio`; zod —
  2,753 communities and no labels file — reads `packages/zod/src/v4/locales`,
  `packages/zod/src/v4/core`, and the rest.
- Selecting a node or opening a community left the community list in the column
  the inspector needed. The list is now dismissed while the reader inspects
  something and returns with the overview; Escape steps back out of a node
  selection. History comparisons keep the list as a disclosure so both sides of
  a diff stay reachable.
- A selected community reported the bubble's own drawn degree, which is zero by
  construction. The inspector now shows the evidence the hierarchy holds:
  symbols, sub-groups, couplings to the groups beside it, cohesion, conductance,
  boundary kinds, and the durable group id. A group of a coarser level no longer
  offers to open the community whose *number* it happens to share — it offers the
  descent instead, because only the finest level pairs a group with a community.

Covered by a new `communityFacts` suite, the inspector/workbench unit tests, a
new `graph-ux` browser spec, and two export fixtures (a one-view clustered
export and a three-level drill-down export). Verified against real exports of
pallets/flask, colinhacks/zod, and TheAlgorithms/Python rendered in Chromium.
@forhappy
forhappy merged commit c3cb37a into main Sep 25, 2026
7 of 14 checks passed
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