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

Diagnostic task: `mix ggen_igniter.doctor [--pack NAME | --pack-dir DIR] [--engine sparql|qlever] [--store-id ID] [--fix] [--strict]`.

Runs a fixed checklist of real checks (no fabricated pass output):

1. Elixir/OTP version satisfies this project's `mix.exs` requirement (`~> 1.17`, OTP >= 25).
2. Required deps (`rdf`, `sparql`, `igniter`, and `gno` when `--engine qlever`) are loaded
   with resolvable `:vsn`.
3. Advisory for the known `sparql` 0.3.12 `FILTER NOT EXISTS` + `BIND` inside `UNION` bug.
4. The CURRENT project's own `mix.exs` doesn't restrict its direct `igniter` dependency
   with `:only` (real conflict: `ggen_igniter` needs `igniter` unconditionally, so a
   consumer's own `only: [:dev, :test]`-restricted declaration makes Mix's resolver
   refuse). `--fix` relaxes it for real; without `--fix` this is diagnostic only.
5. Same as 4, for `sourceror` (only applicable if the current project declares it
   directly at all).
6. `config :dcatr, env: ...` is present in the current project's `config/config.exs`
   whenever `:gno`/`:dcatr` are in its dependency tree (`:gno`'s `Fuseki` adapter raises
   at compile time without it). `--fix` adds the missing entry for real.
7. Every Ash domain module (`use Ash.Domain`) found under the current project's `lib/`
   is registered in `config :OTP_APP, ash_domains: [...]` (an unregistered domain is a
   hard compile error under `mix compile --warnings-as-errors`). `--fix` registers any
   missing one(s) for real.
8. (only with `--engine qlever` or a pack ontology naming a `gnoa:Qlever` store via
   `--store-id`) the QLever endpoint is really reachable via a real `ASK` query.
9. Pack `ontology.ttl` exists and parses as valid Turtle.
10. At least one gate query (`gates/*.rq`) is present.
11. At least one template (`templates/*.{eex,tmpl}`) is present.
12. Every gate query is syntactically valid SPARQL (parse-only, no execution).
13. Target (cwd) git status -- clean vs dirty is reported, never fails the run by itself.
14. `native/ggen_graph_nif` is compiled and up to date: the built `priv/native/ggen_graph_nif.so`
    exists and is newer than every `.rs` source file under the crate (a fast mtime proxy); if
    the `.so` is missing or stale, falls back to a real `cargo build --quiet` and reports real
    stderr on failure.
15. `GgenIgniter.Query.Oxigraph` actually works: runs a real `SELECT * WHERE { ?s ?p ?o }`
    SPARQL query against a tiny real in-memory `%RDF.Graph{}` through the native oxigraph
    engine and confirms it returns without raising -- a functional smoke test, not just
    "does the NIF load".
16. (only with `--hex-check`, off by default) hex-publish readiness: shells out to a real
    `mix hex.build` and reports its real output, plus checks `mix.exs`'s `package[:description]`
    and `package[:licenses]` are both present and non-empty.
17. `check_version_policy`: `mix.exs`'s `version:` literal is a *projection* of this
    project's real, observed versioning convention rather than an independently
    maintained field. This repo has no `git tag`s at all (confirmed via
    `git tag --list` returning empty), so `CHANGELOG.md`'s topmost `## vX` entry
    heading is the only real, standing record of "what the current version is" -- a
    calendar-ish `YY.M.D` string (e.g. `26.8.27` for 2026-08-27), matched verbatim
    against `mix.exs`'s `version:`. Reports `MATCH` or a clearly-named `MISMATCH`;
    never silently rewrites `mix.exs`. `--fix` corrects it for real only when the
    derivation is unambiguous (a single topmost `## vX` heading found and `mix.exs`'s
    `version:` is a simple string literal); an ambiguous shape (no CHANGELOG.md, no
    `## v` heading, or a non-literal `version:`) is reported `✘` as informational-only,
    never guessed at.
18. `GgenIgniter.Lock`'s real cross-process `.ggen_igniter/.sync.lock` file (see
    `GgenIgniter.Lock`'s moduledoc) is checked against the CURRENT project
    (`File.cwd!()`): absent is `✔` (no lock held); present and younger than
    `GgenIgniter.Lock`'s own 5-minute stale threshold is `✔` info naming the real
    holder marker (`pid=... node=... at=...`) written by `GgenIgniter.Lock.acquire/2`
    -- a live/recent lock held by a real, still-running `mix ggen_igniter.sync`/
    `.replay` invocation is not a problem; present and older than 5 minutes is a real
    `⚠` warning naming the exact real remedy (the next `acquire/2` call anywhere
    against this project automatically deletes it -- `GgenIgniter.Lock` has no
    `--force-unlock` flag, so this never invents one). Never `:error`: a held lock,
    stale or not, is advisory information about another invocation, not a defect in
    the current project.

Checks 9-12 only run when `--pack`/`--pack-dir` is given; without it, only checks 1-3
and 4-7 (and 8, if `--engine qlever` was explicitly passed with a graph-free reachability
check is not possible, so 8 is skipped) run. Checks 4-7, 13-15, 17, and 18 always run.
Check 16 only runs with `--hex-check` (it shells out to `mix hex.build`, which is slow,
so it stays off by default to keep `mix ggen_igniter.doctor` fast).

## `--fix`

Checks 4-7 and 17 are real fixes (`GgenIgniter.DoctorFixes`), not just diagnostics:
passing `--fix` applies each detected, safely-recognized fix directly to the CURRENT
project (`File.cwd!()`) -- the real consumer app `doctor` is running inside, never a
test-harness scaffold -- and the check line reports exactly what changed (prefixed
`FIXED:`), or that there was nothing to fix. Without `--fix`, these checks are
read-only: a real, fixable problem is reported as a `⚠` warning naming the exact fix to
run; a real problem whose exact shape isn't safely automatable is reported as a `✘`
error rather than silently skipped or guessed at.

## `--strict`

Without `--strict`, only `:error`-level checks fail the run (exit 1); `:warn`-level
checks (e.g. "git dirty", "not a git repo", a `--fix`-able hygiene gap reported
without `--fix`) are advisory only and never affect the exit code. With `--strict`,
any check currently reporting `:warn` also fails the run (exit 1) -- each such line
is suffixed `[STRICT]` in human output (and carries `"strict_failure": true` in
`--json` output) so it's clear which failures are strict-mode-only and would pass
under the default mode.

## Examples

    mix ggen_igniter.doctor

    mix ggen_igniter.doctor --pack audit-trail-pack --json

## Exit codes

- `0` -- all checks passed (under `--strict`, this also means no `:warn` findings).
- `1` -- ran the full checklist and at least one check came back `:error` (or, under
  `--strict`, at least one came back `:warn`).
- `2` -- invalid invocation/configuration: an unrecognized flag, `--engine` not one
  of `oxigraph`/`sparql`/`qlever`, or both `--pack` and `--pack-dir` given at once.
  The checklist never runs.
- `3` -- an explicitly requested capability isn't available on this toolchain (today:
  `--hex-check` without the `hex` Mix archive installed). The checklist never runs.
- `4` -- blocked by authority/lock/environment (today: `--fix` unable to get
  exclusive access to the current project directory). The checklist never runs.

## DX flags

`--help`/`-h` and `--version`/`-v` print and exit `0` immediately, before any checks
run. `--json` emits a single JSON object (`checks`, `ok`, `exit_code`) instead of the
human checklist -- exit code semantics above are unchanged either way. `--quiet`/`-q`
suppresses passing (`✔`) lines; warnings/errors and the summary line still print.
`--verbose` and `--no-color` are accepted for consistency with other `mix
ggen_igniter.*` tasks; this task's output is always plain `✔`/`⚠`/`✘` glyphs with no
ANSI color and no currently-defined extra verbose detail, so both are no-ops today.

---

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