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/2for every rendered file (and for migrations, since migration entries are inlined infiles/1with 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/1once per logical chunk returned byfeature.post_instructions/2— features return a list of chunks where each chunk represents ONE monolithinfocall, 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.
Summary
Functions
Walks features, runs each enabled feature's callbacks, and returns
the populated %Report{}.
Types
@type run_result() :: {:ok, Sigra.Install.Report.t()}
Functions
@spec run([module()], keyword(), keyword()) :: run_result()
Walks features, runs each enabled feature's callbacks, and returns
the populated %Report{}.
Steps:
- Filter
featuresthroughenabled?/1. - 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). - For each active feature, render files, apply injections, then print the feature's post-install instruction chunks.