# Jido Action Usage Rules

## Scope

Use `jido_action` for validated work and data-first composition:

- `Jido.Action` defines one named module, one validated parameter map, one `run/2` callback, and one result.
- `Jido.Instruction` represents one requested executable call as data.
- `Jido.Flow` composes named Action calls as a validated graph.
- `Jido.Exec` runs Actions, Instructions, and Flows through one public boundary.

## Action Definitions

- Use `use Jido.Action` for public actions.
- Provide stable `name` and useful `description` values.
- Use Zoi schemas for `schema` and `output_schema`; omit them or use `[]` only when validation is intentionally empty.
- Keep `run/2` strict: return `{:ok, result}`, `{:ok, result, extra}`,
  `{:continue, input, target}`, `{:error, reason}`, or
  `{:error, reason, extra}`.
- Return a normal map for success. Use `Jido.Action.Output` for an intentional
  raw, stream, batch, or opaque success value.
- Keep side effects explicit inside `run/2` and make them easy to test.

## Instructions

- Use `Jido.Instruction` when one requested executable call must be data before
  execution.
- Store only the executable target, params, context, and caller metadata in an
  Instruction.
- Use an Action module, Flow module, or runtime Flow value as the target.
- Use `target:` in new code. Deprecated `action:` construction data emits a
  runtime warning and normalizes to `target`; typed `flow:` data also
  normalizes to `target`.
- Do not put execution policy in `Instruction.opts`. Version 3 accepts that
  field only as a warning-based migration shim. Pass options to `Jido.Exec`.
- Use [Migration Shims](guides/migration-shims.md) to distinguish supported
  compatibility paths from removed package APIs.
- Validate the executable contract explicitly when a caller needs that guarantee.

## Validation

- Validate inputs with `validate_params/1`.
- Validate outputs with `validate_output/1`.
- Use `on_before_validate_params/1` only for deterministic raw input
  preparation that must happen before Zoi validation.
- Direct object and struct schemas use open validation at the Action root:
  Jido treats Zoi `:strip` as `:preserve`, so declared keys are validated and
  unknown root keys are preserved.
- Nested and wrapped schemas use their declared Zoi `unrecognized_keys` policy.
  Jido keeps Zoi `:error` and typed preservation policies unchanged.
- Prefer precise schemas with defaults for optional action inputs.
- Use `Jido.Flow.validate/1` for canonical Flow structure and graph rules.
- Use `Jido.Flow.validate_executable/1` to also check all Flow target contracts.
- Use `Jido.Flow.Codec.encode/2` and `Jido.Flow.Codec.decode/2` with a trusted
  `Jido.Flow.Registry` for stored JSON data.
- Use `Jido.Flow.Codec.diagnose/2` when an editor needs all independent stored
  document and graph errors.

## Flow Authoring

- Use the compile-time `Jido.Flow` DSL as the primary developer authoring
  surface.
- Give every component a stable string name.
- Use `step`, `choice`, `map`, `reduce`, `iterate`, and `dispatch` for graph
  structure.
- Use `input`, `context`, and `result` references to map data. Put computation
  in Actions.
- Treat DSL expressions as a restricted data grammar, not general Elixir. Do
  not use assignments, pattern matching, pipes, or application function calls.
- Let result references create data dependencies. Use `after:` only for
  control order without a data dependency.
- Do not add a `parallel` block. Independent nodes run concurrently when
  `max_concurrency` is greater than `1`.
- Add one required `output` declaration to every Flow.
- The DSL, Builder, and canonical data all use the name `output`.
- Use `repeat` or a bounded `while` condition in the Spark `iterate` form. The
  lowerer converts it to canonical `completion` and `max_iterations` data.
- Keep Iterate State local to that component.
- Use at most one `dispatch`. It must be the last component and the complete
  Flow output. Run it only through a run-to-completion Exec call.

## Runtime Flow Data

- Use `Jido.Flow.Builder` only when graph structure comes from runtime data.
- Use `Jido.Flow.Codec.encode/2` for portable Map or JSON storage.
- Use `Jido.Flow.Codec.encode/1` only when a generated temporary Registry is
  sufficient. Keep its returned Registry for decoding.
- Restore stored data with `Jido.Flow.Codec.decode/2` and the same trusted
  `Jido.Flow.Registry`.
- Use `Jido.Flow.Codec.diagnose/2` when a UI or AI agent submits an invalid
  stored map. Diagnostics return ordered, path-based errors and no partial
  Flow.
- Use proper lists in runtime Flow data and non-negative integers for list path
  indexes. Invalid values return structured validation errors.
- Do not parse or evaluate stored Elixir DSL source. AI systems can produce
  stored JSON or Map data instead.
- Only `Builder.step/5` and a Spark `step` can derive a Subflow from an
  executable of kind `:flow`. Choice, Map, Reduce, and Iterate target fields
  accept Actions only. Dispatch decision and expander targets also accept
  Actions only.

## Execution

- Use `Jido.Exec.run/4` for the public validation and error boundary.
- Use `jido: MyApp.Jido` with an Action, Instruction, or Flow when work must run
  under the Task Supervisor for that running Jido core instance.
- When a higher-level runtime builds a Jido instance supervision tree, use
  `Jido.Exec.task_supervisor_name/1` for its Task Supervisor name.
- All run-to-completion targets accept `max_continuations` and
  `max_concurrency`. An Action does not use the concurrency limit itself, but
  it can continue to a Flow. `max_concurrency` defaults to `8`. Use `1` for
  serial Flow execution.
- Use `run_async/4` for an asynchronous run-to-completion call. Only the owner
  process can await, handle messages for, or cancel its handle. Use
  `handle_message/2` in OTP callbacks. Await, message handling, and cancellation
  are alternative one-shot terminal consumers. An await timeout cancels that
  call.
- Use `start/4`, `ready/1`, `step/1`, `step/2`, `wave/1`, `continue/1`, and
  `result/1` for a Flow or an Instruction with a Flow target.
- Treat values from `ready/1`, `step/1`, `step/2`, and `wave/1` as native
  `Runic.Workflow.Runnable` values. Runic support work is visible.
- Use `Jido.Exec.workflow/1` and `Jido.Exec.compiled/1` for supported live
  Runic inspection. Other Execution fields are internal.
- Select `step/2` work with a ready Runnable or its integer ID.
- Treat each execution as caller-owned, in-memory state. `max_concurrency`
  bounds each concurrent ready wave. Always pass the latest value to the next
  step-wise call.
- Do not persist an execution as a checkpoint. Reusing a stale value can run an
  Action side effect again.
- Let the caller or Jido core select timeout and retry policy. Use `timeout:`
  with `Jido.Exec.run/4` or `run_async/4` to enforce one finite whole-call
  timeout. Exec async handles provide only owner-bound, in-memory
  cancellation. Keep retry, backoff, durable cancellation policy,
  persistence, and exactly-once behavior in the higher-level runtime.

## Package Boundary

Keep bundled domain Actions, adapter-specific conversions, and higher-level
runtime policy in separate packages.
