Skip to content

refactor: Consume /v1/analysis/dead-code API endpoint instead of local graph analysis #8

@jonathanpopham

Description

@jonathanpopham

Summary

The Supermodel public API now exposes a dedicated dead code analysis endpoint at POST /v1/analysis/dead-code (operationId: generateDeadCodeAnalysis). This action should be refactored from performing local graph analysis to being a thin consumer of that endpoint.

Currently, the action:

  1. Zips the repo
  2. Calls generateSupermodelGraph to get the full Supermodel IR (nodes + relationships)
  3. Locally filters Function nodes, checks for callers via calls relationships
  4. Applies heuristic entry-point/export/test filtering in dead-code.ts
  5. Formats results into a PR comment

After this refactor, the action should:

  1. Zip the repo (unchanged)
  2. Call generateDeadCodeAnalysis — the API handles all analysis server-side
  3. Map the API response directly to PR comment / action outputs
  4. No local graph processing needed

Why

  • The API endpoint is smarter — it combines parse graph + call graph, detects transitive dead code, does symbol-level import analysis, and provides confidence levels + reasons
  • Removes ~200 lines of local analysis logic that the API now handles better
  • The API detects entry points server-side (exports, route handlers, main functions) — no need for local heuristics
  • Response includes confidence (high/medium/low) and reason fields for richer reporting
  • Detects more than just functions: classes, methods, interfaces, types, variables, constants

Current Architecture (to be replaced)

src/index.ts

  • Entry point for the GitHub Action
  • Creates zip via git archive
  • Generates idempotency key from commit hash + UUID
  • Calls api.generateSupermodelGraph() via @supermodeltools/sdk
  • Passes raw graph nodes/relationships to findDeadCode()
  • Posts PR comment, sets action outputs
  • Comprehensive error handling with status-code-specific messages

src/dead-code.ts

  • findDeadCode(nodes, relationships, ignorePatterns) — the core local analysis:
    • Filters for Function label nodes
    • Builds set of called function IDs from calls relationships
    • Skips: ignored files, entry point files, entry point function names, exported functions
  • formatPrComment(deadCode) — markdown table formatter
  • Helper functions: isEntryPointFile, isEntryPointFunction, shouldIgnoreFile
  • Constants: DEFAULT_EXCLUDE_PATTERNS, ENTRY_POINT_PATTERNS, ENTRY_POINT_FUNCTION_NAMES

action.yml

  • Inputs: supermodel-api-key, github-token, comment-on-pr, fail-on-dead-code, ignore-patterns
  • Outputs: dead-code-count, dead-code-json
  • Runs on node20, main: dist/index.js

Tests

  • src/__tests__/dead-code.test.ts — unit tests for all local analysis functions
  • src/__tests__/integration.test.ts — end-to-end test calling actual API (skipped without key)

Dependencies

  • @supermodeltools/sdk@^0.4.1 — needs upgrade for new endpoint
  • @actions/core, @actions/exec, @actions/github — standard GH Action libs
  • minimatch@^9.0.0 — for glob pattern matching (may no longer be needed)

New API Endpoint Details

POST /v1/analysis/dead-code
Header: Idempotency-Key, X-Api-Key
Body: multipart/form-data with `file` (zip archive)

Response Schema (DeadCodeAnalysisResponse)

{
  metadata: {
    totalDeclarations: number;
    deadCodeCandidates: number;
    aliveCode: number;
    analysisMethod: string; // e.g. "parse_graph + call_graph"
    rootFilesCount?: number;
    transitiveDeadCount?: number;
    symbolLevelDeadCount?: number;
    analysisStartTime?: string;
    analysisEndTime?: string;
  };
  deadCodeCandidates: Array<{
    file: string;
    name: string;
    line: number;
    type: 'function' | 'class' | 'method' | 'interface' | 'type' | 'variable' | 'constant';
    confidence: 'high' | 'medium' | 'low';
    reason: string;
  }>;
  aliveCode: Array<{
    file: string;
    name: string;
    line: number;
    type: string;
    callerCount: number;
  }>;
  entryPoints: Array<{
    file: string;
    name: string;
    line: number;
    type: string;
    reason: string;
  }>;
}

Async envelope: Returns 202 with { status: "processing", jobId, retryAfter } while processing, then 200 with { status: "completed", jobId, result: DeadCodeAnalysisResponse } when done. The SDK handles polling automatically.

Implementation Plan

1. Update SDK dependency

2. Refactor src/index.ts

  • Replace api.generateSupermodelGraph()api.generateDeadCodeAnalysis()
  • Remove graph node/relationship processing
  • Map response.result.deadCodeCandidates directly to output format
  • Update idempotency key prefix from deadcode to analysis:deadcode
  • Expose richer metadata in action outputs (confidence, reason, type, metadata stats)

3. Simplify src/dead-code.ts

  • Remove findDeadCode() — no longer needed
  • Remove all entry point detection helpers — API handles this
  • Remove DEFAULT_EXCLUDE_PATTERNS, ENTRY_POINT_PATTERNS, ENTRY_POINT_FUNCTION_NAMES
  • Keep formatPrComment() but update to use new response fields:
    • Add Type and Confidence columns to the table
    • Include reason in expandable details
    • Add metadata summary (totalDeclarations, analysis method, etc.)
  • ignore-patterns input can remain for client-side post-filtering (filter by file path)
  • minimatch dependency can stay if we keep ignore-patterns, otherwise remove

4. Update action.yml

  • Consider adding new outputs: metadata-json, alive-code-count, entry-points-json
  • Existing outputs (dead-code-count, dead-code-json) should map cleanly

5. Rewrite tests

  • Unit tests: rewrite to test new response mapping and comment formatting
  • Integration test: update to call generateDeadCodeAnalysis instead of generateCallGraph

6. Update README

  • Update "What it does" section to reflect API-driven analysis
  • Note new capabilities (confidence levels, types beyond functions, transitive detection)

7. Version bump for GH Actions marketplace

  • Bump package.json version
  • Rebuild dist/index.js via npm run build
  • Create new release / update v1 tag

Related

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions