Skip to content

Impact analysis: transitiveDependents always 0 while directDependents is populated #138

@cadespivey

Description

@cadespivey

Summary

In supermodel audit and the underlying impact endpoint, every file's blastRadius.transitiveDependents and affectedFiles come back as 0, even when directDependents has a meaningful non-zero count. This makes blast-radius output misleading — large central files appear to have no downstream reach.

Observed on Swift but likely not language-specific — it looks like the reverse-reachability step isn't propagating past the first hop.

Environment

  • supermodel 0.6.10, macOS arm64
  • Language observed: Swift (monorepo with multiple SwiftPM targets)
  • Likely affects any language where direct edges exist but transitive closure is computed

Reproduction

Run supermodel audit on a codebase with clear file-level dependency chains. The audit report's Impact Analysis table shows:

| File                                         | Direct | Transitive | Affected Files |
| Sources/.../TemplateServiceImpl.swift        |    97  |     0      |      0         |
| Sources/.../FinancialIntegrationStoreImpl... |    75  |     0      |      0         |
| Sources/.../OverviewTabView.swift            |    66  |     0      |      0         |
| Tests/.../EthicalWallMutationGuardTests.swift|    66  |     0      |      0         |
| ... (every row has Transitive=0)

The raw impact JSON (from ~/.supermodel/cache/<impact-hash>.json) confirms:

{
  "metadata": {
    "totalFiles": 382,
    "totalFunctions": 5153,
    "analysisMethod": "reverse_reachability_call_graph"
  },
  "impacts": [
    {
      "target": { "file": ".../AuthPolicy.swift", "type": "file" },
      "blastRadius": {
        "directDependents": 0,
        "transitiveDependents": 0,
        "affectedFiles": 0,
        "riskScore": "low"
      },
      "affectedFunctions": [],
      "affectedFiles": []
    },
    ...
  ]
}

Every impacts[].blastRadius.transitiveDependents is 0 across all 382 files analyzed, and every affectedFunctions / affectedFiles list is empty.

Expected

For a target file with, say, Direct=97, transitiveDependents should include the downstream reach (callers of callers), and affectedFiles should be a non-empty list unless the file genuinely has no consumers beyond immediate imports.

Hypothesis

  • The first hop of the reverse-reachability walk is producing directDependents correctly (those numbers appear believable relative to the codebase).
  • The closure step past hop 1 either:
    • Terminates immediately (seed set not being expanded), or
    • Produces a set that is later filtered down to empty before serialization.
  • Not language-specific; would likely repro on any language where the graph has call/import edges.

Happy to share the cached impact JSON (with identifiers redacted) and the underlying graph JSON if useful.

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