# `Sigra.Install.Runner`
[🔗](https://github.com/sztheory/sigra/blob/v1.20.0/lib/sigra/install/runner.ex#L1)

Generic walker over a `[Sigra.Install.Feature]` list. Feature-agnostic:
adding `Features.Organizations`, `Features.Passkeys`, or `Features.Admin`
in a later phase requires ZERO edits to this module — only a new entry
in the caller's feature list.

See Phase 11 CONTEXT.md D-03 (full decomposition) and the "purely
additive" invariant V-PA-01.

## Byte-identity contract with the v1.0 monolith

The runner reproduces the exact stdout the pre-refactor monolith
emitted (see `test/fixtures/install_golden/STDOUT.txt`):

  * `Mix.Generator.create_file/2` for every rendered file (and for
    migrations, since migration entries are inlined in `files/1`
    with their target already resolved from the allocated timestamp).
  * `Mix.shell().info([:green, "* injecting ", :reset, path])` for
    successful injections, `[:yellow, "* already injected ", ...]`
    for idempotent no-ops.
  * `Mix.shell().info/1` once per logical chunk returned by
    `feature.post_instructions/2` — features return a list of chunks
    where each chunk represents ONE monolith `info` call, so the
    trailing newlines Mix.shell().info appends match the monolith.

The `%Sigra.Install.Report{}` is threaded through and populated for
every decision, but is NOT rendered to stdout on the default run
(that would diverge from the monolith's format). Host tooling (CI
smoke runners, the idempotency test) can inspect the returned report
directly.

# `run_result`

```elixir
@type run_result() :: {:ok, Sigra.Install.Report.t()}
```

# `run`

```elixir
@spec run([module()], keyword(), keyword()) :: run_result()
```

Walks `features`, runs each enabled feature's callbacks, and returns
the populated `%Report{}`.

Steps:

  1. Filter `features` through `enabled?/1`.
  2. Allocate migration timestamps for all active features via
     `MigrationTimestamps.allocate/2`, then overlay any pre-existing
     migration files on disk (re-run idempotency, GEN-04).
  3. For each active feature, render files, apply injections, then
     print the feature's post-install instruction chunks.

---

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