Skip to content

fix(stats): stop double-counting dead symbols in the roles map (#2383) - #2522

Merged
carlos-alm merged 2 commits into
mainfrom
fix/issue-2383
Aug 15, 2026
Merged

fix(stats): stop double-counting dead symbols in the roles map (#2383)#2522
carlos-alm merged 2 commits into
mainfrom
fix/issue-2383

Conversation

@carlos-alm

Copy link
Copy Markdown
Contributor

Summary

codegraph stats double-counted every dead symbol. The roles map contained the four granular dead-* sub-roles and an aggregate dead key that was their sum, sitting as a peer in the same flat map — so any total over the map (including the printed "classified symbols" headline) counted dead symbols twice.

Root cause was in countRoles (SQL path) and aggregateRolesFromNative (native path) in src/domain/analysis/module-map.ts — both injected roles.dead = deadTotal into the same object that already held the four dead-* counts.

Changes

  • countRoles/aggregateRolesFromNative now return { roles, deadTotal }deadTotal is a sibling field in the stats result, never a peer key inside roles. stats -j now emits the shape the issue proposed: { "roles": {...no aggregate dead key...}, "deadTotal": N }.
  • printRoles (text output) now renders the dead family as an indented sub-group under a single dead N heading instead of interleaving it with the other roles in one sorted grid.
  • Fixed an accompanying printCountGrid cosmetic bug: a column value at or beyond the fixed pad width ran straight into the next column with no separator (e.g. dead-unresolved 86dead 86).
  • Confirmed no Rust-side change needed: crates/codegraph-core's fetch_role_counts does a raw GROUP BY role with no aggregate injected — the double-count was purely in the JS aggregation layer that both engines' stats paths share.

Verification

  • lint: pass
  • npx vitest run tests/integration/roles.test.ts: 22/22 pass (updated the roles.dead assertion to deadTotal, added a regression test asserting the map no longer carries an aggregate dead peer key)
  • codegraph diff-impact --staged: 7 functions changed, 0 external callers affected beyond stats()
  • Manually verified against this repo's own graph: roles no longer contains dead, deadTotal correctly sums the four sub-roles, and the text headline total now matches the sum of disjoint categories

Note: a full local npm test run in this environment (Node v26.4.0, no other version available) shows ~200 unrelated failures that also reproduce on an untouched origin/main checkout and do not touch any file this PR changes; CI (which pins Node 22, per .github/workflows/ci.yml) has been consistently green on recent main pushes. Filed as #2521 for separate tracking — not blocking this fix.

Closes #2383

The `dead` key was injected into the flat roles map as the sum of its
own dead-* sub-roles, so any total over the map (including the printed
"classified symbols" headline) counted every dead symbol twice.
deadTotal is now returned as a sibling of roles instead of a peer key,
and the text renderer shows it as an indented sub-group heading.

docs check acknowledged: internal stats aggregation bugfix, no
README/CLAUDE.md/ROADMAP surface area changed.

Impact: 7 functions changed, 0 affected
@greptile-apps

greptile-apps Bot commented Aug 15, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

The PR separates the aggregate dead-symbol count from the flat roles map, preventing dead symbols from inflating classified-symbol totals.

  • Both JavaScript and native stats paths now return deadTotal alongside granular role counts.
  • Text output presents dead roles as a grouped hierarchy and preserves spacing between grid columns.
  • Integration coverage verifies that roles.dead is absent and deadTotal equals the granular dead-role sum.

Confidence Score: 5/5

The PR appears safe to merge, with both stats engines and the text renderer consistently using the corrected role-count shape.

The aggregate dead count is removed from the disjoint roles map, retained as a sibling result field, and verified against the granular dead-role sum without leaving a concrete caller or output path broken.

Important Files Changed

Filename Overview
src/domain/analysis/module-map.ts Separates the aggregate dead count from granular role counts consistently across the JavaScript and native stats paths.
src/presentation/queries-cli/overview.ts Groups dead-role output beneath its aggregate heading and prevents adjacent grid columns from running together.
tests/integration/roles.test.ts Updates the stats contract assertion and adds regression coverage against reintroducing the aggregate role-map key.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart LR
  A["SQL or native role counts"] --> B["Aggregate granular dead-* roles"]
  B --> C["roles: disjoint role counts"]
  B --> D["deadTotal: aggregate count"]
  C --> E["Classified-symbol headline"]
  C --> F["Live-role grid"]
  C --> G["Dead sub-role details"]
  D --> G
Loading

Reviews (1): Last reviewed commit: "merge: bring branch up to date with main" | Re-trigger Greptile

@github-actions

Copy link
Copy Markdown
Contributor

Codegraph Impact Analysis

6 functions changed9 callers affected across 3 files

  • countRoles in src/domain/analysis/module-map.ts:261 (3 transitive callers)
  • aggregateRolesFromNative in src/domain/analysis/module-map.ts:425 (3 transitive callers)
  • buildStatsFromNative in src/domain/analysis/module-map.ts:439 (3 transitive callers)
  • buildStatsFromJs in src/domain/analysis/module-map.ts:525 (3 transitive callers)
  • printCountGrid in src/presentation/queries-cli/overview.ts:130 (6 transitive callers)
  • printRoles in src/presentation/queries-cli/overview.ts:229 (2 transitive callers)

@carlos-alm
carlos-alm merged commit 497926e into main Aug 15, 2026
27 checks passed
@carlos-alm
carlos-alm deleted the fix/issue-2383 branch August 15, 2026 19:48
@github-actions github-actions Bot locked and limited conversation to collaborators Aug 15, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bug(stats): roles map double-counts dead symbols — aggregate 'dead' key is a peer of its own sub-roles

1 participant