What this adds
The Knowledge Gaps section catches node-level gaps: isolated nodes, thin communities. It says nothing about cluster-level blind spots, meaning two healthy communities that sit close in the graph (several shared 2-hop intermediaries) but have few or no direct topical edges between them. In a documentation corpus that pattern is usually a real editorial finding: the connection you assumed was written down but never was.
I built this as a post-processor for my own corpus and it surfaced a genuine miss on the first run. Sample output from a 148-node, 12-community doc corpus (a civic infrastructure project I lead):
## Structural Gaps
_Pairs of well-formed topic clusters that sit close in the graph but are barely
connected to each other. Each is a candidate blind spot; the bridging question
is what would close it._
- **Layer 3 — Provenance (Chain of Custody) ⇄ Layer 4 — Federated AI (Collective Learning)**
Bridging question: **What would directly link `Layer 3 — Provenance (Chain of Custody)`
and `Layer 4 — Federated AI (Collective Learning)` - for instance, between
`Layer 2 — The Mesh Protocol (Federated Discovery)` and `Flower (flwr) Federated Learning Runtime`?**
_These two topics share 5 intermediary concepts but have 1 direct edge between them
(structural-gap score 0.0694). A likely blind spot: the graph treats them as
neighbours-of-neighbours, not as directly related._
The architecture in that corpus depends on exactly that coupling, and no document states it. This is the structural-gap reading InfraNodus is known for, done deterministically and offline.
How it works (no LLM calls, sub-second)
- Gate: a community is well-formed if it has at least 4 real nodes (reusing the
_is_file_node / _is_concept_node / _is_json_key_node exclusions) and connectance (2·edges/nodes) of at least 1.2. Connectance rather than cohesion_score because edge density falls mechanically as a community grows, which would drop the largest, most central clusters.
- Proximity: shared 2-hop neighbours, each weighted 1/sqrt(degree) so one mega-hub cannot inflate every pair, normalized by the smaller community's real-node count.
gap_score = proximity / (1 + observed_cross_edges), where imports/contains/method edges do not count as connection (wiring is not topical linkage).
- Pairs with no shared neighbours are excluded. Unrelated is not a gap.
- Each gap renders with a deterministic bridging question built from the two clusters' lead concepts.
Where it lives
community_gaps() in analyze.py (same signature shape as suggest_questions), rendered by report.generate(), which computes it by default when the new structural_gaps kwarg is None. So every report surface gets the section (skill pipeline, cluster-only, watch and hook rebuilds) with zero call-site changes, and the skill files do not change at all: tools.skillgen --check passes untouched. The section is omitted when no gaps are found, so well-linked graphs render byte-identical to today. Passing structural_gaps=[] suppresses it.
Honest scope note
On code corpora this mostly stays quiet, and that seems correct: import edges are explicit and 2-hop overlap between gated communities is sparse. I validated on a 370-node Next.js repo and got zero to one weak gap, and only after filtering JSON key nodes out of the shared-neighbour evidence (without _is_json_key_node, dependencies and scripts dominate the results). The feature earns its place on doc and mixed corpora.
Related: #1768 reconciled the two existing gap counts; this extends the same story from nodes to cluster pairs. #1254 and #198 are about generating cross-layer edges; this is the complement, reporting where topical edges are missing.
I have this implemented and tested against 0.9.13: ten tests covering the gate, damping, structural-edge exclusion, JSON-noise exclusion, determinism, and the report rendering paths, with the full suite passing locally (2539 passed; one pre-existing network-dependent security test fails identically on unmodified main in my sandbox) and skillgen untouched. Happy to open the PR (rebased to current main) if this is in scope. Also fine if you would rather keep the report surface small; it works as a standalone post-processor too, which is how I run it today.
What this adds
The Knowledge Gaps section catches node-level gaps: isolated nodes, thin communities. It says nothing about cluster-level blind spots, meaning two healthy communities that sit close in the graph (several shared 2-hop intermediaries) but have few or no direct topical edges between them. In a documentation corpus that pattern is usually a real editorial finding: the connection you assumed was written down but never was.
I built this as a post-processor for my own corpus and it surfaced a genuine miss on the first run. Sample output from a 148-node, 12-community doc corpus (a civic infrastructure project I lead):
The architecture in that corpus depends on exactly that coupling, and no document states it. This is the structural-gap reading InfraNodus is known for, done deterministically and offline.
How it works (no LLM calls, sub-second)
_is_file_node/_is_concept_node/_is_json_key_nodeexclusions) and connectance (2·edges/nodes) of at least 1.2. Connectance rather thancohesion_scorebecause edge density falls mechanically as a community grows, which would drop the largest, most central clusters.gap_score = proximity / (1 + observed_cross_edges), where imports/contains/method edges do not count as connection (wiring is not topical linkage).Where it lives
community_gaps()in analyze.py (same signature shape assuggest_questions), rendered byreport.generate(), which computes it by default when the newstructural_gapskwarg is None. So every report surface gets the section (skill pipeline, cluster-only, watch and hook rebuilds) with zero call-site changes, and the skill files do not change at all:tools.skillgen --checkpasses untouched. The section is omitted when no gaps are found, so well-linked graphs render byte-identical to today. Passingstructural_gaps=[]suppresses it.Honest scope note
On code corpora this mostly stays quiet, and that seems correct: import edges are explicit and 2-hop overlap between gated communities is sparse. I validated on a 370-node Next.js repo and got zero to one weak gap, and only after filtering JSON key nodes out of the shared-neighbour evidence (without
_is_json_key_node,dependenciesandscriptsdominate the results). The feature earns its place on doc and mixed corpora.Related: #1768 reconciled the two existing gap counts; this extends the same story from nodes to cluster pairs. #1254 and #198 are about generating cross-layer edges; this is the complement, reporting where topical edges are missing.
I have this implemented and tested against 0.9.13: ten tests covering the gate, damping, structural-edge exclusion, JSON-noise exclusion, determinism, and the report rendering paths, with the full suite passing locally (2539 passed; one pre-existing network-dependent security test fails identically on unmodified main in my sandbox) and skillgen untouched. Happy to open the PR (rebased to current main) if this is in scope. Also fine if you would rather keep the report surface small; it works as a standalone post-processor too, which is how I run it today.