# `Dsxir.Artifact`

Encode and decode `Dsxir.Program` save/load artifacts.

The on-disk JSON shape mirrors DSPy structurally:

    {
      "<predictor_name>": {"instructions": String | null, "demos": [Map, ...]},
      ...,
      "_metadata": {"compiled_with": ..., "score": ..., "trainset_hash": ...}
    }

`save/2` writes one program. `load/3` reads one program into a target module
and validates the artifact's predictor + field shape against the target's
signatures, raising `Dsxir.Errors.Invalid.SignatureMismatch` on any drift.

# `load`

```elixir
@spec load(module(), Path.t(), keyword()) ::
  {:ok, Dsxir.Program.t()} | {:error, Exception.t()}
```

Read a saved artifact from `path` and hydrate it into a fresh program for
`target_module`. Returns `{:ok, program}` or `{:error, exception}` on read,
decode, or structural validation failures.

# `load!`

```elixir
@spec load!(module(), Path.t(), keyword()) :: Dsxir.Program.t()
```

Bang variant of `load/3`. Returns the program on success and raises the
underlying exception on failure.

# `save`

```elixir
@spec save(Dsxir.Program.t(), Path.t()) :: {:ok, Path.t()} | {:error, Exception.t()}
```

Write `prog` to `path` as pretty JSON. Returns `{:ok, path}` on success or
`{:error, exception}` when encoding or I/O fails. The target directory is
created if missing.

# `save!`

```elixir
@spec save!(Dsxir.Program.t(), Path.t()) :: Path.t()
```

Bang variant of `save/2`. Returns the path on success and raises the
underlying exception on failure.

---

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