/debug-types

Development L2+ Production medium usage

/debug-types

Overview

/debug-types is a production command in the Development category of the Prismatic Platform that troubleshoots Dialyzer type inference issues, detects conflicting typespecs, identifies vague type declarations, and optimizes type system usage across the codebase. The command automates what is traditionally one of the most time-consuming debugging tasks in Elixir development: understanding and resolving Dialyzer warnings that arise from conflicting or overly vague @spec declarations.

Dialyzer warnings are notoriously difficult to interpret. A single vague typespec (@spec run(term(), term(), term()) :: term()) can mask the precise type information from a more specific typespec on the same function, causing Dialyzer to infer the least useful type. The /debug-types command systematically detects these conflicts, identifies the authoritative typespec (the most specific one), removes the conflicting vague spec, and verifies that Dialyzer produces zero warnings after the fix.

This command operates under the L2+ authority level and is executed by the type-inference-debugger agent. It is part of the platform's 216-command slash command registry, built on the AIAD (Autonomous Intelligence Agent Design) standard. The command was developed from the learnings of the Operation Type Inference Fix, where systematic type analysis resolved multiple Dialyzer warnings that had persisted across several development sessions.

The type debugging approach follows a five-phase pipeline: capture Dialyzer output, analyze conflicts, recommend fixes, implement changes, and verify results. Each phase produces structured output that documents the analysis decisions, ensuring that type system changes are transparent and auditable.

Architecture

The type debugging system follows a diagnostic pipeline from Dialyzer capture through verified fix.

Target (file/module/all)
    |
    v
Dialyzer Runner
    |-- Capture all warnings
    |-- Parse warning structure
    |-- Classify warning types
    |
    v
Conflict Analyzer
    |-- Find multiple @spec for same function
    |-- Detect term() :: term() patterns
    |-- Identify override relationships
    |-- Trace delegation chains
    |
    v
Fix Recommender
    |-- Determine authoritative typespec
    |-- Identify vague specs to remove
    |-- Suggest specific union types
    |-- Plan fix strategy
    |
    v
Fix Implementer
    |-- Remove conflicting vague typespecs
    |-- Preserve specific union types
    |-- Update delegation chain types
    |-- Add @typedoc for complex types
    |
    v
Verifier
    |-- Re-run Dialyzer
    |-- Confirm zero warnings
    |-- Validate type inference correct
    |-- Generate fix report

Common Issue Categories

CategoryPatternSeverityFrequency
Conflicting TypespecsMultiple @spec for same functionHighCommon
Vague Public API Typesterm() :: term() in public functionsMediumCommon
Delegation Chain Type LossType info lost through internal callsMediumModerate
Missing @specPublic function without typespecLowModerate
Overly Restrictive TypesTypes too narrow for actual usageMediumRare

Usage

Basic Type Debugging

# Analyze specific module
/debug-types apps/prismatic_agents/lib/llm_client.ex

# Analyze entire application
/debug-types apps/prismatic_agents/

# Analyze all modules platform-wide
/debug-types

# Quick analysis of specific file
/debug-types apps/prismatic_web/lib/prismatic_web/live/dashboard_live.ex

Targeted Analysis

# Focus on public API types only
/debug-types apps/prismatic/lib/prismatic.ex --public-only

# Check delegation chains
/debug-types apps/prismatic_agents/ --trace-delegations

# Analyze with verbose output
/debug-types apps/prismatic_storage_core/ --verbose

Fix Operations

# Analyze and fix automatically
/debug-types apps/prismatic_agents/ --fix

# Fix with manual review
/debug-types apps/prismatic_agents/ --fix --interactive

# Dry run showing proposed fixes
/debug-types apps/prismatic_agents/ --fix --dry-run

Options & Parameters

ParameterTypeDefaultDescription
targetstringall modulesFile path, app directory, or module name
--fixbooleanfalseAutomatically apply recommended fixes
--interactivebooleanfalseInteractive fix review mode
--dry-runbooleanfalseShow proposed fixes without applying
--public-onlybooleanfalseAnalyze only public function types
--trace-delegationsbooleanfalseTrace type info through delegation chains
--verbosebooleanfalseShow detailed analysis steps

Execution Flow

PHASE 1: DIALYZER CAPTURE (5-10s)
    |-- Run Dialyzer on target
    |-- Capture all warnings
    |-- Parse warning structure
    |-- Classify by type (conflicting, vague, missing)
    |
PHASE 2: CONFLICT ANALYSIS (2-5s per module)
    |-- Scan for multiple @spec on same function
    |-- Detect term() :: term() patterns
    |-- Identify override relationships
    |-- Trace delegation chains for type loss
    |-- Map specificity hierarchy
    |
PHASE 3: FIX RECOMMENDATION
    |-- For conflicting specs:
    |     - Keep most specific typespec
    |     - Remove vague overriding spec
    |-- For vague public APIs:
    |     - Infer types from function body
    |     - Suggest specific union types
    |-- For delegation chains:
    |     - Propagate types through chain
    |     - Add missing intermediate specs
    |
PHASE 4: FIX IMPLEMENTATION (if --fix)
    |-- Remove conflicting vague typespecs
    |-- Add or update specific typespecs
    |-- Add @typedoc for complex types
    |-- Update delegation chain types
    |
PHASE 5: VERIFICATION
    |-- Re-run Dialyzer on modified files
    |-- Confirm zero new warnings
    |-- Verify no existing tests broken
    |-- Generate fix report

Common Issues and Fixes

Issue 1: Conflicting Typespecs

# DETECTED: Two specs for same function, one vague
@spec run(provider(), prompt(), options()) :: result()
@spec run(term(), term(), term()) :: term()  # Conflict!

# FIX: Remove vague typespec, keep specific one
@spec run(provider(), prompt(), options()) :: result()

Issue 2: Vague Public API Types

# DETECTED: Public function with uninformative types
@spec public_function(term()) :: term()

# FIX: Replace with specific types
@spec public_function(String.t()) :: {:ok, result()} | {:error, atom()}

Issue 3: Delegation Chain Type Loss

# DETECTED: Type info lost through private delegation
@spec run(provider(), prompt()) :: result()
def run(provider, prompt), do: do_run(provider, prompt)

# Private function with vague types
defp do_run(term(), term()) :: term()  # Type info lost!

# FIX: Preserve types through delegation
@spec run(provider(), prompt()) :: result()
def run(provider, prompt), do: do_run(provider, prompt)

@spec do_run(provider(), prompt()) :: result()
defp do_run(provider, prompt) do
  # ...
end

Issue 4: Missing @spec on Public Function

# DETECTED: No @spec on public function
def calculate_score(data, options) do
  # ...
end

# FIX: Add inferred @spec
@spec calculate_score(map(), keyword()) :: {:ok, float()} | {:error, atom()}
def calculate_score(data, options) do
  # ...
end

Output Format

TYPE INFERENCE DEBUG: LLMClient

DIALYZER WARNINGS FOUND:
  1. Function run/3 has conflicting typespecs
     Line 51: @spec run(provider(), prompt(), options()) :: result()
     Line 54: @spec run(term(), term(), term()) :: term()

ROOT CAUSE ANALYSIS:
  Vague typespec at line 54 overrides specific typespec at line 51.
  Dialyzer infers most restrictive type (term() :: term()).

RECOMMENDED FIX:
  Action: Remove vague typespec at line 54
  Keep: Specific typespec at line 51
  Reason: Preserve type information for callers

IMPLEMENTATION:
  Removing conflicting typespec...
  Fixed: apps/prismatic_agents/lib/llm_client.ex:54

VERIFICATION:
  Running Dialyzer...
  Zero warnings!
  Type inference now correct

FIX SUMMARY:
  Removed: 1 vague typespec
  Preserved: 1 specific typespec
  Warnings: 1 -> 0

Integration Points

ComponentRelationshipDetails
Prismatic AgentsExecuted by type-inference-debuggerPrimary debugging agent
AIAD RegistryCommand specification and discoveryStandard AIAD interface
Quality GatesPre/post execution quality validationDialyzer as quality gate
TelemetryCommand execution metricsType fix event tracking
DialyzerType analysis toolCore analysis engine
Mix Tasksmix dialyzerPLT building and analysis

Quality Requirements

All type fixes must satisfy:

RequirementDescriptionEnforcement
Zero Dialyzer warningsNo new warnings introducedBlocking
One @spec per functionNo duplicate specs for same functionBlocking
No term() :: term() in public APIsPublic functions must have specific typesWarning
Delegation chains type-safeTypes preserved through internal callsWarning
@typedoc for complex typesCustom types documentedRecommended

Best Practices

  1. Run After Adding New Functions: Execute /debug-types after adding public functions to ensure type coverage and catch conflicts early.

  2. Focus on Public APIs First: Public function types are the most impactful because they affect all callers. Use --public-only for initial analysis.

  3. Always Verify After Fixes: Dialyzer operates on the PLT (Persistent Lookup Table), which can cache stale information. Always re-run Dialyzer after fixes to confirm resolution.

  4. Document Type Decisions: Add @typedoc for complex custom types, especially union types that represent multiple valid states.

  5. Trace Delegation Chains: Use --trace-delegations to find type information loss through private function chains, a common source of vague Dialyzer inference.

  6. Combine with /debug-investigation: For issues where type problems are symptoms of deeper architectural issues, follow up with /debug-investigation for root cause analysis.

Error Handling

ErrorCauseResolution
PLT_NOT_FOUNDDialyzer PLT not builtRun mix dialyzer --plt first
PLT_STALEPLT needs rebuildingRun mix dialyzer --plt --force
MODULE_NOT_FOUNDTarget module does not existVerify module path
FIX_BROKE_TESTSType fix caused test failuresReview fix, may need broader changes
NUCLEAR_CACHE_NEEDEDStale compilation artifactsrm -rf _build/dev/lib/*/ebin && mix compile

Performance

MetricValueNotes
Per-Module Analysis< 2 secondsExcluding PLT build
Per-Typespec Fix< 1 secondIncluding file write
Dialyzer Verification~10 secondsFull Dialyzer run
Total (Typical Module)~15 secondsEnd-to-end
PLT Build (Cold)5-15 minutesFirst run only

Doctrine Compliance

All commands operate under the NO MERCY, NO DOUBTS doctrine:

  • NO MERCY: Zero Dialyzer warnings tolerated. Every type fix must result in zero warnings for the affected modules. Vague term() :: term() typespecs on public APIs are treated as quality violations.
  • NO DOUBTS: Full analysis of type inference chain before any fix. Conflicting specs are resolved based on specificity analysis, not arbitrary removal.
  • /code - Core coding implementation and feature development
  • /fix - Bug fix implementation with mandatory regression tests
  • /debug-investigation - Comprehensive debugging investigation
  • /quality-gates - Enforce quality gate checkpoints with zero-warning compilation validation
  • /cascade - Execute CASCADE pattern fix for systematic anti-pattern removal
  • /coordinate - Orchestrate complex multi-agent operations

Connect & Contribute

Created by Tomáš Korcak (korczis) | Open Source under GHL