# `Runic.Workflow.CausalContext`
[🔗](https://github.com/zblanco/runic/blob/main/lib/workflow/causal_context.ex#L1)

Minimal immutable context for executing a runnable without the full workflow.

Built during the prepare phase and consumed during execute phase.
Contains only what's needed for the specific node type being invoked.

## Design Goals

1. **Minimal footprint** - Only include data needed for execution
2. **Immutable** - Safe to pass across process boundaries
3. **Self-contained** - No workflow reference, all needed state captured
4. **Content-addressed** - Uses causal ancestry rather than generation counters

## Node-Specific Context Fields

Different node types populate different context fields:

- **Step**: `fan_out_context` for mapped pipeline tracking
- **Condition/Conjunction**: `satisfied_conditions` for gate logic
- **Accumulator**: `last_known_state` for stateful operations
- **Join**: `join_context` with satisfaction tracking
- **FanOut**: `fan_out_context` with reduce tracking
- **FanIn**: `fan_in_context` with readiness and sister values
- **All nodes**: `meta_context` for graph-resolved meta expression values, `run_context` for external runtime values from `context/1` expressions

# `t`

```elixir
@type t() :: %Runic.Workflow.CausalContext{
  ancestry_depth: non_neg_integer(),
  fan_in_context: map() | nil,
  fan_out_context: map() | nil,
  hooks: {list(), list()},
  input_fact: Runic.Workflow.Fact.t() | nil,
  is_state_initialized: boolean(),
  join_context: map() | nil,
  last_known_state: term() | nil,
  mergeable: boolean(),
  meta_context: map(),
  node_hash: integer() | nil,
  run_context: map(),
  satisfied_conditions: MapSet.t() | nil
}
```

# `after_hooks`

```elixir
@spec after_hooks(t()) :: list()
```

Returns the after hooks from the context.

# `basic`

```elixir
@spec basic(integer(), Runic.Workflow.Fact.t(), non_neg_integer()) :: t()
```

Builds a basic context with node hash, input fact, and ancestry depth.

# `before_hooks`

```elixir
@spec before_hooks(t()) :: list()
```

Returns the before hooks from the context.

# `has_meta_context?`

```elixir
@spec has_meta_context?(t()) :: boolean()
```

Returns whether this context has any meta context populated.

# `has_run_context?`

```elixir
@spec has_run_context?(t()) :: boolean()
```

Returns whether this context has any run context populated.

# `mergeable?`

```elixir
@spec mergeable?(t()) :: boolean()
```

Returns whether this context's node is mergeable (parallel-safe).

# `meta_context`

```elixir
@spec meta_context(t()) :: map()
```

Returns the meta context map from the context.

# `new`

```elixir
@spec new(keyword()) :: t()
```

Creates a new CausalContext with the given attributes.

# `run_context`

```elixir
@spec run_context(t()) :: map()
```

Returns the run context map from the context.

# `with_fan_in_context`

```elixir
@spec with_fan_in_context(t(), map()) :: t()
```

Adds fan_in context for reduction coordination.

# `with_fan_out_context`

```elixir
@spec with_fan_out_context(t(), map()) :: t()
```

Adds fan_out context for mapped pipeline tracking.

# `with_hooks`

```elixir
@spec with_hooks(
  t(),
  {list(), list()}
) :: t()
```

Adds hooks to the context.

# `with_join_context`

```elixir
@spec with_join_context(t(), map()) :: t()
```

Adds join context for join coordination.

# `with_mergeable`

```elixir
@spec with_mergeable(t(), boolean()) :: t()
```

Sets the mergeable flag on the context.

Components with `mergeable: true` have CRDT-like properties
(commutative, idempotent, associative) and are safe for parallel
merge without ordering guarantees.

# `with_meta_context`

```elixir
@spec with_meta_context(t(), map()) :: t()
```

Adds meta context for nodes with meta expression dependencies.

Meta context contains values prepared from `:meta_ref` edges during the
prepare phase. These values are then available during execution without
requiring workflow access.

## Example

    context = CausalContext.new(...)
    |> CausalContext.with_meta_context(%{cart_state: %{total: 150, items: []}})

# `with_run_context`

```elixir
@spec with_run_context(t(), map()) :: t()
```

Adds run context for external runtime value injection.

Run context contains runtime-scoped values (secrets, tenant IDs, database
connections) resolved for a specific component during the prepare phase.
Available during execution without requiring workflow access.

## Example

    context = CausalContext.new()
    |> CausalContext.with_run_context(%{api_key: "sk-...", model: "gpt-4"})

# `with_satisfied_conditions`

```elixir
@spec with_satisfied_conditions(t(), MapSet.t()) :: t()
```

Adds satisfied conditions for conjunction gates.

# `with_state`

```elixir
@spec with_state(t(), term(), boolean()) :: t()
```

Adds state context for stateful nodes.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
