Read-only admission preview: mix ggen_igniter.plan --template path.eex [--pack NAME | --pack-dir DIR] --query name=path.rq (repeatable) [--engine oxigraph|sparql|qlever] [--store-id ID] [--json].
Runs the SAME observe -> load ontology -> resolve pack -> run queries -> render ->
admit sequence GgenIgniter.Reactors.ReconcileReactor.run/1 runs for a real mix ggen_igniter.sync (with use_reactor: true), via
GgenIgniter.Reactors.ReconcileReactor.plan/1 -- but STOPS before :actuate. Every
%GgenIgniter.PendingActuation{} the admitted plan would write is reported; nothing
is ever written to disk, and no compensation/rollback machinery is ever invoked,
because nothing this task runs can fail an already-applied actuation.
Read-only, no lock (FR-5)
This task performs NO filesystem mutation of the target project and therefore does
not acquire GgenIgniter.Lock -- doctor and a read-only plan inspection may
both run concurrently with an in-flight sync/lock holder, per the PRD's FR-5. Do
not add a Lock.acquire!/1 call here; mix ggen_igniter.sync/mix ggen_igniter.replay are the only verbs that mutate and therefore lock.
Output
Human-readable (default) or --json (a stable, script-parseable rendering of the
same real plan data -- no field present in one is silently dropped from the other):
- inputs + hashes -- resolved
--ontology/--packroot, every resolved--query name=path(or pack-discovered gate query) with its source path, each%PendingActuation{}'sprevious_hash/desired_hash. - query names/sources -- every named query actually run, and whether it came
from an explicit
--query, a pack'sgates/*.rq, or template frontmatter. - engine -- the resolved
--engine(defaultoxigraph) and, forqlever, the resolved--store-id. - bindings -- the semantic-source binding keys visible to each pending item
(
semantic_sourceon%PendingActuation{}-- ontology/query/template identity, never the full rendered content in the human-readable form;--jsonincludesdesired_hashbut never rawdesired_content, since a plan preview is not a content dump). - output paths -- each
%PendingActuation{}.target(ornilforoperation: :eval, per that struct's own documented convention). - existing-file decisions --
operation(:create/:replace/:delete/:eval) plusGgenIgniter.PendingActuation.plan_unchanged?/1per item, so a plan run can distinguish "would create", "would replace with different bytes", "would replace with identical bytes (no-op)", and "would delete" without running:actuateto find out. - skip conditions --
--unless-exists/--skip-ifguards that would cause:actuateto skip a target, surfaced here as a real, computed decision (not re-derived ad hoc by this task -- sourced from whateverReconcileReactor.plan/1itself reports for each item, since admission-time skip evaluation is that function's responsibility, not this CLI wrapper's). - unsupported features -- anything this plan run could not admit because the
resolved template/run uses a capability outside
GgenIgniter.Reconcile.run/1's bounded reactor scope (frontmatterinject: true,--for-eachfan-out, etc.) is reported as exit code 3 (:unsupported_capability), never silently ignored or silently downgraded to a partial plan. - intended mutations -- one line per
%PendingActuation{}in the human-readable form (operation target (unchanged?)), or the full list underplan.pending_actuationsin--json.
Dependency on ReconcileReactor.plan/1
This task calls GgenIgniter.Reactors.ReconcileReactor.plan/1, a read-only
admission-only entry point (observe -> load -> resolve -> run_queries -> render ->
admit, returning the admitted [%PendingActuation{}] without ever reaching
:actuate) that this Mix task was written against by intended signature but that
does not yet exist in this working tree as of this file's authorship -- see
docs/architecture/adr/ and the plan doc this task was scoped from
(prd-ard-wiggly-creek.md, "2. mix ggen_igniter.plan") for the extraction this
depends on. Until ReconcileReactor.plan/1 lands, mix ggen_igniter.plan will fail
to compile/run with an UndefinedFunctionError; this is expected and disclosed,
not a bug in this file. Do not add a compatibility shim here that re-derives a
parallel plan-only pipeline out of run_pipeline!/3-style logic -- the whole point
of this task is to share the SAME admission logic sync uses, not to duplicate it.
Exit codes
0-- plan computed successfully (admitted cleanly, whether or not any item would actually change anything -- an all-unchanged plan is still exit 0).2-- invalid invocation (missing required input resolution: no--templateand no--pack/--pack-dirwith a discoverable template; no queries resolvable;--engine qleverwithout--store-id; etc.) -- the sameArgumentErrorvocabularymix ggen_igniter.syncalready raises for these, caught here and turned into a clean exit instead of a raw stack trace.3-- unsupported capability for the read-only plan path specifically (the resolved template/run needs:actuate-adjacent behaviorplan/1cannot admit without executing it, or uses a feature outsideGgenIgniter.Reconcile.run/1's bounded reactor scope -- frontmatterinject: true,--for-eachfan-out).
Example
mix ggen_igniter.plan --pack ash-lifecycle-pack:resource \
--query resource=priv/ggen/ash-lifecycle-pack/gates/resource.rq
mix ggen_igniter.plan --template test/fixtures/extension.ex.eex \
--query spec=test/fixtures/spec.rq --json