# `mix ggen_igniter.replay`
[🔗](https://github.com/seanchatmangpt/ggen_igniter/blob/v26.9.8/lib/mix/tasks/ggen_igniter.replay.ex#L1)

Diagnostic task: `mix ggen_igniter.replay <receipt_file> [--verify-only] [--json] [--manifest-dir DIR]`.

Loads ONE real `GgenIgniter.Receipt` (see that module's moduledoc for the
on-disk format) and recomputes real, CURRENT hashes of the same inputs the
receipt recorded, to answer: "has anything this receipt depended on
drifted since it was written?"

`receipt_file` accepts either on-disk shape a receipt genuinely exists in:

  * A date-partitioned `.jsonl` partition (`<base_dir>/.ggen_igniter/receipts/<yyyy-mm-dd>.jsonl`,
    `GgenIgniter.Receipt.path/2`'s real format) -- the LAST line (most
    recent attempt in that partition) is replayed.
  * A single JSON object file -- one receipt's `GgenIgniter.Receipt.to_json_map/1`
    shape, decoded directly via `Jason.decode!/1` (no receipt-specific
    wrapper). This is the shape a caller gets by extracting one line out of
    a `.jsonl` partition into its own file.

Exits 2 (invalid invocation) if the file does not exist or does not parse
as either shape -- never guesses at a receipt's content.

## What real drift this can and cannot detect

`GgenIgniter.Receipt`'s on-disk schema (see that module's moduledoc)
records, per attempt: `recipe_key` (`"<template_path>=><out_template>"`),
`files` (the real output paths this attempt touched),
`pre_run_hash`/`post_run_hash` (real content digests over exactly those
`files`, via `GgenIgniter.Receipt.hash_files/1`), and a free-form
`metadata` map -- on an `:alive` receipt written by
`GgenIgniter.Reactors.ReconcileReactor`, `metadata["graph_hash"]` is a real
`"sha256:" <> hex` digest of the ontology file that produced this run (see
that module's `finalize_evidence/1`).

This task recomputes and compares exactly what the schema makes
recomputable, and reports each as its own category rather than a single
pass/fail:

  * **"output state changed"** -- `GgenIgniter.Receipt.hash_files/1` is
    re-run over the receipt's own `files` list, RIGHT NOW, and compared to
    the receipt's recorded `post_run_hash`. A mismatch means at least one
    output this recipe wrote has been edited (or deleted) since this
    receipt was recorded.
  * **"ontology changed"** -- only checked when the receipt's `recipe_key`
    resolves to a `GgenIgniter.Manifest` entry recording a `pack_dir`
    (`--manifest-dir`'s manifest, default `File.cwd!()`) AND the receipt's
    `metadata["graph_hash"]` is present. `<pack_dir>/ontology.ttl` (the
    `--pack` convention's fixed subpath -- see `priv/ggen/CLAUDE.md`) is
    re-read and re-hashed with the SAME `"sha256:" <> hex` algorithm the
    reactor used, and compared to `graph_hash`.

`GgenIgniter.Receipt`'s schema does NOT record a standalone baseline hash
of the template file, the gate query, the pack directory as a whole, the
query engine's version string, or arbitrary run config -- there is no
recorded value for this task to compare a recomputed hash AGAINST for
those categories, so this task never fabricates a comparison for them.
When the receipt's `recipe_key` names a template path, its CURRENT hash is
still reported (`--json`'s `"template_path"`/`"template_current_hash"`
fields, or the human line `template: <path> (no baseline recorded --
informational only)`) so a human has the value on hand, but it is never
counted as drift. If `GgenIgniter.Receipt`'s schema grows recorded
baselines for these categories, extend `detect_drift/2` to compare them for
real rather than widen this task's guesses.

## Exit codes

  * `0` -- receipt loaded and compared; no real drift detected.
  * `1` -- receipt loaded and compared; at least one real drift category found.
  * `2` -- invalid invocation (missing/unreadable/unparsable receipt file,
    or a bad flag).

`--verify-only` performs only this read-only comparison (the only mode
implemented so far; reserved for a future re-actuation mode, never implied
as already present). `--json` prints the machine-readable report
(`%{"drift" => [...], "categories" => %{...}, "receipt" => %{...}}`)
instead of the human-readable lines.

## Examples

    mix ggen_igniter.replay .ggen_igniter/receipts/2026-08-27.jsonl

    mix ggen_igniter.replay tmp/one_receipt.json --json --manifest-dir priv/ggen/audit-trail-pack

---

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