PtcRunner.Step (PtcRunner v0.4.1)

View Source

Result of executing a PTC program or SubAgent mission.

Returned by both PtcRunner.Lisp.run/2 and PtcRunner.SubAgent.run/2.

Fields

return

The computed result value on success.

  • Type: term() | nil

  • Set when: Mission/program completed successfully
  • Nil when: Execution failed (check fail field)

fail

Error information on failure. See fail/0 for the structure.

  • Type: t:fail/0 | nil

  • Set when: Execution failed
  • Nil when: Execution succeeded

memory

Final memory state after execution.

  • Type: map()
  • Always set: Contains accumulated memory from all operations
  • Access in PTC-Lisp: values available as plain symbols

signature

The contract used for validation.

  • Type: String.t() | nil

  • Set when: Signature was provided to run/2
  • Used for: Type propagation when chaining steps

usage

Execution metrics. See usage/0 for available fields.

  • Type: t:usage/0 | nil

  • Set when: Execution completed (success or failure after running)
  • Nil when: Early validation failure (before execution)

trace

Execution trace for debugging (SubAgent only). See trace_entry/0 for the structure.

  • Type: [t:trace_entry/0] | nil

  • Set when: SubAgent execution
  • Nil when: Lisp execution

trace_id

Unique identifier for this execution (for tracing correlation).

  • Type: String.t() | nil

  • Set when: SubAgent execution (32-character hex string)
  • Nil when: Lisp execution
  • Used for: Correlating traces in parallel and nested agent executions

parent_trace_id

ID of parent trace for nested agent calls.

  • Type: String.t() | nil

  • Set when: This agent was spawned by another agent
  • Nil when: Root-level execution (no parent)
  • Used for: Linking child executions to their parent

See PtcRunner.Tracer for trace generation and management.

field_descriptions

Descriptions for signature fields, propagated from SubAgent.

  • Type: map() | nil

  • Set when: SubAgent had field_descriptions option
  • Nil when: No field descriptions provided
  • Used for: Passing field documentation through chained executions

Error Reasons

Complete list of error reasons in step.fail.reason:

ReasonSourceDescription
:parse_errorLispInvalid PTC-Lisp syntax
:analysis_errorLispSemantic error (undefined variable, etc.)
:eval_errorLispRuntime error (division by zero, etc.)
:timeoutBothExecution exceeded time limit
:memory_exceededBothProcess exceeded heap limit
:validation_errorBothInput or output doesn't match signature
:tool_errorSubAgentTool raised an exception
:tool_not_foundSubAgentCalled non-existent tool
:reserved_tool_nameSubAgentAttempted to register return or fail
:max_turns_exceededSubAgentTurn limit reached without termination
:max_depth_exceededSubAgentNested agent depth limit exceeded
:turn_budget_exhaustedSubAgentTotal turn budget exhausted
:mission_timeoutSubAgentTotal mission duration exceeded
:llm_errorSubAgentLLM callback failed after retries
:llm_requiredSubAgentLLM option is required for agent execution
:no_code_foundSubAgentNo PTC-Lisp code found in LLM response
:llm_not_foundSubAgentLLM atom not in registry
:llm_registry_requiredSubAgentAtom LLM used without registry
:invalid_llmSubAgentRegistry value not a function
:chained_failureSubAgentChained onto a failed step
:template_errorSubAgentTemplate placeholder missing
Custom atomsSubAgentFrom (fail {:reason :custom ...})

Usage Patterns

Success Check

case SubAgent.run(prompt, opts) do
  {:ok, step} ->
    IO.puts("Result: #{inspect(step.return)}")
    IO.puts("Took #{step.usage.duration_ms}ms")

  {:error, step} ->
    IO.puts("Failed: #{step.fail.reason} - #{step.fail.message}")
end

Chaining Steps

Pass a successful step's return and signature to the next step:

{:ok, step1} = SubAgent.run("Find emails",
  signature: "() -> {count :int, _ids [:int]}",
  llm: llm
)

# Option 1: Explicit
{:ok, step2} = SubAgent.run("Process emails",
  context: step1.return,
  context_signature: step1.signature,
  llm: llm
)

# Option 2: Auto-extraction (SubAgent only)
{:ok, step2} = SubAgent.run("Process emails",
  context: step1,  # Extracts return and signature automatically
  llm: llm
)

Accessing Firewalled Data

Fields prefixed with _ are hidden from LLM history but available in return:

{:ok, step} = SubAgent.run("Find emails",
  signature: "() -> {count :int, _email_ids [:int]}",
  llm: llm
)

step.return.count      #=> 5 (visible to LLM)
step.return._email_ids #=> [101, 102, 103, 104, 105] (hidden from LLM)

Summary

Types

Error information on failure.

t()

Step result struct.

Tool call information in trace.

Single turn's execution history.

Execution metrics.

Functions

Creates a new failed Step.

Creates a failed Step with additional details.

Creates a new successful Step.

Types

fail()

@type fail() :: %{
  :reason => atom(),
  :message => String.t(),
  optional(:op) => String.t(),
  optional(:details) => map()
}

Error information on failure.

Fields:

  • reason: Machine-readable error code (atom)
  • message: Human-readable description
  • op: Optional operation/tool that failed
  • details: Optional additional context

t()

@type t() :: %PtcRunner.Step{
  fail: fail() | nil,
  field_descriptions: map() | nil,
  memory: map(),
  parent_trace_id: String.t() | nil,
  prints: [String.t()],
  return: term() | nil,
  signature: String.t() | nil,
  trace: [trace_entry()] | nil,
  trace_id: String.t() | nil,
  usage: usage() | nil
}

Step result struct.

One of return or fail will be set, but never both:

  • Success: return is set, fail is nil
  • Failure: fail is set, return is nil

The trace_id and parent_trace_id fields are used for tracing correlation in parallel and nested agent executions. See PtcRunner.Tracer for details.

tool_call()

@type tool_call() :: %{
  name: String.t(),
  args: map(),
  result: term(),
  error: String.t() | nil,
  timestamp: DateTime.t(),
  duration_ms: non_neg_integer()
}

Tool call information in trace.

Fields:

  • name: Tool name
  • args: Arguments passed to tool
  • result: Tool result
  • error: Error message if tool failed
  • timestamp: When tool was called
  • duration_ms: How long tool took

trace_entry()

@type trace_entry() :: %{
  turn: pos_integer(),
  program: String.t(),
  result: term(),
  tool_calls: [tool_call()],
  feedback_truncated: boolean()
}

Single turn's execution history.

Fields:

  • turn: Turn number
  • program: PTC-Lisp program executed
  • result: Result of executing the program
  • tool_calls: List of tool calls made during this turn
  • feedback_truncated: Whether feedback to LLM was truncated

usage()

@type usage() :: %{
  :duration_ms => non_neg_integer(),
  :memory_bytes => non_neg_integer(),
  optional(:turns) => pos_integer(),
  optional(:input_tokens) => non_neg_integer(),
  optional(:output_tokens) => non_neg_integer(),
  optional(:total_tokens) => non_neg_integer(),
  optional(:llm_requests) => non_neg_integer()
}

Execution metrics.

Fields:

  • duration_ms: Total execution time
  • memory_bytes: Peak memory usage
  • turns: Number of LLM turns used (SubAgent only, optional)
  • input_tokens: Total input tokens (SubAgent only, optional)
  • output_tokens: Total output tokens (SubAgent only, optional)
  • total_tokens: Input + output tokens (SubAgent only, optional)
  • llm_requests: Number of LLM API calls (SubAgent only, optional)

Functions

error(reason, message, memory)

@spec error(atom(), String.t(), map()) :: t()

Creates a new failed Step.

Examples

iex> step = PtcRunner.Step.error(:timeout, "Execution exceeded time limit", %{})
iex> step.fail.reason
:timeout
iex> step.return
nil

error(reason, message, memory, details)

@spec error(atom(), String.t(), map(), map()) :: t()

Creates a failed Step with additional details.

Examples

iex> PtcRunner.Step.error(:validation_failed, "Invalid input", %{}, %{field: "name"})
%PtcRunner.Step{
  return: nil,
  fail: %{reason: :validation_failed, message: "Invalid input", details: %{field: "name"}},
  memory: %{},
  signature: nil,
  usage: nil,
  trace: nil,
  trace_id: nil,
  parent_trace_id: nil,
  field_descriptions: nil
}

ok(return, memory)

@spec ok(term(), map()) :: t()

Creates a new successful Step.

Examples

iex> step = PtcRunner.Step.ok(%{count: 5}, %{})
iex> step.return
%{count: 5}
iex> step.fail
nil