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):
- Elixir/OTP version satisfies this project's
mix.exsrequirement (~> 1.17, OTP >= 25). - Required deps (
rdf,sparql,igniter, andgnowhen--engine qlever) are loaded with resolvable:vsn. - Advisory for the known
sparql0.3.12FILTER NOT EXISTS+BINDinsideUNIONbug. - The CURRENT project's own
mix.exsdoesn't restrict its directigniterdependency with:only(real conflict:ggen_igniterneedsigniterunconditionally, so a consumer's ownonly: [:dev, :test]-restricted declaration makes Mix's resolver refuse).--fixrelaxes it for real; without--fixthis is diagnostic only. - Same as 4, for
sourceror(only applicable if the current project declares it directly at all). config :dcatr, env: ...is present in the current project'sconfig/config.exswhenever:gno/:dcatrare in its dependency tree (:gno'sFusekiadapter raises at compile time without it).--fixadds the missing entry for real.- Every Ash domain module (
use Ash.Domain) found under the current project'slib/is registered inconfig :OTP_APP, ash_domains: [...](an unregistered domain is a hard compile error undermix compile --warnings-as-errors).--fixregisters any missing one(s) for real. - (only with
--engine qleveror a pack ontology naming agnoa:Qleverstore via--store-id) the QLever endpoint is really reachable via a realASKquery. - Pack
ontology.ttlexists and parses as valid Turtle. - At least one gate query (
gates/*.rq) is present. - At least one template (
templates/*.{eex,tmpl}) is present. - Every gate query is syntactically valid SPARQL (parse-only, no execution).
- Target (cwd) git status -- clean vs dirty is reported, never fails the run by itself.
native/ggen_graph_nifis compiled and up to date: the builtpriv/native/ggen_graph_nif.soexists and is newer than every.rssource file under the crate (a fast mtime proxy); if the.sois missing or stale, falls back to a realcargo build --quietand reports real stderr on failure.GgenIgniter.Query.Oxigraphactually works: runs a realSELECT * 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".- (only with
--hex-check, off by default) hex-publish readiness: shells out to a realmix hex.buildand reports its real output, plus checksmix.exs'spackage[:description]andpackage[:licenses]are both present and non-empty. check_version_policy:mix.exs'sversion:literal is a projection of this project's real, observed versioning convention rather than an independently maintained field. This repo has nogit tags at all (confirmed viagit tag --listreturning empty), soCHANGELOG.md's topmost## vXentry heading is the only real, standing record of "what the current version is" -- a calendar-ishYY.M.Dstring (e.g.26.8.27for 2026-08-27), matched verbatim againstmix.exs'sversion:. ReportsMATCHor a clearly-namedMISMATCH; never silently rewritesmix.exs.--fixcorrects it for real only when the derivation is unambiguous (a single topmost## vXheading found andmix.exs'sversion:is a simple string literal); an ambiguous shape (no CHANGELOG.md, no## vheading, or a non-literalversion:) is reported✘as informational-only, never guessed at.GgenIgniter.Lock's real cross-process.ggen_igniter/.sync.lockfile (seeGgenIgniter.Lock's moduledoc) is checked against the CURRENT project (File.cwd!()): absent is✔(no lock held); present and younger thanGgenIgniter.Lock's own 5-minute stale threshold is✔info naming the real holder marker (pid=... node=... at=...) written byGgenIgniter.Lock.acquire/2-- a live/recent lock held by a real, still-runningmix ggen_igniter.sync/.replayinvocation is not a problem; present and older than 5 minutes is a real⚠warning naming the exact real remedy (the nextacquire/2call anywhere against this project automatically deletes it --GgenIgniter.Lockhas no--force-unlockflag, 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 --jsonExit codes
0-- all checks passed (under--strict, this also means no:warnfindings).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,--enginenot one ofoxigraph/sparql/qlever, or both--packand--pack-dirgiven at once. The checklist never runs.3-- an explicitly requested capability isn't available on this toolchain (today:--hex-checkwithout thehexMix archive installed). The checklist never runs.4-- blocked by authority/lock/environment (today:--fixunable 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.