Summary
suggest_questions()'s "isolated/weakly-connected nodes" question overcounts by ~3x compared to the report's own "Knowledge Gaps" section, because it's missing filters that report.py already applies. The inflated number makes a healthy graph look like it has a major documentation/connectivity problem when it doesn't.
Where
graphify/analyze.py, suggest_questions(), the "Isolated or weakly-connected nodes" block (~line 504):
isolated = [
n for n in G.nodes()
if G.degree(n) <= 1 and not _is_file_node(G, n) and not _is_concept_node(G, n)
]
graphify/report.py's "Knowledge Gaps" section (~line 237) does the same computation but adds one more filter:
isolated = [
n for n in G.nodes()
if G.degree(n) <= 1
and not _is_file_node(G, n)
and not _is_concept_node(G, n)
and G.nodes[n].get("file_type") != "rationale"
]
Repro
On a 2213-node / 4862-edge real-world graph (Python + TS/React monorepo), the two computations disagree:
suggest_questions() count (no rationale exclusion): 757
report.py "Knowledge Gaps" count (with rationale exclusion): 245
Both appear in the same generated report (GRAPH_REPORT.md) — the Suggested Questions section says "757 weakly-connected nodes found — possible documentation gaps," while the Knowledge Gaps section a few paragraphs earlier says 245, for what's presented as the same concept. That's an internal inconsistency a reader will notice.
Root cause of the inflation
I broke down the 757 by file_type:
| file_type |
count |
% |
is it a real gap? |
rationale |
512 |
68% |
No — these are auto-extracted docstring/comment nodes attached to their parent symbol by exactly one edge (e.g. a node whose label is the first line of a function's docstring). Degree=1 is the intended shape for these, not a missing edge. |
code (JSON leaves) |
86 |
11% |
No — scalar values in JSON config trees (e.g. shadcn's components.json: $schema, rsc, baseColor, cssVariables). Same "single parent edge by construction" pattern as _JSON_NOISE_LABELS already tries to filter for package.json, but that noise list doesn't cover other config schemas. |
code (TS Props interfaces) |
~130 |
17% |
No — single-use React/TypeScript prop-type interfaces declared immediately above the one component that uses them (FlowProps, TopBarProps, BottomPanelProps, etc.). Idiomatic single-use types, not undocumented code. |
| everything else |
~29 |
4% |
plausible real gaps |
So even the smaller, "already filtered" 245 count from report.py still isn't clean — it doesn't exclude JSON config leaves or single-use TS prop interfaces, both of which are structural/idiomatic patterns rather than architectural gaps.
Suggested fix
- Add the same
file_type != "rationale" filter to suggest_questions()'s isolated-node computation so it doesn't disagree with report.py.
- Extend the JSON-leaf exclusion (currently
_is_json_key_node + _JSON_NOISE_LABELS, scoped to package-manager-style keys) to catch degree-1 leaves in any JSON file — the label being a leaf value under a JSON source with degree ≤1 is a strong enough signal regardless of which specific JSON schema it is.
- Consider a heuristic for single-use TS/JS
*Props/*Options/*Config interfaces (declared in the same file as, and only referenced by, one consumer) — these are idiomatic and shouldn't count toward "knowledge gaps" either.
Happy to share the exact reproduction script (loads graph.json, replicates both filters) if useful — got exact parity with both 757 and 245 by hand-rolling the same predicates.
Summary
suggest_questions()'s "isolated/weakly-connected nodes" question overcounts by ~3x compared to the report's own "Knowledge Gaps" section, because it's missing filters thatreport.pyalready applies. The inflated number makes a healthy graph look like it has a major documentation/connectivity problem when it doesn't.Where
graphify/analyze.py,suggest_questions(), the "Isolated or weakly-connected nodes" block (~line 504):graphify/report.py's "Knowledge Gaps" section (~line 237) does the same computation but adds one more filter:Repro
On a 2213-node / 4862-edge real-world graph (Python + TS/React monorepo), the two computations disagree:
suggest_questions()count (no rationale exclusion): 757report.py"Knowledge Gaps" count (with rationale exclusion): 245Both appear in the same generated report (
GRAPH_REPORT.md) — the Suggested Questions section says "757 weakly-connected nodes found — possible documentation gaps," while the Knowledge Gaps section a few paragraphs earlier says 245, for what's presented as the same concept. That's an internal inconsistency a reader will notice.Root cause of the inflation
I broke down the 757 by
file_type:rationalecode(JSON leaves)components.json:$schema,rsc,baseColor,cssVariables). Same "single parent edge by construction" pattern as_JSON_NOISE_LABELSalready tries to filter forpackage.json, but that noise list doesn't cover other config schemas.code(TSPropsinterfaces)FlowProps,TopBarProps,BottomPanelProps, etc.). Idiomatic single-use types, not undocumented code.So even the smaller, "already filtered" 245 count from
report.pystill isn't clean — it doesn't exclude JSON config leaves or single-use TS prop interfaces, both of which are structural/idiomatic patterns rather than architectural gaps.Suggested fix
file_type != "rationale"filter tosuggest_questions()'s isolated-node computation so it doesn't disagree withreport.py._is_json_key_node+_JSON_NOISE_LABELS, scoped to package-manager-style keys) to catch degree-1 leaves in any JSON file — the label being a leaf value under a JSON source with degree ≤1 is a strong enough signal regardless of which specific JSON schema it is.*Props/*Options/*Configinterfaces (declared in the same file as, and only referenced by, one consumer) — these are idiomatic and shouldn't count toward "knowledge gaps" either.Happy to share the exact reproduction script (loads
graph.json, replicates both filters) if useful — got exact parity with both 757 and 245 by hand-rolling the same predicates.