← Back to Foundation

JIDO CONTRACT

Documentation for JIDO_CONTRACT from the Foundation repository.

Jido Contract Analysis: Dialyzer Issues Investigation

Executive Summary

Status: INVESTIGATION IN PROGRESS
Priority: HIGH - Potential Jido framework issues identified

Issues Under Investigation

1. Agent Behavior Contract Mismatches

Error Pattern:

lib/jido_system/agents/coordinator_agent.ex:63:extra_range
The type specification has too many types for the function.
Function: JidoSystem.Agents.CoordinatorAgent.handle_signal/2
Extra type: {:error, _}
Success typing: {:ok, _}

Affected Functions Across All Our Agents:

  • handle_signal/2 - Claims to support {:error, _} but only returns {:ok, _}
  • transform_result/3 - Same issue
  • do_validate/3 - Contract mismatch with actual return types
  • pending?/1 - Spec mismatch
  • reset/1 - Spec mismatch

Investigation Plan

  1. Examine Jido Agent Behavior Definitions
  2. Check Our Agent Implementations
  3. Identify Root Cause
  4. Determine if Bug/Flaw/Mismatch

Analysis Results

Part 1: Jido Agent Behavior Investigation

CONCLUSION: NOT A BUG - This is EXPECTED and CORRECT behavior

Root Cause Analysis

  1. Jido Behavior Definitions (from /home/home/p/g/n/agentjido/jido/lib/jido/agent.ex):

    @callback handle_signal(signal :: Signal.t(), agent :: t()) :: 
      {:ok, Signal.t()} | {:error, any()}
    
    @callback transform_result(signal :: Signal.t(), result :: term(), agent :: t()) ::
      {:ok, term()} | {:error, any()}
    
  2. Jido Default Implementations (same file):

    def handle_signal(signal, _agent), do: OK.success(signal)
    def transform_result(signal, result, _agent), do: OK.success(result)
    
  3. The Issue:

    • Jido’s behavior contracts are designed to allow error returns for flexibility
    • Jido’s default implementations are conservative and never return errors
    • Our agents inherit these defaults and also never return errors
    • Dialyzer correctly identifies this as “extra_range” - the specs allow more than what’s actually returned

Why This Is CORRECT Design

Framework Design Pattern: This is a common and correct pattern in Elixir frameworks:

  1. Behavior contracts are permissive - they define the maximum possible return types
  2. Default implementations are conservative - they only use the success path
  3. Custom implementations can choose to use the full range (including errors)

Examples in Elixir ecosystem:

  • GenServer callbacks can return many different tuples, but simple implementations only use some
  • Phoenix controller actions can return various response types, but most only return successful renders

Verification of Analysis

Let me check the specific functions mentioned in Dialyzer errors:

Function: do_validate/3

@spec do_validate(t(), map(), keyword()) :: map_result()
# where map_result() is defined as: {:ok, map()} | {:error, Error.t()}

Default implementation (lines 730-740):

defp do_validate(%__MODULE__{} = agent, state, opts) do
  schema = schema()
  if Enum.empty?(schema) do
    OK.success(state)  # Always returns {:ok, state}
  else
    # Schema validation that could theoretically fail, but in practice 
    # our schemas are simple and validation succeeds
  end
end

Function: pending?/1

@spec pending?(t()) :: non_neg_integer()
def pending?(%__MODULE__{} = agent) do
  :queue.len(agent.pending_instructions)  # Always returns integer
end

Function: reset/1

@spec reset(t()) :: agent_result()
def reset(%__MODULE__{} = agent) do
  OK.success(%{agent | dirty_state?: false, result: nil})  # Always succeeds
end

Part 2: Sensor Behavior Investigation

CONCLUSION: SAME PATTERN - NOT A BUG

Jido Sensor Behavior Analysis

  1. Jido Sensor Behavior Definitions (from /home/home/p/g/n/agentjido/jido/lib/jido/sensor.ex):

    @callback mount(map()) :: {:ok, map()} | {:error, any()}
    @callback deliver_signal(map()) :: sensor_result()  
    # where sensor_result :: {:ok, Jido.Signal.t()} | {:error, any()}
    @callback shutdown(map()) :: {:ok, map()} | {:error, any()}
    @callback on_before_deliver(Jido.Signal.t(), map()) :: {:ok, Jido.Signal.t()} | {:error, any()}
    
  2. Jido Sensor Default Implementations (same file):

    def mount(opts), do: OK.success(opts)                    # Always {:ok, opts}
    def deliver_signal(state), do: OK.success(...)          # Always {:ok, signal}
    def shutdown(state), do: OK.success(state)              # Always {:ok, state}  
    def on_before_deliver(signal, _state), do: OK.success(signal)  # Always {:ok, signal}
    
  3. Same Pattern:

    • Permissive contracts allowing both success and error
    • Conservative implementations that only return success
    • Dialyzer correctly detects the unused error types as “extra_range”

Part 3: Contract Supertype Issues

Contract Supertype Errors:

Function: JidoSystem.Sensors.SystemHealthSensor.__sensor_metadata__/0
Type specification: @spec __sensor_metadata__() :: map()
Success typing: @spec __sensor_metadata__() :: %{:category => _, :description => _, :name => _, ...}

Analysis: These are metadata functions generated by macros that:

  1. Declare broad return types (map()) for flexibility
  2. Actually return specific structured maps with known keys
  3. Dialyzer detects the actual return type is more specific than declared

This is normal and acceptable - the broad map() type allows for:

  • Future extensions without breaking contracts
  • Different sensor types with varying metadata structures
  • Backward compatibility as metadata evolves

FINAL CONCLUSION

Summary: NO JIDO BUGS OR DESIGN FLAWS

What we found:

  1. Jido Agent behavior - Correctly designed with permissive contracts and conservative defaults
  2. Jido Sensor behavior - Same correct pattern as Agent behavior
  3. Generated metadata functions - Appropriately broad contracts for extensibility

Why Dialyzer complains:

  • Dialyzer’s job is to find discrepancies between specs and actual behavior
  • “Extra range” warnings indicate specs allow more than what’s used
  • “Contract supertype” warnings indicate specs are broader than needed

This is EXPECTED when using frameworks with:

  • Flexible behavior contracts (allowing errors that may never happen)
  • Conservative default implementations (that always succeed)
  • Generated code with broad type specifications

Recommendations

For Our Codebase:

  1. Accept these warnings - they indicate good framework design, not bugs
  2. Keep our current implementations - they correctly use Jido’s defaults
  3. Don’t modify Jido - the framework design is correct

If we want to eliminate warnings (optional):

  1. Add custom specs in our agents that match actual return types
  2. Use Dialyzer ignore patterns for auto-generated functions
  3. Accept the warnings as evidence of robust framework design

Jido Framework Status: ✅ SOLID - No bugs or design flaws identified