Skip to content

Weakly-connected / "Knowledge Gaps" signal doesn't filter AST-origin declarations without a () suffix — false positives dominate #4205

Description

@rsaer

suggest_questions() and the GRAPH_REPORT.md "Knowledge Gaps" section both treat degree(n) <= 1 as "possible documentation gap," filtered only by _is_file_node, _is_concept_node, and file_type != "rationale" (analyze.py:549-563, report.py:327, shared via _real_node).

_is_file_node (analyze.py:65-90) only recognizes three shapes: a label matching its own filename, a .method() stub, or a bare function_name() stub. _is_concept_node (analyze.py:183-193) only excludes nodes with an empty or extension-less source_file.

Neither catches a plain AST declaration whose label has no trailing () — e.g. a TypeScript type/interface alias, an enum member, a local const, or a JSON config key — even when it has a real source_file and _origin == "ast", and even when its only edge is the contains edge from its own file.

Concrete repro: in a Next.js/Medusa monorepo, three file-local row-shape types declared back-to-back:

type TabRow = { id: string; ... };
type ContributionRow = { id: string; ... };
type TabRecognition = { vendor_id: string; ... };

(used only as manager.execute<ContributionRow[]>(...) generic parameters, never exported) — each got exactly one contains edge and were then surfaced by suggest_questions() as "What connects ContributionRow, TabRecognition, TabRow to the rest of the system? — 1,556 weakly-connected nodes found — possible documentation gaps." They are not a gap; they're working as designed.

This isn't a one-off: in a ~5,000-node graph of that repo, 1,718 of 2,033 weakly-connected nodes have _origin == "ast", vs. 260 _origin == "semantic" (docs/PRD-derived) and 55 untagged. Manually sampling 26 of the semantic ones found zero confirmed real gaps too (mostly risk-register table rows and section headings) — but the AST-origin majority is the larger, more mechanically-fixable false-positive source.

Suggested fix: have _is_file_node (or a sibling check) also exclude any node with _origin == "ast" whose only edge is a single contains edge from its declaring file — regardless of whether the label ends in (). That one change would remove the great majority of the 1,718 false positives in our graph without touching the semantic/doc side of gap detection.


(graphify version: graphifyy 0.9.79, installed via uv tool install)

Activity

  1. github-actions commented on Oct 7, 2026

    @github-actions

    Thanks for opening this issue, @rsaer. A maintainer will take a look soon.

    If you would like to discuss it in real time, come say hi on our Discord server. For longer-form questions and ideas there is also GitHub Discussions.

    To help us triage, please make sure the report includes what you expected, what actually happened, and the steps (and a small sample) to reproduce it.

  2. nothariharan commented on Oct 8, 2026

    @nothariharan
    Contributor

    Fixed in PR #4212.

    What changed:

    • New _is_contains_only_ast_node helper in graphify/analyze.py: returns True for AST-tier nodes (_origin == 'ast', with the legacy source_location shape fallback via _is_ast_tier) whose degree is exactly 1 and whose single edge is a contains.
    • That predicate is now applied in both gap surfaces: the suggest_questions() isolated-nodes question and the GRAPH_REPORT.md Knowledge Gaps isolated count.

    Behavior notes:

    • AST declarations like ype TabRow = ... (no () suffix, single contains edge) are no longer reported as weakly-connected gaps.
    • Semantic-tier nodes with only a contains edge are still flagged, and AST nodes whose one edge is a different relation (e.g. calls) are still flagged.
    • Community node listings / thin-community counts still count these declarations as real nodes; only the gap signal changed.

    Verified with 3 new regression tests in ests/test_report_gap_thresholds.py (11 passed).

  3. safishamsi commented on Oct 8, 2026

    @safishamsi
    Member

    Fixed by #4212 (thanks @nothariharan), shipped in v0.9.81. Closing as resolved.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions