Skip to content

gaia author and gaia pkg scaffold

Status: Reference for the optional Tier-2 authoring CLI (v0.5). The recommended authoring path is direct SDK authoring — run gaia sdk, read the cheat sheet, write the DSL directly. See Authoring workflow.

The gaia author subcommand group and the gaia pkg scaffold verb are an optional convenience over direct SDK authoring: instead of editing the Python source, you can scaffold a fresh -gaia package and append DSL statements through gaia author <verb>. Every CLI write is confined to the package's composed authored/ submodule (src/<pkg>/authored/) — the cli never writes the package-root __init__.py, which imports the CLI-authored runtime bindings while keeping root __all__ as the curated public surface. The cli owns identifier collision checks, reference resolution, pre-write defensive validation, file appending, and (by default) a post-write gaia build check to confirm the package still compiles.

Output is JSON-by-default through a uniform envelope (see Envelope shape) so an agent consumer can json.loads(stdout) once and dispatch on verb to interpret payload. --human opts into a short human-readable rendering of the same payload — the JSON form is the contract; the text form is a courtesy.

This reference is scannable, not tutorial. For a worked walkthrough using all 5 of the DSL verbs the canonical Galileo example exercises, see Galileo as a worked example at the end.

Capabilities at a glance (see also bayes.md):

  • --file <relative> on every author verb — route the statement to a sibling Python module instead of __init__.py. Pair with gaia pkg add-module --name <name> to scaffold the sibling.
  • --export on statement-emitting author verbs — add the emitted binding to the target module's literal __all__. Relation verbs export the returned helper Claim only when this flag is explicit.
  • --background <csv> on equal / contradict / exclusive / observe — passes through to the engine's background=[...] kwarg.
  • derive --conclusion-prose / observe --observation-prose / infer --hypothesis-prose — inline-prose mode that emits the prose directly at the call site (no auto-mint Claim binding).
  • claim --formula <expr> — canonical name for the predicate-mode formula expression; --predicate stays as a backwards-compatible alias.
  • gaia author variable — declare a Variable(...) or Constant(...) typed term.
  • gaia bayes <verb> group — predictive-model authoring surface. Covered in bayes.md.

Verb inventory — 22 author verbs + 1 pkg verb

The gaia author group exposes 22 verbs partitioned by DSL layer. 20 are statement-emitting (the cli appends a Python statement to src/<import_name>/__init__.py); 2 are file-based validate-and- register (the cli reads a file containing a decorated function and records its metadata in pyproject.toml).

Layer Verb DSL signature Statement-emitting?
Knowledge note note(content, *, title=None, **metadata) yes
Knowledge claim claim(content, proposition=None, *, title=None, prior=None, background=None, formula=None, ...) yes
Knowledge question question(content, *, title=None, targets=None, **metadata) yes
Knowledge artifact artifact(kind=..., source=None, locator=None, path=None, caption=None, description=None, ...) yes
Knowledge figure figure(source=None, locator=None, path=None, caption=None, description=None, ...) yes
Structural equal equal(a, b, *, rationale="", label=None) yes
Structural contradict contradict(a, b, *, rationale="", label=None) yes
Structural exclusive exclusive(a, b, *, rationale="", label=None) yes
Structural decompose decompose(whole, parts, *, formula=None, rationale="", label=None) yes
Support derive derive(conclusion, *, given=(), background=None, rationale="", label=None) yes
Support observe observe(conclusion, *, value=…, error=…, given=…, rationale="", label=None) yes
Support compute compute(result, *, fn, given=…, rationale="", label=None) yes
Probabilistic infer infer(evidence, *, hypothesis, p_e_given_h, p_e_given_not_h=…, given=…, label=None) yes
Probabilistic associate associate(a, b, p_a_given_b, p_b_given_a, *, pattern=…, rationale="", label=None) yes
Sugar parameter parameter(variable, value, *, content=…, prior=…, label=None, **metadata) yes
Prior register-prior register_prior(claim, *, value, justification, source_id=…) yes
Scaffold depends-on depends_on(conclusion, given, *, rationale="", background=None, label=None) yes
Scaffold candidate-relation candidate_relation(*, claims, pattern, rationale="", background=None, label=None) yes
Scaffold materialize materialize(scaffold, *, by, rationale="", label=None) yes
Composition compose @compose(name=…, version=…) decorating def fn(...) -> Claim no — file-based
Composition composition alias of compose no — file-based
Typed terms variable Variable(symbol=…, domain=…, value=…) or Constant(value, primitive) yes

The 21st verb in this reference, gaia pkg scaffold, lives in the pkg group alongside add, add-import, add-module, and register. It bootstraps a fresh -gaia package directory layout. See gaia pkg scaffold below.

Shared flag conventions

Every statement-emitting gaia author verb honors the same set of cross- cutting flags. Per-verb flags layer on top of these.

Flag Type Default Purpose
--target <path> string . Path to the target Gaia package root (the directory containing pyproject.toml).
--file <relative> string __init__.py Relative path under src/<import_name>/ to append the statement to. Default routes to the package entrypoint. Sibling files (e.g. priors.py) must exist first; use gaia pkg add-module --name <name> to scaffold them.
--label <ident> string required (most verbs) Python identifier the produced binding takes. Must not collide with module or DSL names.
--rationale <text> string none Natural-language justification carried through to the DSL kwarg.
--metadata <json> JSON object none Optional metadata dict; rendered as the DSL metadata= kwarg.
--references <csv> csv idents none Comma-separated background reference identifiers (only the verbs that accept background context).
--export / --no-export bool --no-export Add the emitted binding to the target module's literal __all__. For equal, contradict, exclusive, and associate, this exports the returned relation helper Claim.
--check / --no-check bool --check on Run post-write gaia build check after a successful write. Short-circuited when pre-write fails.
--human bool flag False Render the envelope as human-readable text instead of JSON.
--interactive bool flag False Surface pre-write warnings as a numbered prompt (human mode only — JSON mode auto-suppresses).
--json / --no-json bool --json on Courtesy alias; redundant with the default. --human is the actual switch.

--label is required for every statement-emitting verb except register-prior (which writes a bare register_prior(...) expression with no LHS binding) and note / claim / question (where the positional content arg is also required). Relation verbs (equal / contradict / exclusive / associate) return helper Claims; --export makes that returned helper part of the curated public interface. decompose returns its whole Claim rather than a separate public helper.

Per-verb flag surface (statement-emitting verbs)

The flags below are additional to the shared set. Each verb's DSL signature in the inventory table above is the source of truth for what gets rendered into the package.

note

gaia author note <content> --label <ident> [--target <path>]
    [--title <text>] [--metadata <json>]
    [--check/--no-check] [--human] [--interactive]
Flag Required Description
<content> yes Positional natural-language background.
--title <text> no Optional short title (title= kwarg).

artifact

gaia author artifact --dsl-binding-name <ident> --kind <kind> [--target <path>]
    [--source <citation-key>] [--locator <text>] [--path <package-relative>]
    [--caption <text>] [--description <text>] [--media-type <mime>]

Creates a normal note(...) through the artifact(...) helper. The resulting note carries metadata["gaia"]["artifact"] and can be referenced with [@<dsl-binding-name>].

Flag Required Description
--dsl-binding-name <ident> yes Python module-scope binding for the artifact note.
--kind <kind> yes One of figure, table, dataset, notebook, attachment.
--source <citation-key> no Citation key from references.json; validated during compile/check.
--locator <text> no Source-local locator such as Fig. 3 or Supplementary Data 1.
--path <package-relative> no Package-relative artifact path. Absolute paths and .. escapes are rejected.

figure

gaia author figure --dsl-binding-name <ident> [--target <path>]
    [--source <citation-key>] [--locator <text>] [--path <package-relative>]
    [--caption <text>] [--description <text>] [--media-type <mime>]

Sugar for gaia author artifact --kind figure. A source-bound figure requires --locator so that the source-local figure number is unambiguous.

claim

gaia author claim <content> --label <ident> [--target <path>]
    [--title <text>] [--prior <float>] [--predicate "<expr>"]
    [--references <csv>] [--metadata <json>]
    [--check/--no-check] [--human] [--interactive]
Flag Required Description
<content> yes Positional claim content.
--title <text> no Optional short title.
--prior <float> no Optional inline prior in (0, 1); routed via register_prior with source claim_inline.
--predicate "<expr>" no Predicate-claim mode — sandbox-validated formula expression rendered as the formula= kwarg. See Restricted-globals sandbox.
--references <csv> no Comma-separated background claims (rendered as background= kwarg).

question

gaia author question <content> --label <ident> [--target <path>]
    [--title <text>] [--targets <csv>] [--metadata <json>] ...
Flag Required Description
<content> yes Positional question content.
--targets <csv> no Comma-separated target identifiers (targets= kwarg).

equal / contradict / exclusive

gaia author <equal|contradict|exclusive> --a <ident> --b <ident> \
    --label <ident> [--target <path>]
    [--rationale <text>] [--metadata <json>] [--export] ...
Flag Required Description
--a <ident> yes Identifier of the first Claim.
--b <ident> yes Identifier of the second Claim.

All three verbs produce a binary structural relation between existing Claim references and return a generated relation helper Claim. By default the helper is internal to the package; --export adds the returned helper binding to __all__, and the compiled export manifest marks it as a structural relation interface.

decompose

gaia author decompose --whole <ident> --parts <csv> --label <ident> \
    [--target <path>]
    [--formula-template <atom|and|or>] [--formula-expr "<expr>"]
    [--rationale <text>] [--metadata <json>] ...
Flag Required Description
--whole <ident> yes Identifier of the whole Claim.
--parts <csv> yes Comma-separated identifiers of the part Claims.
--formula-template <atom\|and\|or> no Common-shape builder — renders formula=ClaimAtom(p) / formula=land(*atoms) / formula=lor(*atoms) from --parts. Mutually exclusive with --formula-expr.
--formula-expr "<expr>" no Escape-hatch for shapes outside the three templates (iff_and, iff_or, custom). Sandbox-validated — see Restricted-globals sandbox.

derive

gaia author derive (--conclusion <ident> | --conclusion-content "<prose>" | --conclusion-prose "<prose>") \
    --given <csv> --label <ident> [--target <path>]
    [--conclusion-label <ident>] [--rationale <text>]
    [--background <csv>] [--metadata <json>] ...
Flag Required Description
--conclusion <ident> one-of Reference an already-declared conclusion Claim.
--conclusion-content "<prose>" one-of Prose mode (auto-mint) — cli prepends slug = claim(prose) and uses the slug as conclusion. Mutually exclusive with --conclusion / --conclusion-prose. See Prose mode.
--conclusion-prose "<prose>" one-of Prose mode (inline) — emits derive('<prose>', ...) directly via the engine's Claim \| str polymorphism; no named binding minted. Mutex with the other two; no companion --conclusion-label (no Claim to label). The payload tag conclusion_kind is "inline_prose".
--conclusion-label <ident> no Explicit label for the auto-minted Claim (only valid with --conclusion-content).
--given <csv> yes Comma-separated premise identifiers.
--background <csv> no Comma-separated background identifiers (rendered as background=).

The envelope payload.conclusion_kind distinguishes the three shapes: "qid" (referencing a declared identifier), "auto_mint" (cli minted a named conclusion Claim), "inline_prose" (engine wraps the bare string into an anonymous Claim at runtime).

observe

gaia author observe (--conclusion <ident> | --observation-content "<prose>") \
    --label <ident> [--target <path>]
    [--observation-label <ident>] [--value <expr>] [--error <expr>]
    [--given <csv>] [--source-refs <csv>] [--rationale <text>] [--metadata <json>] ...
Flag Required Description
--conclusion <ident> one-of Identifier of the observed Claim, Variable, or Distribution.
--observation-content "<prose>" one-of Prose mode for discrete observations only. Mutex with --value / --error (those target a Variable or Distribution).
--observation-label <ident> no Explicit label for the auto-minted Claim.
--value <expr> no Numeric / Quantity expression for the continuous observation (value= kwarg).
--error <expr> no Observation error sigma or Distribution (error= kwarg); requires --value.
--given <csv> no Premise identifiers (discrete conditional form).
--source-refs <csv> no Deprecated transition flag. Prefer --rationale "... [@CitationKey]" so citations resolve through references.json.

compute

gaia author compute --conclusion-type <ident> --label <ident> [--target <path>]
    [--fn <ident>] [--given <csv>] [--rationale <text>] [--metadata <json>] ...
Flag Required Description
--conclusion-type <ident> yes Identifier of the result-type Claim.
--fn <ident> no Identifier of the compute function (must already be declared in the package).
--given <csv> no Comma-separated input identifiers.

The decorator form @compute stays at Python-source level (same logic as compose / composition).

infer

gaia author infer --evidence <ident> \
    (--hypothesis <ident> | --hypothesis-content "<prose>") \
    --p-e-given-h <float> --label <ident> [--target <path>]
    [--hypothesis-label <ident>] [--p-e-given-not-h <float>]
    [--given <csv>] [--rationale <text>] [--metadata <json>] ...
Flag Required Description
--evidence <ident> yes Identifier of the evidence Claim.
--hypothesis <ident> one-of Reference an already-declared hypothesis Claim.
--hypothesis-content "<prose>" one-of Prose mode — mint a fresh hypothesis Claim.
--hypothesis-label <ident> no Explicit label override for prose mode.
--p-e-given-h <float> yes P(evidence | hypothesis).
--p-e-given-not-h <float> no P(evidence | NOT hypothesis); DSL default 0.5. Omitting it emits a gaia build check / gaia run infer warning because an explicit background/false-positive rate is preferable when known.
--given <csv> no Conditioning Claim identifiers.

associate

gaia author associate --a <ident> --b <ident> \
    --p-a-given-b <float> --p-b-given-a <float> --label <ident> [--target <path>]
    [--pattern <name>] [--rationale <text>] [--metadata <json>] [--export] ...
Flag Required Description
--a <ident> yes First Claim.
--b <ident> yes Second Claim.
--p-a-given-b <float> yes P(a | b).
--p-b-given-a <float> yes P(b | a).
--pattern <name> no Optional engine-pattern hint.

associate(...) returns an association helper Claim. By default the helper is internal to the package; --export adds the returned helper binding to __all__, and the compiled export manifest marks it as a probabilistic relation interface with endpoint QIDs and conditional probabilities. If neither endpoint has a declared marginal prior, inference lowers the association by a local Jaynes MaxEnt closure of the 2x2 joint table and emits a warning recommending register_prior(...) on at least one endpoint.

parameter

gaia author parameter --variable <ident> --value <expr> --label <ident> \
    [--target <path>] [--content <text>] [--title <text>] [--prior <float>]
    [--rationale <text>] [--metadata <json>] ...
Flag Required Description
--variable <ident> yes Identifier of the bound Variable.
--value <expr> yes Value expression — numeric literal or Quantity.
--content <text> no Optional Claim-content prose.
--prior <float> no Inline prior in (0, 1).

--rationale routes through metadata['rationale'] because parameter() has no top-level rationale= kwarg.

register-prior

gaia author register-prior --claim <ident> --value <float> \
    --justification <text> [--target <path>]
    [--source-id <text>] [--statement-label <ident>] [--metadata <json>] ...
Flag Required Description
--claim <ident> yes Claim the prior attaches to.
--value <float> yes Prior value in (0, 1).
--justification <text> yes Free-text justification.
--source-id <text> no Source identifier; defaults derived from claim.
--statement-label <ident> no Optional trailing-comment label; no semantic effect.

The verb writes a bare expression statement (register_prior(...)) — no LHS binding, since register_prior() returns None.

depends-on

gaia author depends-on --conclusion <ident> --given <csv> --label <ident> \
    [--target <path>] [--rationale <text>] [--background <csv>] [--metadata <json>] ...
Flag Required Description
--conclusion <ident> yes Dependent Claim.
--given <csv> yes Comma-separated premise identifiers.
--background <csv> no Comma-separated background identifiers.

candidate-relation

gaia author candidate-relation --claims <csv> --pattern <name> --label <ident> \
    [--target <path>] [--rationale <text>] [--background <csv>] [--metadata <json>] ...
Flag Required Description
--claims <csv> yes Variadic claim identifiers.
--pattern <name> yes One of equal / contradict / exclusive. contradict requires exactly two claims.

materialize

gaia author materialize --scaffold <ident> --by <csv> --label <ident> \
    [--target <path>] [--rationale <text>] [--metadata <json>] ...
Flag Required Description
--scaffold <ident> yes Scaffold identifier the materialization targets.
--by <csv> yes Comma-separated identifiers used as the by= arg (single-element scalars and multi-element lists both render as by=[...]).

File-based verbs — compose / composition

The two composition verbs do not append a statement to __init__.py. The composition primitive is a Python-decorator-level concept (its body is an arbitrary Python function capturing nested Action invocations through a ContextVar), so the cli takes the file containing the decorated function as input and registers its metadata.

gaia author <compose|composition> --from-file <path> [--target <pkg-root>]
    [--check/--no-check] [--human] [--interactive] [--json/--no-json]
Flag Required Description
--from-file <path> yes Path to the Python file containing the decorated function.
--target <pkg-root> no Target package root; defaults to cwd.

Validation contract (each failure exits 2 with a structured diagnostic):

  • --from-file must exist and parse as valid Python.
  • Exactly one @compose / @composition-decorated FunctionDef per file (the one-compose-per-file rule). Both bare names and <module>.compose Attribute-shaped references are counted.
  • The decorator must carry name= and version= string kwargs.
  • The decorated function's return annotation must read Claim (or "Claim" as a forward-ref string).

Registration target: [[tool.gaia.compositions]] as a TOML array-of-tables in pyproject.toml. Each entry carries name / version / file / function / registered_at. Re-running for the same name rewrites in place (idempotent).

--check: when on (default), runs postwrite_check(target_root) after registration succeeds. Registration is the truth-bearing action; it stays on disk regardless of post-write outcome. On post-write failure: envelope returns status="error", code=1, source="postwrite" diagnostic, payload still carries registration details with check="failed". On success: payload gains check.knowledge_count / check.strategy_count / check.operator_count.

gaia pkg scaffold

Bootstrap a fresh -gaia package directory layout.

gaia pkg scaffold --target <path> [--name <pkg-name>] [--namespace <ns>]
    [--description <text>] [--with-uuid] [--docstring <text>]
    [--check/--no-check] [--human] [--interactive] [--json/--no-json]
Flag Required Description
--target <path> yes Directory to initialise (must be empty or non-existent).
--name <pkg-name> no Package name; must end with -gaia. Defaults to target directory name.
--namespace <ns> no Package namespace; defaults to the import name.
--description <text> no Short description for pyproject.toml.
--with-uuid no Opt in to writing [tool.gaia].uuid; default is to omit it.
--docstring <text> no Optional module docstring for the generated src/<import_name>/__init__.py.

The import_name is derived from --name by stripping the trailing -gaia and converting hyphens to underscores (foo-bar-gaiafoo_bar). This matches the engine's loader convention; the cli does not accept a separate --import-name override because doing so produced packages the engine could not load. If the derived name collides with a Python stdlib module (e.g. os-gaiaos), the verb refuses with exit 4.

The verb writes:

  • pyproject.toml with [tool.gaia] type / namespace; uuid is written only when --with-uuid is passed.
  • src/<import_name>/__init__.py importing the minimal DSL seed from gaia.engine.lang import claim plus __all__: list[str] = []. It does not seed a placeholder claim; subsequent gaia author <verb> calls populate the file. They update __all__ only when --export is explicit.
  • .gaia/.gitkeep so the cli postwrite check can find the IR artifact directory.

--check defaults off because a fresh scaffold has no declarations yet. When enabled, it runs the same postwrite_check the statement-emitting verbs use against the freshly created package; the result is surfaced under payload.check.

Refuses to write into a non-empty target (exit 2, prewrite.collision). Validates --name ends with -gaia (exit 4, prewrite.target_not_gaia_package). Rejects a derived import name that is not a valid Python identifier or collides with stdlib (exit 4, prewrite.target_invalid).

Envelope shape

Every gaia author <verb> (and gaia pkg scaffold) invocation writes a single JSON object to stdout matching this schema:

{
  "status": "ok" | "error" | "aborted",
  "code": 0 | 1 | 2 | 3 | 4,
  "verb": "<verb_name>",
  "payload": { /* verb-specific keys */ },
  "warnings": [ "<str>", ... ],
  "diagnostics": [
    {
      "kind": "<str>",
      "level": "error" | "warning",
      "message": "<str>",
      "source": "prewrite" | "postwrite" | "stub",
      "where": { /* optional structured locator */ }
    }
  ]
}
Field Purpose
status Outcome class. "ok" for successful writes, "error" for any failure that prevented the write or compile, "aborted" for user-driven --interactive aborts.
code Semantic exit code (0–4). Mirrors the process exit status.
verb The verb name (note / claim / derive / ...). Dispatch table key.
payload Verb-specific success payload (e.g. label, written_to, snippet, auto_generated, check.{knowledge,strategy,operator}_count).
warnings Flat string list of human-readable warning messages — convenience for log scraping.
diagnostics Structured list of error/warning entries. Each carries a kind an agent can dispatch on.

Payload — common keys

Key When Description
target always Resolved absolute path of the target package.
written_to statement-emitting success Path of the file the cli appended to (src/<import_name>/__init__.py).
label statement-emitting success The --label value (None for register-prior).
verb always Echo of the verb name (alongside the top-level verb).
snippet statement-emitting success The exact Python source string appended to the file.
auto_generated prose-mode success List of {label, snippet} for each auto-minted Claim.
check.knowledge_count / check.strategy_count / check.operator_count success + --check Counts from the post-write compile.
check success + --no-check Literal "skipped".
check compose post-write failure Literal "failed".

Exit codes

The semantic exit-code table is fixed by gaia/cli/commands/author/_envelope.py:

Code Meaning Typical kinds
0 Success (or --interactive abort, which is also a non-failure).
1 Pre-write structural failure or post-write check failure. prewrite.self_loop, prewrite.order_structure, postwrite.compile_fail, postwrite.check_fail
2 Input syntax error or unimplemented stub. prewrite.syntax, prewrite.expr_unsafe, stub.not_implemented
3 Identifier collision or unresolved reference. prewrite.collision, prewrite.reference_unresolved
4 System / IO error. Target missing, target not a -gaia package, target pyproject invalid. prewrite.target_missing, prewrite.target_not_gaia_package, prewrite.target_invalid

Warning kinds (prewrite.label_shadow, prewrite.deprecated_ref) flow through the envelope's warnings and diagnostics arrays at level: "warning" and map to exit code 0 — they are informational, not blocking.

Pre-write invariants

Pre-write always runs before any file write. Fail-fast: the first invariant to trip emits its diagnostic and aborts the run. Ordering matters because the first failure determines kind and exit code.

  1. (a) Target validity--target exists, is a -gaia package directory, has a parseable pyproject.toml with [tool.gaia]. Failure kinds: prewrite.target_missing / prewrite.target_not_gaia_package / prewrite.target_invalid (all exit 4).
  2. (b) Syntactic well-formedness — the proposed generated statement (and any prepended prose-mode auto-Claim statements) parse as valid Python. Failure kind: prewrite.syntax (exit 2). Sandbox failures for --predicate / --formula-expr distinguish via prewrite.expr_unsafe (also exit 2).
  3. (d) Structural self-loop check — the proposed op's references set must not contain the proposed --label. Failure kind: prewrite.self_loop (exit 1).
  4. (c) Collision and reference resolution — the proposed --label must not collide with an existing module binding or DSL surface name; all references must resolve to module bindings (or one of the prepended-statement labels in the same invocation). Failure kinds: prewrite.collision / prewrite.reference_unresolved (both exit 3).

The (d)-before-(c) ordering is deliberate: structural self-loops surface as their own kind (exit 1) instead of being eaten by the collision / unresolved-ref machinery (exit 3). Documented in gaia/cli/commands/author/_prewrite.py.

Warning kinds (post-(c), non-blocking)

Kind Fires when Behavior
prewrite.label_shadow The proposed --label collides with a Python builtin or DSL surface name (defensive — most shadow cases are intercepted by the (c) hard error). Run proceeds; warning flows to envelope.
prewrite.deprecated_ref A call site in the generated code or one of references names a DSL symbol carrying a DeprecationWarning in the engine (sourced via AST scan of gaia/engine/lang/dsl/**.py at cli import; merged with a small hand-curated fallback for safety). Scan is narrowed to call positions, so a binding name that happens to match a deprecated factory does not trip the warning. Run proceeds; replacement hint in where.

Both flow through --interactive: in human mode + --interactive + at least one warning, the cli surfaces a numbered prompt with default N. JSON mode auto-suppresses prompts because agents cannot drive stdin; warnings still ship in envelope.warnings. An --interactive abort produces status="aborted" / code=0 and a user.aborted diagnostic.

Prose mode — "introducing new statement" verbs

Four verbs accept a --<arg>-content flag that introduces a fresh Claim inline rather than referencing an existing identifier:

Verb Flag Mutex with Label override
derive --conclusion-content "<prose>" --conclusion, --conclusion-prose --conclusion-label
claim --predicate "<formula-expr>" — (predicate-mode is additive, not replacement)
infer --hypothesis-content "<prose>" --hypothesis --hypothesis-label
observe --observation-content "<prose>" --conclusion, also --value/--error --observation-label

These verbs share the rationale that the named Claim-ref arg is introducing a new statement that the verb itself is bringing into existence (the derivation's conclusion, the hypothesis under test, the observation's proposition). Auto-generating slug = claim(prose) and using the slug downstream is semantically honest. The remaining 13 author verbs are either linking existing claims (Structural / Scaffold) or quantitative (compute / parameter / register-prior), where prose-mode auto-mint would awkwardly bundle ops.

The cli derives a snake-case slug for the auto-Claim from the first several word-tokens of the prose, lowercased; numeric leading tokens get a c_ prefix; collisions against caller-supplied identifiers get _2 / _3 suffixes. Module-symbol collisions still surface as the standard prewrite.collision hard error.

--predicate for claim is not a prose-mode flag; it is a formula-expression flag rendered as the formula= kwarg. The expression goes through the same restricted-globals sandbox as decompose --formula-expr.

Inline-prose mode — derive --conclusion-prose

derive carries a third shape, --conclusion-prose "<prose>", that does not mint a named binding. The prose is emitted at the call site as a bare string literal:

visibility_warrant = derive('Stars are visible tonight.', given=[hypothesis], label='visibility_warrant')

The engine's derive(conclusion: Claim | str, ...) polymorphism wraps the string into an anonymous Claim at runtime. The shape is byte-text closer to a hand-authored package that uses the inline-string idiom (see the Galileo prose-mode divergence note in the walkthrough under examples/galileo-v0-5-gaia/), at the cost of losing referenceability — subsequent author calls cannot reach the conclusion Claim by name. The envelope payload tags the shape via conclusion_kind:

conclusion_kind Trigger Source effect
"qid" --conclusion <ident> Reuses a declared identifier; no extra statements written.
"auto_mint" --conclusion-content "<prose>" Prepends slug = claim(prose); uses the slug.
"inline_prose" --conclusion-prose "<prose>" Emits derive('<prose>', ...) directly; no prepended statement.

Pick --conclusion-content (auto-mint, default) when downstream author calls might need to reference the conclusion. Pick --conclusion-prose when byte-text fidelity to a target source layout matters more.

Restricted-globals sandbox

gaia author decompose --formula-expr and gaia author claim --predicate both accept Python expressions, evaluated by the engine at package import time. To prevent os.system(...)-shaped attacks at pre-write time, the cli sandbox-validates the expression against a whitelist:

Category Names
Formula primitives land, lor, lnot, implies, iff, equals, forall, exists
Atomic operand ClaimAtom
Distribution factories Normal, LogNormal, Beta, Exponential, Gamma, StudentT, Cauchy, ChiSquared, Binomial, Poisson, Distribution

Allowed AST forms: function calls, keyword arguments, arithmetic / comparison operators, name lookups (against the whitelist), numeric / string / boolean / None constants, tuples / lists.

Rejected: attribute access (x.attr), subscripting (x[k]), lambdas, comprehensions, dunder names (__import__), **kwargs unpacking, any identifier outside the whitelist. Failures emit prewrite.expr_unsafe (exit 2), distinct from prewrite.syntax, so an agent can dispatch on the kind to distinguish "your Python is wrong" from "your Python parses but the sandbox does not allow it".

Compose validate-and-register

The compose / composition verbs (see File-based verbs) extract metadata from a Python file with a single @compose / @composition-decorated FunctionDef and record that metadata in the target package's pyproject.toml as a [[tool.gaia.compositions]] array-of-tables entry. Five string fields: name / version / file / function / registered_at (UTC ISO timestamp). Insert-or-update by composition name (case- sensitive).

The one-compose-per-file rule is enforced at AST-walk time. If a file contains zero or more than one decorated function, the verb exits 2 with a structured diagnostic identifying the count.

The minimal pattern in the file:

from gaia.engine.lang import compose

@compose(name="my_strategy", version="0.1.0")
def my_strategy(...) -> Claim:
    ...

The cli does not import the file (no side effects); it only walks the AST. The decorated function's body is left untouched; only the metadata flows to pyproject.toml.

Galileo as a worked example

The canonical Galileo falling-body thought experiment lives at examples/galileo-v0-5-gaia/. The package uses 5 author verbsnote, claim, derive, equal, contradict — and exercises 15 total statements (3 notes + 3 claims + 5 derives + 2 equals + 1 contradict + 1 register_prior in priors.py).

A scripted walkthrough that re-creates the same package end-to-end via the cli lives at examples/galileo-v0-5-gaia/CLI-AUTHORED.md. The walkthrough demonstrates:

  1. gaia pkg scaffold to bootstrap the package directory layout.
  2. gaia author note invocations for the three contextual notes.
  3. gaia author claim invocations for the three model / observation claims.
  4. gaia author derive invocations for the five model predictions (using --conclusion-content prose mode, with the structural divergence from the hand-authored shape documented inline).
  5. gaia author equal and gaia author contradict for the four structural relations.
  6. gaia author register-prior for the empirical-background prior.

A pytest fixture at tests/cli/galileo_demo/test_equivalence.py drives the same cli sequence at test time and asserts that the resulting package is content-equivalent to the hand-authored ground truth — every Claim content string in the hand-authored package appears in the cli-authored package, every structural relation (equal / contradict / derive-given) maps to the same content pair, and both compile through gaia build compile to a graph with matching knowledge / strategy / operator counts.

Prose-mode auto-mint introduces additional named-Claim bindings (one per --conclusion-content invocation) that the hand-authored file expresses as inline string literals to derive()'s polymorphic Claim | str conclusion argument. The two shapes compile to equivalent runtime graphs but diverge at the source-text level; see CLI-AUTHORED.md for the explicit delta inventory.

Mendel as a worked example (bayes + Variable + formula + multi-file)

The Mendel single-factor cross example at examples/mendel-v0-5-gaia/ exercises the harder cli surface. Where Galileo uses 5 author verbs, Mendel additionally reaches for:

  • gaia author variable to declare two Variable(...) typed terms (f2_total_count, f2_dominant_count).
  • gaia author observe --conclusion <Variable> --value <number> to record the quantitative count data used by Bayes comparison.
  • gaia bayes model with an inline Binomial(...) / BetaBinomial(...) Distribution expression on --distribution, followed by gaia bayes compare for the quantitative count-comparison sub-pipeline.
  • gaia pkg add-module + gaia author register-prior --file priors.py for the multi-file authoring layout that mirrors the hand-authored package's priors.py sibling module.

A scripted walkthrough lives at examples/mendel-v0-5-gaia/CLI-AUTHORED.md. The pytest fixture at tests/cli/mendel_demo/test_equivalence.py re-runs the cli sequence on every PR-gate run and asserts equivalence through the multi-level tolerance helper at tests/cli/_equivalence_levels.py — BYTE_TEXT on the user-authored content axes + structural counts, CONTENT_SET on the intrinsic single---label discipline axis. Mendel is therefore the empirical demonstration that the cli surface covers the v0.5 engine end-to-end.

See also

  • gaia pkg — install dependencies, manage module/import plumbing, publish, and scaffold packages.
  • gaia build — compile / check the package the cli wrote.
  • Foundational CLI workflow — narrative tour of the cli day-to-day.
  • CLI Commands — workflow-oriented guide for the broader cli surface.

Implementation

gaia.cli.commands.author

gaia author subcommand group — optional authoring convenience.

This module exposes the gaia author <verb> namespace, where <verb> matches one of the DSL surface verbs (claim / artifact / figure / equal / derive / note / question / contradict / exclusive / decompose / observe / compute / infer / associate / parameter / register_prior / depends_on / candidate_relation / materialize / variable / compose / composition). Direct SDK authoring (gaia sdk + writing the DSL in Python) is the primary path; this CLI is an OPTIONAL convenience for humans and agents alike. When used, it owns identifier collision checks, reference resolution, pre-write defensive validation, file appending into the package's composed authored/ submodule (never the package-root __init__.py), and (by default) a post-write gaia build check to make sure the package still compiles. Output is JSON-by-default through a uniform envelope (see :mod:._envelope); --human opts into a human-readable rendering of the same payload.

The author surface ships 22 verbs end-to-end against a uniform pre-write envelope skeleton: 20 statement-emitting verbs (claim / artifact / figure / equal / derive / note / question / contradict / exclusive / decompose / observe / compute / infer / associate / parameter / register_prior / depends_on / candidate_relation / materialize / variable) plus the two file-based compose / composition validate-and-register verbs (see :mod:.compose). Prose-mode --<arg>-content flags, two pre-write warning kinds, and a restricted-globals formula sandbox round out the authoring surface; the engine's deprecation catalog is discovered via an AST scan over the DSL source (see :mod:._deprecation_scan).

See docs/reference/cli/author.md for the per-verb contract.

artifact_command

artifact_command(dsl_binding_name: str = typer.Option(..., '--dsl-binding-name', help='Python module-scope identifier to bind.'), kind: str = typer.Option(..., '--kind', help=f'Artifact kind: {', '.join(sorted(ARTIFACT_KINDS))}.'), source: str | None = typer.Option(None, '--source', help='Citation key in references.json.'), locator: str | None = typer.Option(None, '--locator', help='Source-local locator.'), path: str | None = typer.Option(None, '--path', help='Package-relative artifact path.'), caption: str | None = typer.Option(None, '--caption', help='Caption for visual artifacts.'), description: str | None = typer.Option(None, '--description', help='Description for attachments.'), media_type: str | None = typer.Option(None, '--media-type', help='Optional MIME type.'), content: str | None = typer.Option(None, '--content', help='Override note content.'), title: str | None = typer.Option(None, '--title', help='Optional note title.'), target: str = typer.Option('.', '--target', help='Path to the target Gaia package.'), file: str | None = typer.Option(None, '--file', help='Relative module file under src/<import_name>.'), export: bool = typer.Option(False, '--export/--no-export', help='Export the artifact binding.'), check: bool = typer.Option(True, '--check/--no-check', help='Run post-write build check.'), human: bool = typer.Option(False, '--human', help='Render human-readable output.'), interactive: bool = typer.Option(False, '--interactive', help='Prompt on pre-write warnings.'), json_: bool = typer.Option(True, '--json/--no-json', help='JSON-first output.')) -> None

Append an artifact(...) note anchor statement.

Source code in gaia/cli/commands/author/artifact.py
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
def artifact_command(
    dsl_binding_name: str = typer.Option(
        ..., "--dsl-binding-name", help="Python module-scope identifier to bind."
    ),
    kind: str = typer.Option(
        ...,
        "--kind",
        help=f"Artifact kind: {', '.join(sorted(ARTIFACT_KINDS))}.",
    ),
    source: str | None = typer.Option(None, "--source", help="Citation key in references.json."),
    locator: str | None = typer.Option(None, "--locator", help="Source-local locator."),
    path: str | None = typer.Option(None, "--path", help="Package-relative artifact path."),
    caption: str | None = typer.Option(None, "--caption", help="Caption for visual artifacts."),
    description: str | None = typer.Option(
        None, "--description", help="Description for attachments."
    ),
    media_type: str | None = typer.Option(None, "--media-type", help="Optional MIME type."),
    content: str | None = typer.Option(None, "--content", help="Override note content."),
    title: str | None = typer.Option(None, "--title", help="Optional note title."),
    target: str = typer.Option(".", "--target", help="Path to the target Gaia package."),
    file: str | None = typer.Option(
        None, "--file", help="Relative module file under src/<import_name>."
    ),
    export: bool = typer.Option(False, "--export/--no-export", help="Export the artifact binding."),
    check: bool = typer.Option(True, "--check/--no-check", help="Run post-write build check."),
    human: bool = typer.Option(False, "--human", help="Render human-readable output."),
    interactive: bool = typer.Option(False, "--interactive", help="Prompt on pre-write warnings."),
    json_: bool = typer.Option(True, "--json/--no-json", help="JSON-first output."),
) -> None:
    """Append an ``artifact(...)`` note anchor statement."""
    del json_
    _validate_cli_artifact(
        verb="artifact",
        kind=kind,
        source=source,
        locator=locator,
        path=path,
        caption=caption,
        description=description,
        media_type=media_type,
        target=str(target),
        human=human,
    )
    generated_code = _render_artifact_statement(
        binding_name=dsl_binding_name,
        kind=kind,
        source=source,
        locator=locator,
        path=path,
        caption=caption,
        description=description,
        media_type=media_type,
        content=content,
        title=title,
    )
    proposed_op = ProposedAuthorOp(
        verb="artifact",
        kind="reasoning",
        label=dsl_binding_name,
        references=[],
        generated_code=generated_code,
        required_imports=("artifact",),
        target_file=normalize_file_option(file),
        export=export,
    )
    run_author_op(proposed_op, target=target, human=human, check=check, interactive=interactive)

figure_command

figure_command(dsl_binding_name: str = typer.Option(..., '--dsl-binding-name', help='Python module-scope identifier to bind.'), source: str | None = typer.Option(None, '--source', help='Citation key in references.json.'), locator: str | None = typer.Option(None, '--locator', help='Source-local figure locator.'), path: str | None = typer.Option(None, '--path', help='Package-relative image path.'), caption: str | None = typer.Option(None, '--caption', help='Figure caption.'), description: str | None = typer.Option(None, '--description', help='Optional description.'), media_type: str | None = typer.Option(None, '--media-type', help='Optional MIME type.'), content: str | None = typer.Option(None, '--content', help='Override note content.'), title: str | None = typer.Option(None, '--title', help='Optional note title.'), target: str = typer.Option('.', '--target', help='Path to the target Gaia package.'), file: str | None = typer.Option(None, '--file', help='Relative module file under src/<import_name>.'), export: bool = typer.Option(False, '--export/--no-export', help='Export the figure binding.'), check: bool = typer.Option(True, '--check/--no-check', help='Run post-write build check.'), human: bool = typer.Option(False, '--human', help='Render human-readable output.'), interactive: bool = typer.Option(False, '--interactive', help='Prompt on pre-write warnings.'), json_: bool = typer.Option(True, '--json/--no-json', help='JSON-first output.')) -> None

Append a figure(...) artifact note anchor statement.

Source code in gaia/cli/commands/author/artifact.py
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
def figure_command(
    dsl_binding_name: str = typer.Option(
        ..., "--dsl-binding-name", help="Python module-scope identifier to bind."
    ),
    source: str | None = typer.Option(None, "--source", help="Citation key in references.json."),
    locator: str | None = typer.Option(None, "--locator", help="Source-local figure locator."),
    path: str | None = typer.Option(None, "--path", help="Package-relative image path."),
    caption: str | None = typer.Option(None, "--caption", help="Figure caption."),
    description: str | None = typer.Option(None, "--description", help="Optional description."),
    media_type: str | None = typer.Option(None, "--media-type", help="Optional MIME type."),
    content: str | None = typer.Option(None, "--content", help="Override note content."),
    title: str | None = typer.Option(None, "--title", help="Optional note title."),
    target: str = typer.Option(".", "--target", help="Path to the target Gaia package."),
    file: str | None = typer.Option(
        None, "--file", help="Relative module file under src/<import_name>."
    ),
    export: bool = typer.Option(False, "--export/--no-export", help="Export the figure binding."),
    check: bool = typer.Option(True, "--check/--no-check", help="Run post-write build check."),
    human: bool = typer.Option(False, "--human", help="Render human-readable output."),
    interactive: bool = typer.Option(False, "--interactive", help="Prompt on pre-write warnings."),
    json_: bool = typer.Option(True, "--json/--no-json", help="JSON-first output."),
) -> None:
    """Append a ``figure(...)`` artifact note anchor statement."""
    del json_
    _validate_cli_artifact(
        verb="figure",
        kind="figure",
        source=source,
        locator=locator,
        path=path,
        caption=caption,
        description=description,
        media_type=media_type,
        target=str(target),
        human=human,
    )
    generated_code = _render_figure_statement(
        binding_name=dsl_binding_name,
        source=source,
        locator=locator,
        path=path,
        caption=caption,
        description=description,
        media_type=media_type,
        content=content,
        title=title,
    )
    proposed_op = ProposedAuthorOp(
        verb="figure",
        kind="reasoning",
        label=dsl_binding_name,
        references=[],
        generated_code=generated_code,
        required_imports=("figure",),
        target_file=normalize_file_option(file),
        export=export,
    )
    run_author_op(proposed_op, target=target, human=human, check=check, interactive=interactive)

associate_command

associate_command(label: str | None = typer.Option(None, '--label', help='Engine `label=` kwarg on the rendered associate(...) call. Distinct from --dsl-binding-name (the Python LHS).'), dsl_binding_name: str | None = typer.Option(None, '--dsl-binding-name', help='Python LHS for the rendered statement (``<name> = associate(...)``). Omit to emit a bare expression.'), a: str = typer.Option(..., '--a', help='Identifier of the first Claim.'), b: str = typer.Option(..., '--b', help='Identifier of the second Claim.'), p_a_given_b: float = typer.Option(..., '--p-a-given-b', help='P(a | b) — required.'), p_b_given_a: float = typer.Option(..., '--p-b-given-a', help='P(b | a) — required.'), target: str = typer.Option('.', '--target', help='Path to the target Gaia package (default: cwd).'), file: str | None = typer.Option(None, '--file', help='Relative path under src/<import_name>/ to write into. Default: `__init__.py`.'), pattern: str | None = typer.Option(None, '--pattern', help='Optional structural pattern (equal / contradict / exclusive).'), rationale: str | None = typer.Option(None, '--rationale', help='Optional natural-language justification.'), metadata: str | None = typer.Option(None, '--metadata', help='Optional JSON-encoded metadata dict.'), export: bool = typer.Option(False, '--export/--no-export', help='Add the returned association helper to __all__ as a public probabilistic relation export. Default off: exports are curated explicitly.'), check: bool = typer.Option(True, '--check/--no-check', help='Run post-write `gaia build check` after a successful write (default on).'), human: bool = typer.Option(False, '--human', help='Render the envelope in human-readable form instead of JSON.'), interactive: bool = typer.Option(False, '--interactive', help='Prompt on pre-write warnings (human mode only).'), json_: bool = typer.Option(True, '--json/--no-json', help='JSON-first output (default; redundant for clarity).')) -> None

Append an associate(...) probabilistic-association statement.

Example

gaia author associate --a my_claim_a --b my_claim_b \ --p-a-given-b 0.9 --p-b-given-a 0.6 \ --dsl-binding-name my_association

Source code in gaia/cli/commands/author/associate.py
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
def associate_command(
    label: str | None = typer.Option(
        None,
        "--label",
        help=(
            "Engine `label=` kwarg on the rendered associate(...) call. "
            "Distinct from --dsl-binding-name (the Python LHS)."
        ),
    ),
    dsl_binding_name: str | None = typer.Option(
        None,
        "--dsl-binding-name",
        help=(
            "Python LHS for the rendered statement (``<name> = "
            "associate(...)``). Omit to emit a bare expression."
        ),
    ),
    a: str = typer.Option(..., "--a", help="Identifier of the first Claim."),
    b: str = typer.Option(..., "--b", help="Identifier of the second Claim."),
    p_a_given_b: float = typer.Option(..., "--p-a-given-b", help="P(a | b) — required."),
    p_b_given_a: float = typer.Option(..., "--p-b-given-a", help="P(b | a) — required."),
    target: str = typer.Option(
        ".", "--target", help="Path to the target Gaia package (default: cwd)."
    ),
    file: str | None = typer.Option(
        None,
        "--file",
        help=("Relative path under src/<import_name>/ to write into. Default: `__init__.py`."),
    ),
    pattern: str | None = typer.Option(
        None,
        "--pattern",
        help="Optional structural pattern (equal / contradict / exclusive).",
    ),
    rationale: str | None = typer.Option(
        None, "--rationale", help="Optional natural-language justification."
    ),
    metadata: str | None = typer.Option(
        None, "--metadata", help="Optional JSON-encoded metadata dict."
    ),
    export: bool = typer.Option(
        False,
        "--export/--no-export",
        help=(
            "Add the returned association helper to __all__ as a public probabilistic "
            "relation export. Default off: exports are curated explicitly."
        ),
    ),
    check: bool = typer.Option(
        True,
        "--check/--no-check",
        help="Run post-write `gaia build check` after a successful write (default on).",
    ),
    human: bool = typer.Option(
        False, "--human", help="Render the envelope in human-readable form instead of JSON."
    ),
    interactive: bool = typer.Option(
        False, "--interactive", help="Prompt on pre-write warnings (human mode only)."
    ),
    json_: bool = typer.Option(
        True, "--json/--no-json", help="JSON-first output (default; redundant for clarity)."
    ),
) -> None:
    r"""Append an ``associate(...)`` probabilistic-association statement.

    Example:
        gaia author associate --a my_claim_a --b my_claim_b \
            --p-a-given-b 0.9 --p-b-given-a 0.6 \
            --dsl-binding-name my_association
    """
    del json_

    if pattern is not None and pattern not in _ASSOCIATE_PATTERNS:
        allowed = ", ".join(sorted(_ASSOCIATE_PATTERNS))
        emit_syntax_error(
            "associate",
            f"--pattern must be one of: {allowed} (got {pattern!r})",
            target=str(target),
            human=human,
        )
        return

    metadata_dict, metadata_error = parse_metadata(metadata)
    if metadata_error:
        emit_syntax_error("associate", metadata_error, target=str(target), human=human)
        return

    if not validate_identifier_flag(
        a, verb="associate", flag="--a", target=str(target), human=human
    ):
        return
    if not validate_identifier_flag(
        b, verb="associate", flag="--b", target=str(target), human=human
    ):
        return

    generated_code = _render_associate_statement(
        binding_name=dsl_binding_name,
        engine_label=label,
        a=a,
        b=b,
        p_a_given_b=p_a_given_b,
        p_b_given_a=p_b_given_a,
        pattern=pattern,
        rationale=rationale,
        metadata=metadata_dict,
    )
    target_file = normalize_file_option(file)
    references = [a, b]
    proposed_op = ProposedAuthorOp(
        verb="associate",
        kind="reasoning",
        label=dsl_binding_name,
        references=references,
        generated_code=generated_code,
        required_imports=("associate",),
        target_file=target_file,
        sibling_imports=build_sibling_imports(references, target_file=target_file),
        export=export,
    )
    run_author_op(
        proposed_op,
        target=target,
        human=human,
        check=check,
        interactive=interactive,
    )

candidate_relation_command

candidate_relation_command(label: str | None = typer.Option(None, '--label', help='Engine `label=` kwarg on the rendered candidate_relation(...) call. Distinct from --dsl-binding-name (the Python LHS).'), dsl_binding_name: str | None = typer.Option(None, '--dsl-binding-name', help='Python LHS for the rendered statement (``<name> = candidate_relation(...)``). Omit to emit a bare expression.'), claims: str = typer.Option(..., '--claims', help='Comma-separated references to at least two Claim(s): local Claim identifiers and/or pulled-claim QIDs (lkm:<package>::<label>).'), target: str = typer.Option('.', '--target', help='Path to the target Gaia package (default: cwd).'), file: str | None = typer.Option(None, '--file', help='Relative path under src/<import_name>/ to write into. Default: `__init__.py`.'), pattern: str | None = typer.Option(None, '--pattern', help='Optional structural pattern (equal / contradict / exclusive).'), rationale: str | None = typer.Option(None, '--rationale', help='Optional natural-language justification.'), background: str | None = typer.Option(None, '--background', help='Comma-separated background Knowledge identifiers.'), metadata: str | None = typer.Option(None, '--metadata', help='Optional JSON-encoded metadata dict.'), export: bool = typer.Option(False, '--export/--no-export', help='Add --dsl-binding-name to __all__ on a successful write (default off for candidate_relation: scaffold-tier output is structural, not part of the public Knowledge surface).'), check: bool = typer.Option(True, '--check/--no-check', help='Run post-write `gaia build check` after a successful write (default on).'), human: bool = typer.Option(False, '--human', help='Render the envelope in human-readable form instead of JSON.'), interactive: bool = typer.Option(False, '--interactive', help='Prompt on pre-write warnings (human mode only).'), json_: bool = typer.Option(True, '--json/--no-json', help='JSON-first output (default; redundant for clarity).')) -> None

Append a candidate_relation(...) scaffold-tier hypothesised relation.

Example

gaia author candidate-relation \ --claims my_claim_a,my_claim_b,my_claim_c \ --pattern equal --dsl-binding-name my_maybe_equal

Source code in gaia/cli/commands/author/candidate_relation.py
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
def candidate_relation_command(
    label: str | None = typer.Option(
        None,
        "--label",
        help=(
            "Engine `label=` kwarg on the rendered candidate_relation(...) "
            "call. Distinct from --dsl-binding-name (the Python LHS)."
        ),
    ),
    dsl_binding_name: str | None = typer.Option(
        None,
        "--dsl-binding-name",
        help=(
            "Python LHS for the rendered statement (``<name> = "
            "candidate_relation(...)``). Omit to emit a bare expression."
        ),
    ),
    claims: str = typer.Option(
        ...,
        "--claims",
        help=(
            "Comma-separated references to at least two Claim(s): local Claim "
            "identifiers and/or pulled-claim QIDs (lkm:<package>::<label>)."
        ),
    ),
    target: str = typer.Option(
        ".", "--target", help="Path to the target Gaia package (default: cwd)."
    ),
    file: str | None = typer.Option(
        None,
        "--file",
        help=("Relative path under src/<import_name>/ to write into. Default: `__init__.py`."),
    ),
    pattern: str | None = typer.Option(
        None,
        "--pattern",
        help="Optional structural pattern (equal / contradict / exclusive).",
    ),
    rationale: str | None = typer.Option(
        None, "--rationale", help="Optional natural-language justification."
    ),
    background: str | None = typer.Option(
        None, "--background", help="Comma-separated background Knowledge identifiers."
    ),
    metadata: str | None = typer.Option(
        None, "--metadata", help="Optional JSON-encoded metadata dict."
    ),
    export: bool = typer.Option(
        False,
        "--export/--no-export",
        help=(
            "Add --dsl-binding-name to __all__ on a successful write "
            "(default off for candidate_relation: scaffold-tier output is "
            "structural, not part of the public Knowledge surface)."
        ),
    ),
    check: bool = typer.Option(
        True,
        "--check/--no-check",
        help="Run post-write `gaia build check` after a successful write (default on).",
    ),
    human: bool = typer.Option(
        False, "--human", help="Render the envelope in human-readable form instead of JSON."
    ),
    interactive: bool = typer.Option(
        False, "--interactive", help="Prompt on pre-write warnings (human mode only)."
    ),
    json_: bool = typer.Option(
        True, "--json/--no-json", help="JSON-first output (default; redundant for clarity)."
    ),
) -> None:
    r"""Append a ``candidate_relation(...)`` scaffold-tier hypothesised relation.

    Example:
        gaia author candidate-relation \
            --claims my_claim_a,my_claim_b,my_claim_c \
            --pattern equal --dsl-binding-name my_maybe_equal
    """
    del json_

    if pattern is not None and pattern not in _CANDIDATE_PATTERNS:
        allowed = ", ".join(sorted(_CANDIDATE_PATTERNS))
        emit_syntax_error(
            "candidate_relation",
            f"--pattern must be one of: {allowed} (got {pattern!r})",
            target=str(target),
            human=human,
        )
        return

    metadata_dict, metadata_error = parse_metadata(metadata)
    if metadata_error:
        emit_syntax_error("candidate_relation", metadata_error, target=str(target), human=human)
        return

    claim_refs, claim_error = split_csv_refs(claims)
    if claim_error:
        emit_syntax_error(
            "candidate_relation",
            f"--claims rejected: {claim_error}",
            target=str(target),
            human=human,
            kind="prewrite.expr_unsafe",
        )
        return
    claim_list = claim_refs.rendered
    background_refs, background_error = split_csv_refs(background)
    if background_error:
        emit_syntax_error(
            "candidate_relation",
            f"--background rejected: {background_error}",
            target=str(target),
            human=human,
            kind="prewrite.expr_unsafe",
        )
        return
    background_list = background_refs.rendered
    if len(claim_list) < 2:
        emit_syntax_error(
            "candidate_relation",
            "--claims must list at least two identifiers",
            target=str(target),
            human=human,
        )
        return
    if pattern == "contradict" and len(claim_list) != 2:
        emit_syntax_error(
            "candidate_relation",
            '--pattern="contradict" requires exactly two --claims entries',
            target=str(target),
            human=human,
        )
        return

    generated_code = _render_candidate_relation_statement(
        binding_name=dsl_binding_name,
        engine_label=label,
        claims=claim_list,
        pattern=pattern,
        rationale=rationale,
        background=background_list,
        metadata=metadata_dict,
    )
    references = [*claim_refs.local, *background_refs.local]
    target_file = normalize_file_option(file)
    foreign_imports = tuple(
        (fi.module, fi.symbol, fi.alias)
        for fi in (*claim_refs.foreign_imports, *background_refs.foreign_imports)
    )
    proposed_op = ProposedAuthorOp(
        verb="candidate_relation",
        kind="scaffold",
        label=dsl_binding_name,
        references=references,
        generated_code=generated_code,
        required_imports=("candidate_relation",),
        target_file=target_file,
        sibling_imports=build_sibling_imports(references, target_file=target_file),
        foreign_imports=foreign_imports,
        export=export,
    )
    run_author_op(
        proposed_op,
        target=target,
        human=human,
        check=check,
        interactive=interactive,
    )

claim_command

claim_command(content: str = typer.Argument(..., help='Claim content (natural-language statement).'), dsl_binding_name: str | None = typer.Option(None, '--dsl-binding-name', help='Python module-scope identifier the rendered statement binds to (``<name> = claim(...)``). Omit to emit a bare expression statement. ``claim()`` does not take an engine ``label=`` kwarg, so this is the only label-like flag the verb exposes.'), target: str = typer.Option('.', '--target', help='Path to the target Gaia package (default: cwd).'), file: str | None = typer.Option(None, '--file', help='Relative path under src/<import_name>/ to write into (e.g. `priors.py`). Default: `__init__.py`. The file must already exist; use `gaia pkg add-module` to scaffold a fresh sibling.'), title: str | None = typer.Option(None, '--title', help='Optional short title for the claim.'), prior: float | None = typer.Option(None, '--prior', help='Optional direct prior in (0, 1), rendered as a prior= kwarg on the claim() call. Usually omit it: leaf-claim priors are normally recorded separately via `gaia author register-prior`, which keeps the belief value out of the claim definition and carries a source tag. Pass --prior only when a prior belongs inline on the claim itself.'), metadata: str | None = typer.Option(None, '--metadata', help='Optional JSON-encoded metadata dict.'), references: str | None = typer.Option(None, '--references', help="Comma-separated identifiers to whitelist inside the formula sandbox (no effect when --formula / --predicate is absent). References are NOT rendered into the claim's `background=` kwarg — use --background for that. Typical shape: `--references f2_total_count,f2_dominant_count --formula 'equals(f2_total_count, Constant(395, Nat))'`."), background: str | None = typer.Option(None, '--background', help="Comma-separated Knowledge identifiers passed to the rendered claim's `background=[...]` kwarg. Pre-write resolves each identifier in module scope. Independent of --references (--references is sandbox-only and not rendered into the claim call)."), predicate: str | None = typer.Option(None, '--predicate', help='Alias for --formula (predicate-mode shape). Backwards-compatible spelling; prefer --formula for new authoring.'), formula: str | None = typer.Option(None, '--formula', help="Predicate-logic expression rendered as the ``formula=`` kwarg. Validated by the formula sandbox (whitelist: land/lor/lnot/implies/iff/equals/forall/exists + ClaimAtom + Distribution factories + Variable/Constant + Nat/Real/Bool/Probability + references). Example: `--formula 'land(equals(my_var, Constant(395, Nat)), ClaimAtom(p))'`. Mutually exclusive with --predicate."), label: str | None = typer.Option(None, '--label', help="Optional Claim label. ``claim()`` does not take an engine ``label=`` kwarg, so the cli emits a follow-up ``<binding>.label = '<label>'`` assignment line after the rendered ``claim(...)`` call — matching the hand-authored pattern in the example packages. Requires --dsl-binding-name (no LHS means nothing to mutate)."), export: bool = typer.Option(False, '--export/--no-export', help="Add --dsl-binding-name to the target module's __all__ on a successful write. Default off: exports are the package's curated public Knowledge surface."), check: bool = typer.Option(True, '--check/--no-check', help='Run post-write `gaia build check` after a successful write (default on).'), human: bool = typer.Option(False, '--human', help='Render the envelope in human-readable form instead of JSON.'), interactive: bool = typer.Option(False, '--interactive', help='Prompt on pre-write warnings (currently a no-op; reserved for future use).'), json_: bool = typer.Option(True, '--json/--no-json', help='JSON-first output (default; redundant for clarity).')) -> None

Append a claim(...) Knowledge declaration.

Example

gaia author claim "The reaction is fast." \ --dsl-binding-name fast_reaction --prior 0.7

Source code in gaia/cli/commands/author/claim.py
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
def claim_command(
    content: str = typer.Argument(..., help="Claim content (natural-language statement)."),
    dsl_binding_name: str | None = typer.Option(
        None,
        "--dsl-binding-name",
        help=(
            "Python module-scope identifier the rendered statement binds to "
            "(``<name> = claim(...)``). Omit to emit a bare expression "
            "statement. ``claim()`` does not take an engine ``label=`` "
            "kwarg, so this is the only label-like flag the verb exposes."
        ),
    ),
    target: str = typer.Option(
        ".", "--target", help="Path to the target Gaia package (default: cwd)."
    ),
    file: str | None = typer.Option(
        None,
        "--file",
        help=(
            "Relative path under src/<import_name>/ to write into (e.g. "
            "`priors.py`). Default: `__init__.py`. The file must already "
            "exist; use `gaia pkg add-module` to scaffold a fresh sibling."
        ),
    ),
    title: str | None = typer.Option(None, "--title", help="Optional short title for the claim."),
    prior: float | None = typer.Option(
        None,
        "--prior",
        help=(
            "Optional direct prior in (0, 1), rendered as a prior= kwarg on the "
            "claim() call. Usually omit it: leaf-claim priors are normally "
            "recorded separately via `gaia author register-prior`, which keeps "
            "the belief value out of the claim definition and carries a source "
            "tag. Pass --prior only when a prior belongs inline on the claim "
            "itself."
        ),
    ),
    metadata: str | None = typer.Option(
        None, "--metadata", help="Optional JSON-encoded metadata dict."
    ),
    references: str | None = typer.Option(
        None,
        "--references",
        help=(
            "Comma-separated identifiers to whitelist inside the formula "
            "sandbox (no effect when --formula / --predicate is absent). "
            "References are NOT rendered into the claim's `background=` "
            "kwarg — use --background for that. Typical shape: "
            "`--references f2_total_count,f2_dominant_count "
            "--formula 'equals(f2_total_count, Constant(395, Nat))'`."
        ),
    ),
    background: str | None = typer.Option(
        None,
        "--background",
        help=(
            "Comma-separated Knowledge identifiers passed to the rendered "
            "claim's `background=[...]` kwarg. Pre-write resolves each "
            "identifier in module scope. Independent of --references "
            "(--references is sandbox-only and not rendered into the "
            "claim call)."
        ),
    ),
    predicate: str | None = typer.Option(
        None,
        "--predicate",
        help=(
            "Alias for --formula (predicate-mode shape). Backwards-compatible "
            "spelling; prefer --formula for new authoring."
        ),
    ),
    formula: str | None = typer.Option(
        None,
        "--formula",
        help=(
            "Predicate-logic expression rendered as the ``formula=`` kwarg. "
            "Validated by the formula sandbox (whitelist: land/lor/lnot/implies/"
            "iff/equals/forall/exists + ClaimAtom + Distribution factories + "
            "Variable/Constant + Nat/Real/Bool/Probability + references). "
            "Example: `--formula 'land(equals(my_var, Constant(395, Nat)), "
            "ClaimAtom(p))'`. Mutually exclusive with --predicate."
        ),
    ),
    label: str | None = typer.Option(
        None,
        "--label",
        help=(
            "Optional Claim label. ``claim()`` does not take an engine "
            "``label=`` kwarg, so the cli emits a follow-up "
            "``<binding>.label = '<label>'`` assignment line after the "
            "rendered ``claim(...)`` call — matching the hand-authored "
            "pattern in the example packages. Requires --dsl-binding-name "
            "(no LHS means nothing to mutate)."
        ),
    ),
    export: bool = typer.Option(
        False,
        "--export/--no-export",
        help=(
            "Add --dsl-binding-name to the target module's __all__ on a "
            "successful write. Default off: exports are the package's curated "
            "public Knowledge surface."
        ),
    ),
    check: bool = typer.Option(
        True,
        "--check/--no-check",
        help="Run post-write `gaia build check` after a successful write (default on).",
    ),
    human: bool = typer.Option(
        False,
        "--human",
        help="Render the envelope in human-readable form instead of JSON.",
    ),
    interactive: bool = typer.Option(
        False,
        "--interactive",
        help="Prompt on pre-write warnings (currently a no-op; reserved for future use).",
    ),
    json_: bool = typer.Option(
        True, "--json/--no-json", help="JSON-first output (default; redundant for clarity)."
    ),
) -> None:
    r"""Append a ``claim(...)`` Knowledge declaration.

    Example:
        gaia author claim "The reaction is fast." \
            --dsl-binding-name fast_reaction --prior 0.7
    """
    del json_  # JSON-vs-human is governed by `--human`; --json is a courtesy alias.

    if label is not None and dsl_binding_name is None:
        emit_syntax_error(
            "claim",
            "--label requires --dsl-binding-name (the follow-up "
            "`<binding>.label = ...` line needs an LHS to mutate)",
            target=str(target),
            human=human,
        )
        return

    metadata_dict, metadata_error = _format_metadata_kwargs(metadata)
    if metadata_error:
        diag = Diagnostic(
            kind="prewrite.syntax",
            level="error",
            message=metadata_error,
            source="prewrite",
        )
        result = AuthorResult(
            verb="claim",
            status="error",
            code=exit_code_for_diagnostic(diag.kind),
            payload={"target": str(target)},
            diagnostics=[diag],
        )
        emit(result, human=human)
        return

    ref_list, ref_error = split_csv_idents(references)
    if ref_error:
        emit_syntax_error(
            "claim",
            f"--references rejected: {ref_error}",
            target=str(target),
            human=human,
            kind="prewrite.expr_unsafe",
        )
        return
    background_list, background_error = split_csv_idents(background)
    if background_error:
        emit_syntax_error(
            "claim",
            f"--background rejected: {background_error}",
            target=str(target),
            human=human,
            kind="prewrite.expr_unsafe",
        )
        return

    # --- formula mode: sandbox-validate the formula expression ---------- #
    if predicate is not None and formula is not None:
        emit_syntax_error(
            "claim",
            "--predicate and --formula are aliases — pass only one",
            target=str(target),
            human=human,
        )
        return
    # Canonical name is --formula; --predicate stays as a
    # backwards-compatible alias. The render path treats them
    # interchangeably.
    formula_expr = formula if formula is not None else predicate
    if formula_expr is not None:
        # Permitted identifiers: the standing whitelist plus user-named
        # references (so ``ClaimAtom(some_ref)`` resolves when
        # ``some_ref`` is on the --references list). Background
        # identifiers are also permitted in the formula sandbox: a user
        # who lists a Knowledge claim in --background may still reference
        # it inside a ClaimAtom(...) inside the formula.
        extra = frozenset(ref_list) | frozenset(background_list)
        try:
            validate_formula_expr(formula_expr, extra_names=extra)
        except FormulaSandboxError as exc:
            arg_label = "--formula" if formula is not None else "--predicate"
            emit_syntax_error(
                "claim",
                f"{arg_label} rejected by sandbox: {exc}",
                target=str(target),
                human=human,
                kind="prewrite.expr_unsafe",
            )
            return

    generated_code = _render_claim_statement(
        binding_name=dsl_binding_name,
        content=content,
        title=title,
        prior=prior,
        metadata=metadata_dict,
        background=background_list,
        predicate=formula_expr,
        label=label,
    )
    # Pre-write resolves BOTH --references (formula-sandbox identifiers)
    # and --background (rendered kwarg identifiers) against module scope.
    # Both lists must point at already-bound names; the difference is
    # the rendered output (background appears in the claim's
    # ``background=``; references stay sandbox-only).
    op_references = list(dict.fromkeys((*ref_list, *background_list)))
    target_file = normalize_file_option(file)
    # Extend the required-imports tuple with any formula-primitive names
    # (``land`` / ``equals`` / ``Constant`` / ``Nat`` / ...) referenced
    # inside the validated formula expression. The G1 writer step then
    # inserts the missing names into the rendered ``from gaia.engine.lang
    # import (...)`` line so the postwrite check can import the module
    # cleanly without an explicit ``--imports`` flag at scaffold time.
    formula_imports = extract_engine_lang_names(formula_expr) if formula_expr is not None else ()
    required_imports = ("claim", *formula_imports)
    proposed_op = ProposedAuthorOp(
        verb="claim",
        kind="reasoning",
        label=dsl_binding_name,
        references=op_references,
        generated_code=generated_code,
        required_imports=required_imports,
        target_file=target_file,
        sibling_imports=build_sibling_imports(op_references, target_file=target_file),
        export=export,
    )
    run_author_op(
        proposed_op,
        target=target,
        human=human,
        check=check,
        interactive=interactive,
    )

compose_command

compose_command(from_file: str = typer.Option(..., '--from-file', help='Path to a Python file containing exactly one @compose-decorated function.'), target: str = typer.Option('.', '--target', help='Path to the target Gaia package (default: cwd).'), check: bool = typer.Option(True, '--check/--no-check', help="Run postwrite_check against the target package after registration (default on). Registration is preserved on disk even when post-check fails — the envelope reports source='postwrite' so the caller can detect a register-but-validation-broken state."), human: bool = typer.Option(False, '--human', help='Render the envelope in human-readable form instead of JSON.'), interactive: bool = typer.Option(False, '--interactive', help='Reserved for symmetry; compose does not currently surface warnings.'), json_: bool = typer.Option(True, '--json/--no-json', help='JSON-first output (default; redundant for clarity).')) -> None

Validate + register a @compose-decorated function into pkg metadata.

Source code in gaia/cli/commands/author/compose.py
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
def compose_command(
    from_file: str = typer.Option(
        ...,
        "--from-file",
        help="Path to a Python file containing exactly one @compose-decorated function.",
    ),
    target: str = typer.Option(
        ".", "--target", help="Path to the target Gaia package (default: cwd)."
    ),
    check: bool = typer.Option(
        True,
        "--check/--no-check",
        help=(
            "Run postwrite_check against the target package after registration "
            "(default on). Registration is preserved on disk even when post-check "
            "fails — the envelope reports source='postwrite' so the caller can "
            "detect a register-but-validation-broken state."
        ),
    ),
    human: bool = typer.Option(
        False,
        "--human",
        help="Render the envelope in human-readable form instead of JSON.",
    ),
    interactive: bool = typer.Option(
        False,
        "--interactive",
        help="Reserved for symmetry; compose does not currently surface warnings.",
    ),
    json_: bool = typer.Option(
        True, "--json/--no-json", help="JSON-first output (default; redundant for clarity)."
    ),
) -> None:
    r"""Validate + register a @compose-decorated function into pkg metadata."""
    del json_, interactive
    _run_compose("compose", from_file=from_file, target=target, human=human, check=check)

composition_command

composition_command(from_file: str = typer.Option(..., '--from-file', help='Path to a Python file containing exactly one @composition-decorated function.'), target: str = typer.Option('.', '--target', help='Path to the target Gaia package (default: cwd).'), check: bool = typer.Option(True, '--check/--no-check', help="Run postwrite_check against the target package after registration (default on). Registration is preserved on disk even when post-check fails — the envelope reports source='postwrite' so the caller can detect a register-but-validation-broken state."), human: bool = typer.Option(False, '--human', help='Render the envelope in human-readable form instead of JSON.'), interactive: bool = typer.Option(False, '--interactive', help='Reserved for symmetry; composition does not currently surface warnings.'), json_: bool = typer.Option(True, '--json/--no-json', help='JSON-first output (default; redundant for clarity).')) -> None

Alias of compose; validate + register a @composition-decorated function.

Source code in gaia/cli/commands/author/compose.py
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
def composition_command(
    from_file: str = typer.Option(
        ...,
        "--from-file",
        help="Path to a Python file containing exactly one @composition-decorated function.",
    ),
    target: str = typer.Option(
        ".", "--target", help="Path to the target Gaia package (default: cwd)."
    ),
    check: bool = typer.Option(
        True,
        "--check/--no-check",
        help=(
            "Run postwrite_check against the target package after registration "
            "(default on). Registration is preserved on disk even when post-check "
            "fails — the envelope reports source='postwrite' so the caller can "
            "detect a register-but-validation-broken state."
        ),
    ),
    human: bool = typer.Option(
        False,
        "--human",
        help="Render the envelope in human-readable form instead of JSON.",
    ),
    interactive: bool = typer.Option(
        False,
        "--interactive",
        help="Reserved for symmetry; composition does not currently surface warnings.",
    ),
    json_: bool = typer.Option(
        True, "--json/--no-json", help="JSON-first output (default; redundant for clarity)."
    ),
) -> None:
    r"""Alias of ``compose``; validate + register a @composition-decorated function."""
    del json_, interactive
    _run_compose("composition", from_file=from_file, target=target, human=human, check=check)

compute_command

compute_command(label: str | None = typer.Option(None, '--label', help='Engine `label=` kwarg on the rendered compute(...) call. Distinct from --dsl-binding-name (the Python LHS).'), dsl_binding_name: str | None = typer.Option(None, '--dsl-binding-name', help='Python LHS for the rendered statement (``<name> = compute(...)``). Omit to emit a bare expression.'), conclusion_type: str = typer.Option(..., '--conclusion-type', help='Identifier of the Claim subclass the computation produces (e.g. `Probability`).'), target: str = typer.Option('.', '--target', help='Path to the target Gaia package (default: cwd).'), file: str | None = typer.Option(None, '--file', help='Relative path under src/<import_name>/ to write into. Default: `__init__.py`.'), fn: str | None = typer.Option(None, '--fn', help='Identifier of a callable producing the result (e.g. `compute_probability`).'), given: str | None = typer.Option(None, '--given', help='Comma-separated identifiers of premise Claim(s).'), rationale: str | None = typer.Option(None, '--rationale', help='Optional natural-language justification.'), metadata: str | None = typer.Option(None, '--metadata', help='Optional JSON-encoded metadata dict.'), export: bool = typer.Option(False, '--export/--no-export', help="Add --dsl-binding-name to __all__ on a successful write. Default off: exports are the package's curated public Knowledge surface."), check: bool = typer.Option(True, '--check/--no-check', help='Run post-write `gaia build check` after a successful write (default on).'), human: bool = typer.Option(False, '--human', help='Render the envelope in human-readable form instead of JSON.'), interactive: bool = typer.Option(False, '--interactive', help='Prompt on pre-write warnings (human mode only).'), json_: bool = typer.Option(True, '--json/--no-json', help='JSON-first output (default; redundant for clarity).')) -> None

Append a compute(...) deterministic-computation statement.

Example

gaia author compute --conclusion-type Probability \ --fn my_compute_prob --given my_hypothesis_x \ --dsl-binding-name my_result --label my_result

Source code in gaia/cli/commands/author/compute.py
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
def compute_command(
    label: str | None = typer.Option(
        None,
        "--label",
        help=(
            "Engine `label=` kwarg on the rendered compute(...) call. "
            "Distinct from --dsl-binding-name (the Python LHS)."
        ),
    ),
    dsl_binding_name: str | None = typer.Option(
        None,
        "--dsl-binding-name",
        help=(
            "Python LHS for the rendered statement (``<name> = "
            "compute(...)``). Omit to emit a bare expression."
        ),
    ),
    conclusion_type: str = typer.Option(
        ...,
        "--conclusion-type",
        help="Identifier of the Claim subclass the computation produces (e.g. `Probability`).",
    ),
    target: str = typer.Option(
        ".", "--target", help="Path to the target Gaia package (default: cwd)."
    ),
    file: str | None = typer.Option(
        None,
        "--file",
        help=("Relative path under src/<import_name>/ to write into. Default: `__init__.py`."),
    ),
    fn: str | None = typer.Option(
        None,
        "--fn",
        help="Identifier of a callable producing the result (e.g. `compute_probability`).",
    ),
    given: str | None = typer.Option(
        None,
        "--given",
        help="Comma-separated identifiers of premise Claim(s).",
    ),
    rationale: str | None = typer.Option(
        None, "--rationale", help="Optional natural-language justification."
    ),
    metadata: str | None = typer.Option(
        None, "--metadata", help="Optional JSON-encoded metadata dict."
    ),
    export: bool = typer.Option(
        False,
        "--export/--no-export",
        help=(
            "Add --dsl-binding-name to __all__ on a successful write. Default off: "
            "exports are the package's curated public Knowledge surface."
        ),
    ),
    check: bool = typer.Option(
        True,
        "--check/--no-check",
        help="Run post-write `gaia build check` after a successful write (default on).",
    ),
    human: bool = typer.Option(
        False, "--human", help="Render the envelope in human-readable form instead of JSON."
    ),
    interactive: bool = typer.Option(
        False, "--interactive", help="Prompt on pre-write warnings (human mode only)."
    ),
    json_: bool = typer.Option(
        True, "--json/--no-json", help="JSON-first output (default; redundant for clarity)."
    ),
) -> None:
    r"""Append a ``compute(...)`` deterministic-computation statement.

    Example:
        gaia author compute --conclusion-type Probability \
            --fn my_compute_prob --given my_hypothesis_x \
            --dsl-binding-name my_result --label my_result
    """
    del json_

    metadata_dict, metadata_error = parse_metadata(metadata)
    if metadata_error:
        emit_syntax_error("compute", metadata_error, target=str(target), human=human)
        return

    given_list, given_error = split_csv_idents(given)
    if given_error:
        emit_syntax_error(
            "compute",
            f"--given rejected: {given_error}",
            target=str(target),
            human=human,
            kind="prewrite.expr_unsafe",
        )
        return

    # --conclusion-type and --fn both splice into the rendered
    # compute() call. The references list only catches malformed
    # values incidentally (string-membership test against module
    # symbols); add explicit identifier-shape gates here.
    references: list[str] = []
    try:
        _, rendered_conclusion = parse_literal_or_identifier(
            conclusion_type,
            references_sink=references,
        )
    except PrewriteUnsafeError as exc:
        emit_syntax_error(
            "compute",
            f"--conclusion-type rejected: {exc}",
            target=str(target),
            human=human,
            kind="prewrite.expr_unsafe",
        )
        return
    rendered_fn: str | None = None
    if fn is not None:
        try:
            _, rendered_fn = parse_literal_or_identifier(fn, references_sink=references)
        except PrewriteUnsafeError as exc:
            emit_syntax_error(
                "compute",
                f"--fn rejected: {exc}",
                target=str(target),
                human=human,
                kind="prewrite.expr_unsafe",
            )
            return

    generated_code = _render_compute_statement(
        binding_name=dsl_binding_name,
        engine_label=label,
        conclusion_type=rendered_conclusion,
        fn=rendered_fn,
        given=given_list,
        rationale=rationale,
        metadata=metadata_dict,
    )
    references = [*references, *given_list]
    target_file = normalize_file_option(file)
    proposed_op = ProposedAuthorOp(
        verb="compute",
        kind="reasoning",
        label=dsl_binding_name,
        references=references,
        generated_code=generated_code,
        required_imports=("compute",),
        target_file=target_file,
        sibling_imports=build_sibling_imports(references, target_file=target_file),
        export=export,
    )
    run_author_op(
        proposed_op,
        target=target,
        human=human,
        check=check,
        interactive=interactive,
    )

contradict_command

contradict_command(label: str | None = typer.Option(None, '--label', help='Engine `label=` kwarg on the rendered contradict(...) call. Distinct from --dsl-binding-name (the Python LHS).'), dsl_binding_name: str | None = typer.Option(None, '--dsl-binding-name', help='Python LHS for the rendered statement (``<name> = contradict(...)``). Omit to emit a bare expression.'), a: str = typer.Option(..., '--a', help='First Claim reference: a local Claim identifier or a pulled-claim QID (lkm:<package>::<label>).'), b: str = typer.Option(..., '--b', help='Second Claim reference: a local Claim identifier or a pulled-claim QID (lkm:<package>::<label>).'), target: str = typer.Option('.', '--target', help='Path to the target Gaia package (default: cwd).'), file: str | None = typer.Option(None, '--file', help='Relative path under src/<import_name>/ to write into. Default: `__init__.py`.'), rationale: str | None = typer.Option(None, '--rationale', help='Optional natural-language justification.'), background: str | None = typer.Option(None, '--background', help='Comma-separated identifiers passed as the contradict() background kwarg.'), metadata: str | None = typer.Option(None, '--metadata', help='Optional JSON-encoded metadata dict.'), export: bool = typer.Option(False, '--export/--no-export', help='Add the returned contradiction helper to __all__ as a public relation export. Default off: exports are curated explicitly.'), check: bool = typer.Option(True, '--check/--no-check', help='Run post-write `gaia build check` after a successful write (default on).'), human: bool = typer.Option(False, '--human', help='Render the envelope in human-readable form instead of JSON.'), interactive: bool = typer.Option(False, '--interactive', help='Prompt on pre-write warnings (human mode only).'), json_: bool = typer.Option(True, '--json/--no-json', help='JSON-first output (default; redundant for clarity).')) -> None

Append a contradict(a, b, ...) structural relation.

Example

gaia author contradict --a my_claim_a --b my_claim_b \ --dsl-binding-name my_contradiction --label my_contradiction

Source code in gaia/cli/commands/author/contradict.py
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
def contradict_command(
    label: str | None = typer.Option(
        None,
        "--label",
        help=(
            "Engine `label=` kwarg on the rendered contradict(...) call. "
            "Distinct from --dsl-binding-name (the Python LHS)."
        ),
    ),
    dsl_binding_name: str | None = typer.Option(
        None,
        "--dsl-binding-name",
        help=(
            "Python LHS for the rendered statement (``<name> = "
            "contradict(...)``). Omit to emit a bare expression."
        ),
    ),
    a: str = typer.Option(
        ...,
        "--a",
        help=(
            "First Claim reference: a local Claim identifier or a pulled-claim "
            "QID (lkm:<package>::<label>)."
        ),
    ),
    b: str = typer.Option(
        ...,
        "--b",
        help=(
            "Second Claim reference: a local Claim identifier or a pulled-claim "
            "QID (lkm:<package>::<label>)."
        ),
    ),
    target: str = typer.Option(
        ".", "--target", help="Path to the target Gaia package (default: cwd)."
    ),
    file: str | None = typer.Option(
        None,
        "--file",
        help=("Relative path under src/<import_name>/ to write into. Default: `__init__.py`."),
    ),
    rationale: str | None = typer.Option(
        None, "--rationale", help="Optional natural-language justification."
    ),
    background: str | None = typer.Option(
        None,
        "--background",
        help="Comma-separated identifiers passed as the contradict() background kwarg.",
    ),
    metadata: str | None = typer.Option(
        None, "--metadata", help="Optional JSON-encoded metadata dict."
    ),
    export: bool = typer.Option(
        False,
        "--export/--no-export",
        help=(
            "Add the returned contradiction helper to __all__ as a public "
            "relation export. Default off: exports are curated explicitly."
        ),
    ),
    check: bool = typer.Option(
        True,
        "--check/--no-check",
        help="Run post-write `gaia build check` after a successful write (default on).",
    ),
    human: bool = typer.Option(
        False, "--human", help="Render the envelope in human-readable form instead of JSON."
    ),
    interactive: bool = typer.Option(
        False, "--interactive", help="Prompt on pre-write warnings (human mode only)."
    ),
    json_: bool = typer.Option(
        True, "--json/--no-json", help="JSON-first output (default; redundant for clarity)."
    ),
) -> None:
    r"""Append a ``contradict(a, b, ...)`` structural relation.

    Example:
        gaia author contradict --a my_claim_a --b my_claim_b \
            --dsl-binding-name my_contradiction --label my_contradiction
    """
    del json_

    metadata_dict, metadata_error = parse_metadata(metadata)
    if metadata_error:
        emit_syntax_error("contradict", metadata_error, target=str(target), human=human)
        return

    # ``--a`` / ``--b`` each accept a single reference token — a local Claim
    # identifier or a pulled-claim QID. Routing each through ``split_csv_refs``
    # resolves a QID to an aliased import + alias reference exactly the way
    # ``derive --given`` does, while a bare identifier passes through unchanged.
    a_refs, a_error = split_csv_refs(a)
    if a_error or len(a_refs.rendered) != 1:
        emit_syntax_error(
            "contradict",
            f"--a rejected: {a_error or 'must be a single Claim reference'}",
            target=str(target),
            human=human,
            kind="prewrite.expr_unsafe",
        )
        return
    b_refs, b_error = split_csv_refs(b)
    if b_error or len(b_refs.rendered) != 1:
        emit_syntax_error(
            "contradict",
            f"--b rejected: {b_error or 'must be a single Claim reference'}",
            target=str(target),
            human=human,
            kind="prewrite.expr_unsafe",
        )
        return

    background_refs, background_error = split_csv_refs(background)
    if background_error:
        emit_syntax_error(
            "contradict",
            f"--background rejected: {background_error}",
            target=str(target),
            human=human,
            kind="prewrite.expr_unsafe",
        )
        return
    background_list = background_refs.rendered
    generated_code = _render_contradict_statement(
        binding_name=dsl_binding_name,
        engine_label=label,
        a=a_refs.rendered[0],
        b=b_refs.rendered[0],
        rationale=rationale,
        metadata=metadata_dict,
        background=background_list,
    )
    target_file = normalize_file_option(file)
    references = [*a_refs.local, *b_refs.local, *background_refs.local]
    foreign_imports = tuple(
        (fi.module, fi.symbol, fi.alias)
        for fi in (
            *a_refs.foreign_imports,
            *b_refs.foreign_imports,
            *background_refs.foreign_imports,
        )
    )
    proposed_op = ProposedAuthorOp(
        verb="contradict",
        kind="reasoning",
        label=dsl_binding_name,
        references=references,
        generated_code=generated_code,
        required_imports=("contradict",),
        target_file=target_file,
        sibling_imports=build_sibling_imports(references, target_file=target_file),
        foreign_imports=foreign_imports,
        export=export,
    )
    run_author_op(
        proposed_op,
        target=target,
        human=human,
        check=check,
        interactive=interactive,
    )

decompose_command

decompose_command(label: str | None = typer.Option(None, '--label', help='Engine `label=` kwarg on the rendered decompose(...) call. Distinct from --dsl-binding-name (the Python LHS).'), dsl_binding_name: str | None = typer.Option(None, '--dsl-binding-name', help='Python LHS for the rendered statement (``<name> = decompose(...)``). Omit to emit a bare expression.'), whole: str = typer.Option(..., '--whole', help='Identifier of the composite Claim being decomposed.'), parts: str = typer.Option(..., '--parts', help='Comma-separated identifiers of atomic Claim parts.'), target: str = typer.Option('.', '--target', help='Path to the target Gaia package (default: cwd).'), file: str | None = typer.Option(None, '--file', help='Relative path under src/<import_name>/ to write into. Default: `__init__.py`.'), formula_template: str | None = typer.Option(None, '--formula-template', help='Pre-baked formula shape over --parts: atom (single-part), and (conjunction), or (disjunction).'), formula_expr: str | None = typer.Option(None, '--formula-expr', help='Raw Python expression for the formula, evaluated at author time with land/lor/lnot/iff/implies/ClaimAtom plus part names in scope.'), rationale: str | None = typer.Option(None, '--rationale', help='Optional natural-language justification.'), metadata: str | None = typer.Option(None, '--metadata', help='Optional JSON-encoded metadata dict.'), export: bool = typer.Option(False, '--export/--no-export', help='Add --dsl-binding-name to __all__ on a successful write when the decomposed Knowledge is part of the curated public surface. Default off.'), check: bool = typer.Option(True, '--check/--no-check', help='Run post-write `gaia build check` after a successful write (default on).'), human: bool = typer.Option(False, '--human', help='Render the envelope in human-readable form instead of JSON.'), interactive: bool = typer.Option(False, '--interactive', help='Prompt on pre-write warnings (human mode only).'), json_: bool = typer.Option(True, '--json/--no-json', help='JSON-first output (default; redundant for clarity).')) -> None

Append a decompose(whole, parts=..., formula=...) decomposition.

Example

gaia author decompose --whole my_composite \ --parts my_atom_a,my_atom_b \ --formula-template and \ --dsl-binding-name my_decomposition

Source code in gaia/cli/commands/author/decompose.py
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
def decompose_command(
    label: str | None = typer.Option(
        None,
        "--label",
        help=(
            "Engine `label=` kwarg on the rendered decompose(...) call. "
            "Distinct from --dsl-binding-name (the Python LHS)."
        ),
    ),
    dsl_binding_name: str | None = typer.Option(
        None,
        "--dsl-binding-name",
        help=(
            "Python LHS for the rendered statement (``<name> = "
            "decompose(...)``). Omit to emit a bare expression."
        ),
    ),
    whole: str = typer.Option(
        ..., "--whole", help="Identifier of the composite Claim being decomposed."
    ),
    parts: str = typer.Option(
        ..., "--parts", help="Comma-separated identifiers of atomic Claim parts."
    ),
    target: str = typer.Option(
        ".", "--target", help="Path to the target Gaia package (default: cwd)."
    ),
    file: str | None = typer.Option(
        None,
        "--file",
        help=("Relative path under src/<import_name>/ to write into. Default: `__init__.py`."),
    ),
    formula_template: str | None = typer.Option(
        None,
        "--formula-template",
        help=(
            "Pre-baked formula shape over --parts: atom (single-part), "
            "and (conjunction), or (disjunction)."
        ),
    ),
    formula_expr: str | None = typer.Option(
        None,
        "--formula-expr",
        help=(
            "Raw Python expression for the formula, evaluated at author time with "
            "land/lor/lnot/iff/implies/ClaimAtom plus part names in scope."
        ),
    ),
    rationale: str | None = typer.Option(
        None, "--rationale", help="Optional natural-language justification."
    ),
    metadata: str | None = typer.Option(
        None, "--metadata", help="Optional JSON-encoded metadata dict."
    ),
    export: bool = typer.Option(
        False,
        "--export/--no-export",
        help=(
            "Add --dsl-binding-name to __all__ on a successful write "
            "when the decomposed Knowledge is part of the curated public surface. "
            "Default off."
        ),
    ),
    check: bool = typer.Option(
        True,
        "--check/--no-check",
        help="Run post-write `gaia build check` after a successful write (default on).",
    ),
    human: bool = typer.Option(
        False, "--human", help="Render the envelope in human-readable form instead of JSON."
    ),
    interactive: bool = typer.Option(
        False, "--interactive", help="Prompt on pre-write warnings (human mode only)."
    ),
    json_: bool = typer.Option(
        True, "--json/--no-json", help="JSON-first output (default; redundant for clarity)."
    ),
) -> None:
    r"""Append a ``decompose(whole, parts=..., formula=...)`` decomposition.

    Example:
        gaia author decompose --whole my_composite \
            --parts my_atom_a,my_atom_b \
            --formula-template and \
            --dsl-binding-name my_decomposition
    """
    del json_

    if formula_template is None and formula_expr is None:
        emit_syntax_error(
            "decompose",
            "decompose requires exactly one of --formula-template / --formula-expr",
            target=str(target),
            human=human,
        )
        return
    if formula_template is not None and formula_expr is not None:
        emit_syntax_error(
            "decompose",
            "--formula-template and --formula-expr are mutually exclusive",
            target=str(target),
            human=human,
        )
        return

    metadata_dict, metadata_error = parse_metadata(metadata)
    if metadata_error:
        emit_syntax_error("decompose", metadata_error, target=str(target), human=human)
        return

    # --- Axis 1 — identifier-shape gates on --whole / --parts -------- #
    try:
        parse_literal_or_identifier(whole)
    except PrewriteUnsafeError as exc:
        emit_syntax_error(
            "decompose",
            f"--whole rejected: {exc}",
            target=str(target),
            human=human,
            kind="prewrite.expr_unsafe",
        )
        return
    part_list, part_error = split_csv_idents(parts)
    if part_error:
        emit_syntax_error(
            "decompose",
            f"--parts rejected: {part_error}",
            target=str(target),
            human=human,
            kind="prewrite.expr_unsafe",
        )
        return
    if not part_list:
        emit_syntax_error(
            "decompose",
            "--parts must list at least one identifier",
            target=str(target),
            human=human,
        )
        return

    if formula_template is not None:
        formula_src, template_error = _render_formula_from_template(formula_template, part_list)
        if template_error:
            emit_syntax_error("decompose", template_error, target=str(target), human=human)
            return
        assert formula_src is not None
    else:
        assert formula_expr is not None
        # Validate the raw expression against the formula sandbox before
        # letting pre-write parse the rendered statement. Identifier
        # whitelist = standing primitives + each ``--parts`` entry (so
        # ``ClaimAtom(atom_a)`` resolves).
        extra = frozenset({whole, *part_list})
        try:
            validate_formula_expr(formula_expr, extra_names=extra)
        except FormulaSandboxError as exc:
            emit_syntax_error(
                "decompose",
                f"--formula-expr rejected by sandbox: {exc}",
                target=str(target),
                human=human,
                kind="prewrite.expr_unsafe",
            )
            return
        formula_src = formula_expr

    generated_code = _render_decompose_statement(
        binding_name=dsl_binding_name,
        engine_label=label,
        whole=whole,
        parts=part_list,
        formula_src=formula_src,
        rationale=rationale,
        metadata=metadata_dict,
    )
    references = [whole, *part_list]
    target_file = normalize_file_option(file)
    # Track which engine-lang primitives the rendered ``formula=...``
    # expression actually uses; G1 then injects only the needed names.
    # ``ClaimAtom`` is always needed in the template path; user-supplied
    # ``--formula-expr`` may reference any subset of the whitelist.
    primitive_imports = extract_engine_lang_names(formula_src)
    required_imports = ("decompose", *primitive_imports)
    proposed_op = ProposedAuthorOp(
        verb="decompose",
        kind="reasoning",
        label=dsl_binding_name,
        references=references,
        generated_code=generated_code,
        required_imports=required_imports,
        target_file=target_file,
        sibling_imports=build_sibling_imports(references, target_file=target_file),
        export=export,
    )
    run_author_op(
        proposed_op,
        target=target,
        human=human,
        check=check,
        interactive=interactive,
    )

depends_on_command

depends_on_command(label: str | None = typer.Option(None, '--label', help='Engine `label=` kwarg on the rendered depends_on(...) call. Distinct from --dsl-binding-name (the Python LHS).'), dsl_binding_name: str | None = typer.Option(None, '--dsl-binding-name', help='Python LHS for the rendered statement (``<name> = depends_on(...)``). Omit to emit a bare expression.'), conclusion: str = typer.Option(..., '--conclusion', help='Identifier of the dependent Claim.'), given: str = typer.Option(..., '--given', help='Comma-separated dependency references: local Claim identifiers and/or pulled-claim QIDs (lkm:<package>::<label>).'), target: str = typer.Option('.', '--target', help='Path to the target Gaia package (default: cwd).'), file: str | None = typer.Option(None, '--file', help='Relative path under src/<import_name>/ to write into. Default: `__init__.py`.'), rationale: str | None = typer.Option(None, '--rationale', help='Optional natural-language justification.'), background: str | None = typer.Option(None, '--background', help='Comma-separated background Knowledge identifiers.'), metadata: str | None = typer.Option(None, '--metadata', help='Optional JSON-encoded metadata dict.'), export: bool = typer.Option(False, '--export/--no-export', help="Add --dsl-binding-name to __all__ on a successful write (default off for depends_on: scaffold actions are structural, not part of the package's public Knowledge surface)."), check: bool = typer.Option(True, '--check/--no-check', help='Run post-write `gaia build check` after a successful write (default on).'), human: bool = typer.Option(False, '--human', help='Render the envelope in human-readable form instead of JSON.'), interactive: bool = typer.Option(False, '--interactive', help='Prompt on pre-write warnings (human mode only).'), json_: bool = typer.Option(True, '--json/--no-json', help='JSON-first output (default; redundant for clarity).')) -> None

Append a depends_on(...) scaffold dependency.

Example

gaia author depends-on --conclusion my_big_claim \ --given my_small_claim_a,my_small_claim_b \ --dsl-binding-name my_dependency

Source code in gaia/cli/commands/author/depends_on.py
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
def depends_on_command(
    label: str | None = typer.Option(
        None,
        "--label",
        help=(
            "Engine `label=` kwarg on the rendered depends_on(...) call. "
            "Distinct from --dsl-binding-name (the Python LHS)."
        ),
    ),
    dsl_binding_name: str | None = typer.Option(
        None,
        "--dsl-binding-name",
        help=(
            "Python LHS for the rendered statement (``<name> = "
            "depends_on(...)``). Omit to emit a bare expression."
        ),
    ),
    conclusion: str = typer.Option(..., "--conclusion", help="Identifier of the dependent Claim."),
    given: str = typer.Option(
        ...,
        "--given",
        help=(
            "Comma-separated dependency references: local Claim identifiers "
            "and/or pulled-claim QIDs (lkm:<package>::<label>)."
        ),
    ),
    target: str = typer.Option(
        ".", "--target", help="Path to the target Gaia package (default: cwd)."
    ),
    file: str | None = typer.Option(
        None,
        "--file",
        help=("Relative path under src/<import_name>/ to write into. Default: `__init__.py`."),
    ),
    rationale: str | None = typer.Option(
        None, "--rationale", help="Optional natural-language justification."
    ),
    background: str | None = typer.Option(
        None,
        "--background",
        help="Comma-separated background Knowledge identifiers.",
    ),
    metadata: str | None = typer.Option(
        None, "--metadata", help="Optional JSON-encoded metadata dict."
    ),
    export: bool = typer.Option(
        False,
        "--export/--no-export",
        help=(
            "Add --dsl-binding-name to __all__ on a successful write "
            "(default off for depends_on: scaffold actions are structural, "
            "not part of the package's public Knowledge surface)."
        ),
    ),
    check: bool = typer.Option(
        True,
        "--check/--no-check",
        help="Run post-write `gaia build check` after a successful write (default on).",
    ),
    human: bool = typer.Option(
        False, "--human", help="Render the envelope in human-readable form instead of JSON."
    ),
    interactive: bool = typer.Option(
        False, "--interactive", help="Prompt on pre-write warnings (human mode only)."
    ),
    json_: bool = typer.Option(
        True, "--json/--no-json", help="JSON-first output (default; redundant for clarity)."
    ),
) -> None:
    r"""Append a ``depends_on(...)`` scaffold dependency.

    Example:
        gaia author depends-on --conclusion my_big_claim \
            --given my_small_claim_a,my_small_claim_b \
            --dsl-binding-name my_dependency
    """
    del json_

    metadata_dict, metadata_error = parse_metadata(metadata)
    if metadata_error:
        emit_syntax_error("depends_on", metadata_error, target=str(target), human=human)
        return

    if not validate_identifier_flag(
        conclusion, verb="depends_on", flag="--conclusion", target=str(target), human=human
    ):
        return
    given_refs, given_error = split_csv_refs(given)
    if given_error:
        emit_syntax_error(
            "depends_on",
            f"--given rejected: {given_error}",
            target=str(target),
            human=human,
            kind="prewrite.expr_unsafe",
        )
        return
    given_list = given_refs.rendered
    background_refs, background_error = split_csv_refs(background)
    if background_error:
        emit_syntax_error(
            "depends_on",
            f"--background rejected: {background_error}",
            target=str(target),
            human=human,
            kind="prewrite.expr_unsafe",
        )
        return
    background_list = background_refs.rendered
    if not given_list:
        emit_syntax_error(
            "depends_on",
            "--given must list at least one dependency identifier",
            target=str(target),
            human=human,
        )
        return

    generated_code = _render_depends_on_statement(
        binding_name=dsl_binding_name,
        engine_label=label,
        conclusion=conclusion,
        given=given_list,
        rationale=rationale,
        metadata=metadata_dict,
        background=background_list,
    )
    references = [conclusion, *given_refs.local, *background_refs.local]
    target_file = normalize_file_option(file)
    foreign_imports = tuple(
        (fi.module, fi.symbol, fi.alias)
        for fi in (*given_refs.foreign_imports, *background_refs.foreign_imports)
    )
    proposed_op = ProposedAuthorOp(
        verb="depends_on",
        kind="scaffold",
        label=dsl_binding_name,
        references=references,
        generated_code=generated_code,
        required_imports=("depends_on",),
        target_file=target_file,
        sibling_imports=build_sibling_imports(references, target_file=target_file),
        foreign_imports=foreign_imports,
        export=export,
    )
    run_author_op(
        proposed_op,
        target=target,
        human=human,
        check=check,
        interactive=interactive,
    )

derive_command

derive_command(conclusion: str | None = typer.Option(None, '--conclusion', help='Identifier of the conclusion Claim (must already be declared).'), conclusion_content: str | None = typer.Option(None, '--conclusion-content', help='Prose for an auto-generated conclusion Claim. Mutually exclusive with --conclusion and --conclusion-prose. Cli derives a snake-case slug for the label (override via --conclusion-label).'), conclusion_label: str | None = typer.Option(None, '--conclusion-label', help='Optional explicit label for the auto-generated conclusion Claim (only meaningful with --conclusion-content).'), conclusion_prose: str | None = typer.Option(None, '--conclusion-prose', help="Inline prose passed to the engine's ``derive(conclusion: Claim | str, ...)`` polymorphism. Emits ``derive('<prose>', ...)`` directly with no named binding. Mutually exclusive with --conclusion and --conclusion-content."), given: str = typer.Option(..., '--given', help='Comma-separated premise references the conclusion is derived from: local Claim identifiers and/or pulled-claim QIDs (lkm:<package>::<label>).'), label: str | None = typer.Option(None, '--label', help='Engine `label=` kwarg passed through to the rendered derive(...) call. Distinct from --dsl-binding-name (the Python LHS). Omit both to render derive(...) without a label= kwarg and no LHS.'), dsl_binding_name: str | None = typer.Option(None, '--dsl-binding-name', help='Python module-scope identifier the rendered statement binds to (``<name> = derive(...)``). Omit to emit a bare expression statement (no LHS); the derive() result is then only reachable via transitive Claim references in the IR.'), target: str = typer.Option('.', '--target', help='Path to the target Gaia package (default: cwd).'), file: str | None = typer.Option(None, '--file', help='Relative path under src/<import_name>/ to write into. Default: `__init__.py`.'), rationale: str | None = typer.Option(None, '--rationale', help='Optional natural-language justification of the derivation.'), metadata: str | None = typer.Option(None, '--metadata', help='Optional JSON-encoded metadata dict.'), background: str | None = typer.Option(None, '--background', help='Comma-separated identifiers passed as the derive() background kwarg.'), export: bool = typer.Option(False, '--export/--no-export', help="Append --dsl-binding-name to the target module's __all__ on a successful write. Default off: exports are the package's curated public Knowledge surface."), check: bool = typer.Option(True, '--check/--no-check', help='Run post-write `gaia build check` after a successful write (default on).'), human: bool = typer.Option(False, '--human', help='Render the envelope in human-readable form instead of JSON.'), interactive: bool = typer.Option(False, '--interactive', help='Prompt on pre-write warnings (human mode only).'), json_: bool = typer.Option(True, '--json/--no-json', help='JSON-first output (default; redundant for clarity).')) -> None

Append a derive(conclusion, given=[...]) support relation.

Example

gaia author derive --conclusion-content "B follows from A." \ --given my_claim_a \ --dsl-binding-name my_derivation \ --label my_derivation_path

Source code in gaia/cli/commands/author/derive.py
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
def derive_command(
    conclusion: str | None = typer.Option(
        None,
        "--conclusion",
        help="Identifier of the conclusion Claim (must already be declared).",
    ),
    conclusion_content: str | None = typer.Option(
        None,
        "--conclusion-content",
        help=(
            "Prose for an auto-generated conclusion Claim. Mutually exclusive with "
            "--conclusion and --conclusion-prose. Cli derives a snake-case slug for "
            "the label (override via --conclusion-label)."
        ),
    ),
    conclusion_label: str | None = typer.Option(
        None,
        "--conclusion-label",
        help=(
            "Optional explicit label for the auto-generated conclusion Claim "
            "(only meaningful with --conclusion-content)."
        ),
    ),
    conclusion_prose: str | None = typer.Option(
        None,
        "--conclusion-prose",
        help=(
            "Inline prose passed to the engine's ``derive(conclusion: Claim | str, "
            "...)`` polymorphism. Emits ``derive('<prose>', ...)`` directly with no "
            "named binding. Mutually exclusive with --conclusion and "
            "--conclusion-content."
        ),
    ),
    given: str = typer.Option(
        ...,
        "--given",
        help=(
            "Comma-separated premise references the conclusion is derived from: "
            "local Claim identifiers and/or pulled-claim QIDs "
            "(lkm:<package>::<label>)."
        ),
    ),
    label: str | None = typer.Option(
        None,
        "--label",
        help=(
            "Engine `label=` kwarg passed through to the rendered derive(...) "
            "call. Distinct from --dsl-binding-name (the Python LHS). Omit "
            "both to render derive(...) without a label= kwarg and no LHS."
        ),
    ),
    dsl_binding_name: str | None = typer.Option(
        None,
        "--dsl-binding-name",
        help=(
            "Python module-scope identifier the rendered statement binds to "
            "(``<name> = derive(...)``). Omit to emit a bare expression "
            "statement (no LHS); the derive() result is then only reachable "
            "via transitive Claim references in the IR."
        ),
    ),
    target: str = typer.Option(
        ".", "--target", help="Path to the target Gaia package (default: cwd)."
    ),
    file: str | None = typer.Option(
        None,
        "--file",
        help=("Relative path under src/<import_name>/ to write into. Default: `__init__.py`."),
    ),
    rationale: str | None = typer.Option(
        None, "--rationale", help="Optional natural-language justification of the derivation."
    ),
    metadata: str | None = typer.Option(
        None, "--metadata", help="Optional JSON-encoded metadata dict."
    ),
    background: str | None = typer.Option(
        None,
        "--background",
        help="Comma-separated identifiers passed as the derive() background kwarg.",
    ),
    export: bool = typer.Option(
        False,
        "--export/--no-export",
        help=(
            "Append --dsl-binding-name to the target module's __all__ on a "
            "successful write. Default off: exports are the package's curated "
            "public Knowledge surface."
        ),
    ),
    check: bool = typer.Option(
        True,
        "--check/--no-check",
        help="Run post-write `gaia build check` after a successful write (default on).",
    ),
    human: bool = typer.Option(
        False,
        "--human",
        help="Render the envelope in human-readable form instead of JSON.",
    ),
    interactive: bool = typer.Option(
        False,
        "--interactive",
        help="Prompt on pre-write warnings (human mode only).",
    ),
    json_: bool = typer.Option(
        True, "--json/--no-json", help="JSON-first output (default; redundant for clarity)."
    ),
) -> None:
    r"""Append a ``derive(conclusion, given=[...])`` support relation.

    Example:
        gaia author derive --conclusion-content "B follows from A." \
            --given my_claim_a \
            --dsl-binding-name my_derivation \
            --label my_derivation_path
    """
    del json_

    # --- mutual-exclusion check on conclusion-mode ----------------------- #
    conclusion_modes = [conclusion, conclusion_content, conclusion_prose]
    modes_set = sum(1 for value in conclusion_modes if value is not None)
    if modes_set == 0:
        emit_syntax_error(
            "derive",
            (
                "derive requires exactly one of --conclusion / --conclusion-content / "
                "--conclusion-prose"
            ),
            target=str(target),
            human=human,
        )
        return
    if modes_set > 1:
        emit_syntax_error(
            "derive",
            (
                "--conclusion, --conclusion-content, and --conclusion-prose are "
                "mutually exclusive — pick exactly one"
            ),
            target=str(target),
            human=human,
        )
        return
    if conclusion_label is not None and conclusion_content is None:
        emit_syntax_error(
            "derive",
            "--conclusion-label only applies with --conclusion-content",
            target=str(target),
            human=human,
        )
        return

    metadata_dict, metadata_error = _parse_metadata(metadata)
    if metadata_error:
        diag = Diagnostic(
            kind="prewrite.syntax",
            level="error",
            message=metadata_error,
            source="prewrite",
        )
        result = AuthorResult(
            verb="derive",
            status="error",
            code=exit_code_for_diagnostic(diag.kind),
            payload={"target": str(target)},
            diagnostics=[diag],
        )
        emit(result, human=human)
        return

    given_refs, given_error = split_csv_refs(given)
    if given_error:
        emit_syntax_error(
            "derive",
            f"--given rejected: {given_error}",
            target=str(target),
            human=human,
            kind="prewrite.expr_unsafe",
        )
        return
    given_list = given_refs.rendered
    background_refs, background_error = split_csv_refs(background)
    if background_error:
        emit_syntax_error(
            "derive",
            f"--background rejected: {background_error}",
            target=str(target),
            human=human,
            kind="prewrite.expr_unsafe",
        )
        return
    background_list = background_refs.rendered
    if not given_list:
        diag = Diagnostic(
            kind="prewrite.syntax",
            level="error",
            message="--given must list at least one premise identifier",
            source="prewrite",
        )
        result = AuthorResult(
            verb="derive",
            status="error",
            code=exit_code_for_diagnostic(diag.kind),
            payload={"target": str(target)},
            diagnostics=[diag],
        )
        emit(result, human=human)
        return

    # --- resolve conclusion mode ---------------------------------------- #
    # ``conclusion_expr`` is the Python source spelling that ends up at
    # the call site for the conclusion arg. ``references`` is the list
    # of identifier names that must resolve in module scope — the
    # inline-prose shape contributes no reference at all (the prose is
    # a bare string literal at the call site).
    prepended: tuple[tuple[str, str], ...] = ()
    references: list[str]
    conclusion_kind: str
    if conclusion_content is not None:
        # Auto-mint: derive a slug, prepend a ``slug = claim(prose)``
        # statement, use the slug as ``conclusion``. The slug must avoid
        # the caller-supplied identifiers; the prewrite (c) collision
        # check also runs against module symbols, so a slug collision
        # against an existing binding surfaces as the standard
        # ``prewrite.collision`` error.
        if conclusion_label is not None:
            auto_label = conclusion_label
        else:
            reserved = {*given_list, *background_list}
            if dsl_binding_name is not None:
                reserved.add(dsl_binding_name)
            auto_label = slugify_label(conclusion_content, existing=reserved)
        prepended = ((auto_label, build_auto_claim_statement(auto_label, conclusion_content)),)
        conclusion_expr = auto_label
        references = [auto_label, *given_refs.local, *background_refs.local]
        conclusion_kind = "auto_mint"
    elif conclusion_prose is not None:
        # Inline-prose: pass the prose through as a bare string
        # literal. The engine's ``derive(conclusion: Claim | str, ...)``
        # polymorphism wraps it into an anonymous Claim at runtime; no
        # auto-claim binding is prepended. References list omits the
        # prose entirely.
        conclusion_expr = repr(conclusion_prose)
        references = [*given_refs.local, *background_refs.local]
        conclusion_kind = "inline_prose"
    else:
        assert conclusion is not None  # mutex check above
        conclusion_expr = conclusion
        references = [conclusion, *given_refs.local, *background_refs.local]
        conclusion_kind = "qid"

    generated_code = _render_derive_statement(
        binding_name=dsl_binding_name,
        engine_label=label,
        conclusion_expr=conclusion_expr,
        given=given_list,
        rationale=rationale,
        metadata=metadata_dict,
        background=background_list,
    )
    target_file = normalize_file_option(file)
    foreign_imports = tuple(
        (fi.module, fi.symbol, fi.alias)
        for fi in (*given_refs.foreign_imports, *background_refs.foreign_imports)
    )
    proposed_op = ProposedAuthorOp(
        verb="derive",
        kind="reasoning",
        label=dsl_binding_name,
        references=references,
        generated_code=generated_code,
        required_imports=("derive",),
        target_file=target_file,
        sibling_imports=build_sibling_imports(references, target_file=target_file),
        foreign_imports=foreign_imports,
        prepended_statements=prepended,
        extra_payload={"conclusion_kind": conclusion_kind},
        export=export,
    )
    run_author_op(
        proposed_op,
        target=target,
        human=human,
        check=check,
        interactive=interactive,
    )

equal_command

equal_command(label: str | None = typer.Option(None, '--label', help='Engine `label=` kwarg on the rendered equal(...) call. Distinct from --dsl-binding-name (the Python LHS).'), dsl_binding_name: str | None = typer.Option(None, '--dsl-binding-name', help='Python LHS for the rendered statement (``<name> = equal(...)``). Omit to emit a bare expression statement.'), a: str = typer.Option(..., '--a', help='Identifier of the first Claim.'), b: str = typer.Option(..., '--b', help='Identifier of the second Claim.'), target: str = typer.Option('.', '--target', help='Path to the target Gaia package (default: cwd).'), file: str | None = typer.Option(None, '--file', help='Relative path under src/<import_name>/ to write into. Default: `__init__.py`.'), rationale: str | None = typer.Option(None, '--rationale', help='Optional natural-language justification.'), background: str | None = typer.Option(None, '--background', help='Comma-separated identifiers passed as the equal() background kwarg.'), metadata: str | None = typer.Option(None, '--metadata', help='Optional JSON-encoded metadata dict.'), export: bool = typer.Option(False, '--export/--no-export', help='Add the returned equivalence helper to __all__ as a public relation export. Default off: exports are curated explicitly.'), check: bool = typer.Option(True, '--check/--no-check', help='Run post-write `gaia build check` after a successful write (default on).'), human: bool = typer.Option(False, '--human', help='Render the envelope in human-readable form instead of JSON.'), interactive: bool = typer.Option(False, '--interactive', help='Prompt on pre-write warnings (currently a no-op; reserved for future use).'), json_: bool = typer.Option(True, '--json/--no-json', help='JSON-first output (default; redundant for clarity).')) -> None

Append an equal(a, b, ...) structural relation.

Example

gaia author equal --a my_claim_a --b my_claim_b \ --dsl-binding-name my_equivalence --label my_equivalence

Source code in gaia/cli/commands/author/equal.py
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
def equal_command(
    label: str | None = typer.Option(
        None,
        "--label",
        help=(
            "Engine `label=` kwarg on the rendered equal(...) call. Distinct "
            "from --dsl-binding-name (the Python LHS)."
        ),
    ),
    dsl_binding_name: str | None = typer.Option(
        None,
        "--dsl-binding-name",
        help=(
            "Python LHS for the rendered statement (``<name> = equal(...)``). "
            "Omit to emit a bare expression statement."
        ),
    ),
    a: str = typer.Option(..., "--a", help="Identifier of the first Claim."),
    b: str = typer.Option(..., "--b", help="Identifier of the second Claim."),
    target: str = typer.Option(
        ".", "--target", help="Path to the target Gaia package (default: cwd)."
    ),
    file: str | None = typer.Option(
        None,
        "--file",
        help=("Relative path under src/<import_name>/ to write into. Default: `__init__.py`."),
    ),
    rationale: str | None = typer.Option(
        None, "--rationale", help="Optional natural-language justification."
    ),
    background: str | None = typer.Option(
        None,
        "--background",
        help="Comma-separated identifiers passed as the equal() background kwarg.",
    ),
    metadata: str | None = typer.Option(
        None, "--metadata", help="Optional JSON-encoded metadata dict."
    ),
    export: bool = typer.Option(
        False,
        "--export/--no-export",
        help=(
            "Add the returned equivalence helper to __all__ as a public "
            "relation export. Default off: exports are curated explicitly."
        ),
    ),
    check: bool = typer.Option(
        True,
        "--check/--no-check",
        help="Run post-write `gaia build check` after a successful write (default on).",
    ),
    human: bool = typer.Option(
        False,
        "--human",
        help="Render the envelope in human-readable form instead of JSON.",
    ),
    interactive: bool = typer.Option(
        False,
        "--interactive",
        help="Prompt on pre-write warnings (currently a no-op; reserved for future use).",
    ),
    json_: bool = typer.Option(
        True, "--json/--no-json", help="JSON-first output (default; redundant for clarity)."
    ),
) -> None:
    r"""Append an ``equal(a, b, ...)`` structural relation.

    Example:
        gaia author equal --a my_claim_a --b my_claim_b \
            --dsl-binding-name my_equivalence --label my_equivalence
    """
    del json_

    metadata_dict, metadata_error = _parse_metadata(metadata)
    if metadata_error:
        diag = Diagnostic(
            kind="prewrite.syntax",
            level="error",
            message=metadata_error,
            source="prewrite",
        )
        result = AuthorResult(
            verb="equal",
            status="error",
            code=exit_code_for_diagnostic(diag.kind),
            payload={"target": str(target)},
            diagnostics=[diag],
        )
        emit(result, human=human)
        return

    # Axis 1 — identifier-shape gates on --a / --b.
    if not validate_identifier_flag(a, verb="equal", flag="--a", target=str(target), human=human):
        return
    if not validate_identifier_flag(b, verb="equal", flag="--b", target=str(target), human=human):
        return

    background_list, background_error = split_csv_idents(background)
    if background_error:
        emit_syntax_error(
            "equal",
            f"--background rejected: {background_error}",
            target=str(target),
            human=human,
            kind="prewrite.expr_unsafe",
        )
        return
    generated_code = _render_equal_statement(
        binding_name=dsl_binding_name,
        engine_label=label,
        a=a,
        b=b,
        rationale=rationale,
        metadata=metadata_dict,
        background=background_list,
    )
    target_file = normalize_file_option(file)
    references = [a, b, *background_list]
    proposed_op = ProposedAuthorOp(
        verb="equal",
        kind="reasoning",
        label=dsl_binding_name,
        references=references,
        generated_code=generated_code,
        required_imports=("equal",),
        target_file=target_file,
        sibling_imports=build_sibling_imports(references, target_file=target_file),
        export=export,
    )
    run_author_op(
        proposed_op,
        target=target,
        human=human,
        check=check,
        interactive=interactive,
    )

exclusive_command

exclusive_command(label: str | None = typer.Option(None, '--label', help='Engine `label=` kwarg on the rendered exclusive(...) call. Distinct from --dsl-binding-name (the Python LHS).'), dsl_binding_name: str | None = typer.Option(None, '--dsl-binding-name', help='Python LHS for the rendered statement (``<name> = exclusive(...)``). Omit to emit a bare expression.'), a: str = typer.Option(..., '--a', help='Identifier of the first Claim.'), b: str = typer.Option(..., '--b', help='Identifier of the second Claim.'), target: str = typer.Option('.', '--target', help='Path to the target Gaia package (default: cwd).'), file: str | None = typer.Option(None, '--file', help='Relative path under src/<import_name>/ to write into. Default: `__init__.py`.'), rationale: str | None = typer.Option(None, '--rationale', help='Optional natural-language justification.'), background: str | None = typer.Option(None, '--background', help='Comma-separated identifiers passed as the exclusive() background kwarg.'), metadata: str | None = typer.Option(None, '--metadata', help='Optional JSON-encoded metadata dict.'), export: bool = typer.Option(False, '--export/--no-export', help='Add the returned exclusive/complement helper to __all__ as a public relation export. Default off: exports are curated explicitly.'), check: bool = typer.Option(True, '--check/--no-check', help='Run post-write `gaia build check` after a successful write (default on).'), human: bool = typer.Option(False, '--human', help='Render the envelope in human-readable form instead of JSON.'), interactive: bool = typer.Option(False, '--interactive', help='Prompt on pre-write warnings (human mode only).'), json_: bool = typer.Option(True, '--json/--no-json', help='JSON-first output (default; redundant for clarity).')) -> None

Append an exclusive(a, b, ...) closed-partition statement.

Example

gaia author exclusive --a my_outcome_a --b my_outcome_b \ --dsl-binding-name my_partition

Source code in gaia/cli/commands/author/exclusive.py
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
def exclusive_command(
    label: str | None = typer.Option(
        None,
        "--label",
        help=(
            "Engine `label=` kwarg on the rendered exclusive(...) call. "
            "Distinct from --dsl-binding-name (the Python LHS)."
        ),
    ),
    dsl_binding_name: str | None = typer.Option(
        None,
        "--dsl-binding-name",
        help=(
            "Python LHS for the rendered statement (``<name> = "
            "exclusive(...)``). Omit to emit a bare expression."
        ),
    ),
    a: str = typer.Option(..., "--a", help="Identifier of the first Claim."),
    b: str = typer.Option(..., "--b", help="Identifier of the second Claim."),
    target: str = typer.Option(
        ".", "--target", help="Path to the target Gaia package (default: cwd)."
    ),
    file: str | None = typer.Option(
        None,
        "--file",
        help=("Relative path under src/<import_name>/ to write into. Default: `__init__.py`."),
    ),
    rationale: str | None = typer.Option(
        None, "--rationale", help="Optional natural-language justification."
    ),
    background: str | None = typer.Option(
        None,
        "--background",
        help="Comma-separated identifiers passed as the exclusive() background kwarg.",
    ),
    metadata: str | None = typer.Option(
        None, "--metadata", help="Optional JSON-encoded metadata dict."
    ),
    export: bool = typer.Option(
        False,
        "--export/--no-export",
        help=(
            "Add the returned exclusive/complement helper to __all__ as a public "
            "relation export. Default off: exports are curated explicitly."
        ),
    ),
    check: bool = typer.Option(
        True,
        "--check/--no-check",
        help="Run post-write `gaia build check` after a successful write (default on).",
    ),
    human: bool = typer.Option(
        False, "--human", help="Render the envelope in human-readable form instead of JSON."
    ),
    interactive: bool = typer.Option(
        False, "--interactive", help="Prompt on pre-write warnings (human mode only)."
    ),
    json_: bool = typer.Option(
        True, "--json/--no-json", help="JSON-first output (default; redundant for clarity)."
    ),
) -> None:
    r"""Append an ``exclusive(a, b, ...)`` closed-partition statement.

    Example:
        gaia author exclusive --a my_outcome_a --b my_outcome_b \
            --dsl-binding-name my_partition
    """
    del json_

    metadata_dict, metadata_error = parse_metadata(metadata)
    if metadata_error:
        emit_syntax_error("exclusive", metadata_error, target=str(target), human=human)
        return

    if not validate_identifier_flag(
        a, verb="exclusive", flag="--a", target=str(target), human=human
    ):
        return
    if not validate_identifier_flag(
        b, verb="exclusive", flag="--b", target=str(target), human=human
    ):
        return

    background_list, background_error = split_csv_idents(background)
    if background_error:
        emit_syntax_error(
            "exclusive",
            f"--background rejected: {background_error}",
            target=str(target),
            human=human,
            kind="prewrite.expr_unsafe",
        )
        return
    generated_code = _render_exclusive_statement(
        binding_name=dsl_binding_name,
        engine_label=label,
        a=a,
        b=b,
        rationale=rationale,
        metadata=metadata_dict,
        background=background_list,
    )
    target_file = normalize_file_option(file)
    references = [a, b, *background_list]
    proposed_op = ProposedAuthorOp(
        verb="exclusive",
        kind="reasoning",
        label=dsl_binding_name,
        references=references,
        generated_code=generated_code,
        required_imports=("exclusive",),
        target_file=target_file,
        sibling_imports=build_sibling_imports(references, target_file=target_file),
        export=export,
    )
    run_author_op(
        proposed_op,
        target=target,
        human=human,
        check=check,
        interactive=interactive,
    )

infer_command

infer_command(label: str | None = typer.Option(None, '--label', help='Engine `label=` kwarg on the rendered infer(...) call. Distinct from --dsl-binding-name (the Python LHS).'), dsl_binding_name: str | None = typer.Option(None, '--dsl-binding-name', help='Python LHS for the rendered statement (``<name> = infer(...)``). Omit to emit a bare expression.'), evidence: str = typer.Option(..., '--evidence', help='Identifier of the evidence Claim.'), hypothesis: str | None = typer.Option(None, '--hypothesis', help='Identifier of the hypothesis Claim (must already be declared). Mutually exclusive with --hypothesis-content.'), hypothesis_content: str | None = typer.Option(None, '--hypothesis-content', help='Prose for an auto-generated hypothesis Claim. Mutually exclusive with --hypothesis. Cli derives a snake-case slug for the label (override via --hypothesis-label).'), hypothesis_prose: str | None = typer.Option(None, '--hypothesis-prose', help="Inline prose wrapped into an anonymous claim() at the call site. Emits ``infer(evidence, hypothesis=claim('<prose>'), ...)`` with no named binding. Mutually exclusive with --hypothesis and --hypothesis-content. (The engine's ``infer()`` hypothesis kwarg is strictly Claim-typed, so the cli wraps the prose at the call site rather than passing a bare string.)"), hypothesis_label: str | None = typer.Option(None, '--hypothesis-label', help='Optional explicit label for the auto-generated hypothesis Claim (only meaningful with --hypothesis-content).'), p_e_given_h: float = typer.Option(..., '--p-e-given-h', help='P(evidence | hypothesis) — required likelihood under the hypothesis.'), target: str = typer.Option('.', '--target', help='Path to the target Gaia package (default: cwd).'), file: str | None = typer.Option(None, '--file', help='Relative path under src/<import_name>/ to write into. Default: `__init__.py`.'), p_e_given_not_h: float | None = typer.Option(None, '--p-e-given-not-h', help='P(evidence | NOT hypothesis) — defaults to 0.5 in the DSL when omitted.'), given: str | None = typer.Option(None, '--given', help='Comma-separated identifiers of conditioning Claim(s).'), background: str | None = typer.Option(None, '--background', help='Comma-separated identifiers passed as the infer() background kwarg.'), rationale: str | None = typer.Option(None, '--rationale', help='Optional natural-language justification.'), metadata: str | None = typer.Option(None, '--metadata', help='Optional JSON-encoded metadata dict.'), export: bool = typer.Option(False, '--export/--no-export', help="Add --dsl-binding-name to __all__ on a successful write. Default off: exports are the package's curated public Knowledge surface."), check: bool = typer.Option(True, '--check/--no-check', help='Run post-write `gaia build check` after a successful write (default on).'), human: bool = typer.Option(False, '--human', help='Render the envelope in human-readable form instead of JSON.'), interactive: bool = typer.Option(False, '--interactive', help='Prompt on pre-write warnings (human mode only).'), json_: bool = typer.Option(True, '--json/--no-json', help='JSON-first output (default; redundant for clarity).')) -> None

Append an infer(...) Bayesian-inference statement.

Example

gaia author infer --evidence my_evidence \ --hypothesis my_hypothesis --p-e-given-h 0.7 \ --dsl-binding-name my_inference --label my_inference

Source code in gaia/cli/commands/author/infer.py
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
def infer_command(
    label: str | None = typer.Option(
        None,
        "--label",
        help=(
            "Engine `label=` kwarg on the rendered infer(...) call. "
            "Distinct from --dsl-binding-name (the Python LHS)."
        ),
    ),
    dsl_binding_name: str | None = typer.Option(
        None,
        "--dsl-binding-name",
        help=(
            "Python LHS for the rendered statement (``<name> = "
            "infer(...)``). Omit to emit a bare expression."
        ),
    ),
    evidence: str = typer.Option(..., "--evidence", help="Identifier of the evidence Claim."),
    hypothesis: str | None = typer.Option(
        None,
        "--hypothesis",
        help=(
            "Identifier of the hypothesis Claim (must already be declared). "
            "Mutually exclusive with --hypothesis-content."
        ),
    ),
    hypothesis_content: str | None = typer.Option(
        None,
        "--hypothesis-content",
        help=(
            "Prose for an auto-generated hypothesis Claim. Mutually exclusive with "
            "--hypothesis. Cli derives a snake-case slug for the label "
            "(override via --hypothesis-label)."
        ),
    ),
    hypothesis_prose: str | None = typer.Option(
        None,
        "--hypothesis-prose",
        help=(
            "Inline prose wrapped into an anonymous claim() at the call site. "
            "Emits ``infer(evidence, hypothesis=claim('<prose>'), ...)`` with "
            "no named binding. Mutually exclusive with --hypothesis and "
            "--hypothesis-content. (The engine's ``infer()`` hypothesis kwarg "
            "is strictly Claim-typed, so the cli wraps the prose at the call "
            "site rather than passing a bare string.)"
        ),
    ),
    hypothesis_label: str | None = typer.Option(
        None,
        "--hypothesis-label",
        help=(
            "Optional explicit label for the auto-generated hypothesis Claim "
            "(only meaningful with --hypothesis-content)."
        ),
    ),
    p_e_given_h: float = typer.Option(
        ...,
        "--p-e-given-h",
        help="P(evidence | hypothesis) — required likelihood under the hypothesis.",
    ),
    target: str = typer.Option(
        ".", "--target", help="Path to the target Gaia package (default: cwd)."
    ),
    file: str | None = typer.Option(
        None,
        "--file",
        help=("Relative path under src/<import_name>/ to write into. Default: `__init__.py`."),
    ),
    p_e_given_not_h: float | None = typer.Option(
        None,
        "--p-e-given-not-h",
        help="P(evidence | NOT hypothesis) — defaults to 0.5 in the DSL when omitted.",
    ),
    given: str | None = typer.Option(
        None,
        "--given",
        help="Comma-separated identifiers of conditioning Claim(s).",
    ),
    background: str | None = typer.Option(
        None,
        "--background",
        help="Comma-separated identifiers passed as the infer() background kwarg.",
    ),
    rationale: str | None = typer.Option(
        None, "--rationale", help="Optional natural-language justification."
    ),
    metadata: str | None = typer.Option(
        None, "--metadata", help="Optional JSON-encoded metadata dict."
    ),
    export: bool = typer.Option(
        False,
        "--export/--no-export",
        help=(
            "Add --dsl-binding-name to __all__ on a successful write. Default off: "
            "exports are the package's curated public Knowledge surface."
        ),
    ),
    check: bool = typer.Option(
        True,
        "--check/--no-check",
        help="Run post-write `gaia build check` after a successful write (default on).",
    ),
    human: bool = typer.Option(
        False, "--human", help="Render the envelope in human-readable form instead of JSON."
    ),
    interactive: bool = typer.Option(
        False, "--interactive", help="Prompt on pre-write warnings (human mode only)."
    ),
    json_: bool = typer.Option(
        True, "--json/--no-json", help="JSON-first output (default; redundant for clarity)."
    ),
) -> None:
    r"""Append an ``infer(...)`` Bayesian-inference statement.

    Example:
        gaia author infer --evidence my_evidence \
            --hypothesis my_hypothesis --p-e-given-h 0.7 \
            --dsl-binding-name my_inference --label my_inference
    """
    del json_

    # --- mutual-exclusion check on hypothesis-mode ----------------------- #
    hyp_modes = [hypothesis, hypothesis_content, hypothesis_prose]
    hyp_modes_set = sum(1 for value_ in hyp_modes if value_ is not None)
    if hyp_modes_set == 0:
        emit_syntax_error(
            "infer",
            (
                "infer requires exactly one of --hypothesis / --hypothesis-content "
                "/ --hypothesis-prose"
            ),
            target=str(target),
            human=human,
        )
        return
    if hyp_modes_set > 1:
        emit_syntax_error(
            "infer",
            (
                "--hypothesis, --hypothesis-content, and --hypothesis-prose are "
                "mutually exclusive — pick exactly one"
            ),
            target=str(target),
            human=human,
        )
        return
    if hypothesis_label is not None and hypothesis_content is None:
        emit_syntax_error(
            "infer",
            "--hypothesis-label only applies with --hypothesis-content",
            target=str(target),
            human=human,
        )
        return

    metadata_dict, metadata_error = parse_metadata(metadata)
    if metadata_error:
        emit_syntax_error("infer", metadata_error, target=str(target), human=human)
        return

    given_list, given_error = split_csv_idents(given)
    if given_error:
        emit_syntax_error(
            "infer",
            f"--given rejected: {given_error}",
            target=str(target),
            human=human,
            kind="prewrite.expr_unsafe",
        )
        return
    background_list, background_error = split_csv_idents(background)
    if background_error:
        emit_syntax_error(
            "infer",
            f"--background rejected: {background_error}",
            target=str(target),
            human=human,
            kind="prewrite.expr_unsafe",
        )
        return

    # --- resolve hypothesis mode ---------------------------------------- #
    prepended: tuple[tuple[str, str], ...] = ()
    references: list[str]
    hypothesis_kind: str
    if hypothesis_content is not None:
        if hypothesis_label is not None:
            auto_label = hypothesis_label
        else:
            reserved = {evidence, *given_list, *background_list}
            if dsl_binding_name is not None:
                reserved.add(dsl_binding_name)
            auto_label = slugify_label(hypothesis_content, existing=reserved)
        prepended = ((auto_label, build_auto_claim_statement(auto_label, hypothesis_content)),)
        hypothesis_expr = auto_label
        references = [evidence, auto_label, *given_list, *background_list]
        hypothesis_kind = "auto_mint"
    elif hypothesis_prose is not None:
        # Inline-prose: wrap the prose with claim() at the call
        # site. The engine's infer() hypothesis kwarg is strictly Claim-
        # typed (no ``Claim | str`` polymorphism unlike derive/observe).
        # No named binding is introduced; references list omits the prose.
        hypothesis_expr = f"claim({hypothesis_prose!r})"
        references = [evidence, *given_list, *background_list]
        hypothesis_kind = "inline_prose"
    else:
        assert hypothesis is not None  # mutex check above
        hypothesis_expr = hypothesis
        references = [evidence, hypothesis, *given_list, *background_list]
        hypothesis_kind = "qid"

    generated_code = _render_infer_statement(
        binding_name=dsl_binding_name,
        engine_label=label,
        evidence=evidence,
        hypothesis_expr=hypothesis_expr,
        p_e_given_h=p_e_given_h,
        p_e_given_not_h=p_e_given_not_h,
        given=given_list,
        rationale=rationale,
        metadata=metadata_dict,
        background=background_list,
    )
    required_imports: tuple[str, ...] = ("infer",)
    if hypothesis_prose is not None:
        # Inline-prose wraps the prose with ``claim(...)`` at the call
        # site, so the target file needs ``claim`` importable.
        required_imports = ("infer", "claim")
    target_file = normalize_file_option(file)
    proposed_op = ProposedAuthorOp(
        verb="infer",
        kind="reasoning",
        label=dsl_binding_name,
        references=references,
        generated_code=generated_code,
        required_imports=required_imports,
        target_file=target_file,
        sibling_imports=build_sibling_imports(references, target_file=target_file),
        prepended_statements=prepended,
        extra_payload={"hypothesis_kind": hypothesis_kind},
        export=export,
    )
    run_author_op(
        proposed_op,
        target=target,
        human=human,
        check=check,
        interactive=interactive,
    )

list_command

list_command(target: str = typer.Option('.', '--target', help='Path to the target Gaia package (default: cwd).'), file: str | None = typer.Option(None, '--file', help='Relative path under ``src/<import_name>/`` to scope the scan to a single source file (e.g. ``__init__.py`` or ``observations.py``). Default: every non-auxiliary ``.py`` file under the source root.'), kind: str | None = typer.Option(None, '--kind', help='Filter to one binding kind (e.g. ``claim`` / ``derive`` / ``variable``). The value matches the ``kind`` column / JSON field — use the underscored callable form.'), unbound: bool = typer.Option(False, '--unbound', help='Include bare-expression statements (calls without an LHS binding) in the output. Default off — bare calls are frequently scaffolding noise.'), human: bool = typer.Option(False, '--human', help='Render output as a human-readable table instead of the JSON envelope.'), json_: bool = typer.Option(True, '--json/--no-json', help='JSON-first output (default; redundant for clarity).')) -> None

List author-verb bindings in a Gaia DSL package.

Walks every .py file under the package source root as Python AST (no engine import, no compile pipeline) and reports every top-level author-verb statement: kind, binding name, content preview, file, line, and whether the binding is exported via __all__. Reads [[tool.gaia.compositions]] entries from pyproject.toml for the trailing compositions section.

The verb is read-only; it never writes to disk and does not share the write/postwrite pipeline used by the other 19 author verbs.

Example

gaia author list --target ./my-pkg-gaia --human gaia author list --target ./my-pkg --kind claim --json

Source code in gaia/cli/commands/author/list.py
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
def list_command(
    target: str = typer.Option(
        ".", "--target", help="Path to the target Gaia package (default: cwd)."
    ),
    file: str | None = typer.Option(
        None,
        "--file",
        help=(
            "Relative path under ``src/<import_name>/`` to scope the scan "
            "to a single source file (e.g. ``__init__.py`` or "
            "``observations.py``). Default: every non-auxiliary ``.py`` "
            "file under the source root."
        ),
    ),
    kind: str | None = typer.Option(
        None,
        "--kind",
        help=(
            "Filter to one binding kind (e.g. ``claim`` / ``derive`` / "
            "``variable``). The value matches the ``kind`` column / "
            "JSON field — use the underscored callable form."
        ),
    ),
    unbound: bool = typer.Option(
        False,
        "--unbound",
        help=(
            "Include bare-expression statements (calls without an LHS "
            "binding) in the output. Default off — bare calls are "
            "frequently scaffolding noise."
        ),
    ),
    human: bool = typer.Option(
        False,
        "--human",
        help="Render output as a human-readable table instead of the JSON envelope.",
    ),
    json_: bool = typer.Option(
        True, "--json/--no-json", help="JSON-first output (default; redundant for clarity)."
    ),
) -> None:
    """List author-verb bindings in a Gaia DSL package.

    Walks every ``.py`` file under the package source root as Python AST
    (no engine import, no compile pipeline) and reports every top-level
    author-verb statement: kind, binding name, content preview, file,
    line, and whether the binding is exported via ``__all__``. Reads
    ``[[tool.gaia.compositions]]`` entries from ``pyproject.toml`` for
    the trailing compositions section.

    The verb is read-only; it never writes to disk and does not share
    the write/postwrite pipeline used by the other 19 author verbs.

    Example:
        gaia author list --target ./my-pkg-gaia --human
        gaia author list --target ./my-pkg --kind claim --json
    """
    del json_  # JSON-vs-human is governed by --human; --json is a courtesy alias.

    target_root = Path(target).resolve()

    resolved, target_diag = _resolve_target(target_root)
    if resolved is None:
        # _resolve_target guarantees a diagnostic when resolved is None.
        assert target_diag is not None
        _emit_target_error(target_diag, target_root=target_root, human=human)
        return

    warnings_list: list[str] = []
    target_file_rel = normalize_file_option(file)
    scanned_files = _select_scanned_files(
        resolved=resolved,
        target_file_rel=target_file_rel,
        target_root=target_root,
        human=human,
        warnings_list=warnings_list,
    )
    if scanned_files is None:
        return

    file_scans, parse_errors, scan_warnings = _run_scans(
        scanned_files=scanned_files, source_root=resolved.source_root
    )
    warnings_list.extend(scan_warnings)

    rows = _assemble_rows(file_scans=file_scans, unbound=unbound, kind=kind)
    compositions = _read_compositions(resolved.pyproject)
    summary = _build_summary(rows, len(compositions))

    _emit_outcome(
        target_root=target_root,
        resolved=resolved,
        rows=rows,
        compositions=compositions,
        summary=summary,
        warnings_list=warnings_list,
        parse_errors=parse_errors,
        human=human,
    )

materialize_command

materialize_command(label: str | None = typer.Option(None, '--label', help='Engine `label=` kwarg on the rendered materialize(...) call. Distinct from --dsl-binding-name (the Python LHS).'), dsl_binding_name: str | None = typer.Option(None, '--dsl-binding-name', help='Python LHS for the rendered statement (``<name> = materialize(...)``). Omit to emit a bare expression.'), scaffold: str = typer.Option(..., '--scaffold', help='Identifier of the registered Scaffold (DependsOn / CandidateRelation).'), by: str = typer.Option(..., '--by', help='Comma-separated identifiers of formal graph records that materialise the scaffold.'), target: str = typer.Option('.', '--target', help='Path to the target Gaia package (default: cwd).'), file: str | None = typer.Option(None, '--file', help='Relative path under src/<import_name>/ to write into. Default: `__init__.py`.'), rationale: str | None = typer.Option(None, '--rationale', help='Optional natural-language justification.'), metadata: str | None = typer.Option(None, '--metadata', help='Optional JSON-encoded metadata dict.'), export: bool = typer.Option(False, '--export/--no-export', help='Add --dsl-binding-name to __all__ on a successful write (default off for materialize: the link is structural metadata, not part of the public Knowledge surface).'), check: bool = typer.Option(True, '--check/--no-check', help='Run post-write `gaia build check` after a successful write (default on).'), human: bool = typer.Option(False, '--human', help='Render the envelope in human-readable form instead of JSON.'), interactive: bool = typer.Option(False, '--interactive', help='Prompt on pre-write warnings (human mode only).'), json_: bool = typer.Option(True, '--json/--no-json', help='JSON-first output (default; redundant for clarity).')) -> None

Append a materialize(scaffold, by=...) scaffold-to-formal-record link.

Example

gaia author materialize --scaffold my_maybe_equal \ --by my_formal_equal --dsl-binding-name my_materialization

Source code in gaia/cli/commands/author/materialize.py
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
def materialize_command(
    label: str | None = typer.Option(
        None,
        "--label",
        help=(
            "Engine `label=` kwarg on the rendered materialize(...) call. "
            "Distinct from --dsl-binding-name (the Python LHS)."
        ),
    ),
    dsl_binding_name: str | None = typer.Option(
        None,
        "--dsl-binding-name",
        help=(
            "Python LHS for the rendered statement (``<name> = "
            "materialize(...)``). Omit to emit a bare expression."
        ),
    ),
    scaffold: str = typer.Option(
        ...,
        "--scaffold",
        help="Identifier of the registered Scaffold (DependsOn / CandidateRelation).",
    ),
    by: str = typer.Option(
        ...,
        "--by",
        help="Comma-separated identifiers of formal graph records that materialise the scaffold.",
    ),
    target: str = typer.Option(
        ".", "--target", help="Path to the target Gaia package (default: cwd)."
    ),
    file: str | None = typer.Option(
        None,
        "--file",
        help=("Relative path under src/<import_name>/ to write into. Default: `__init__.py`."),
    ),
    rationale: str | None = typer.Option(
        None, "--rationale", help="Optional natural-language justification."
    ),
    metadata: str | None = typer.Option(
        None, "--metadata", help="Optional JSON-encoded metadata dict."
    ),
    export: bool = typer.Option(
        False,
        "--export/--no-export",
        help=(
            "Add --dsl-binding-name to __all__ on a successful write "
            "(default off for materialize: the link is structural metadata, "
            "not part of the public Knowledge surface)."
        ),
    ),
    check: bool = typer.Option(
        True,
        "--check/--no-check",
        help="Run post-write `gaia build check` after a successful write (default on).",
    ),
    human: bool = typer.Option(
        False, "--human", help="Render the envelope in human-readable form instead of JSON."
    ),
    interactive: bool = typer.Option(
        False, "--interactive", help="Prompt on pre-write warnings (human mode only)."
    ),
    json_: bool = typer.Option(
        True, "--json/--no-json", help="JSON-first output (default; redundant for clarity)."
    ),
) -> None:
    r"""Append a ``materialize(scaffold, by=...)`` scaffold-to-formal-record link.

    Example:
        gaia author materialize --scaffold my_maybe_equal \
            --by my_formal_equal --dsl-binding-name my_materialization
    """
    del json_

    metadata_dict, metadata_error = parse_metadata(metadata)
    if metadata_error:
        emit_syntax_error("materialize", metadata_error, target=str(target), human=human)
        return

    if not validate_identifier_flag(
        scaffold, verb="materialize", flag="--scaffold", target=str(target), human=human
    ):
        return
    by_list, by_error = split_csv_idents(by)
    if by_error:
        emit_syntax_error(
            "materialize",
            f"--by rejected: {by_error}",
            target=str(target),
            human=human,
            kind="prewrite.expr_unsafe",
        )
        return
    if not by_list:
        emit_syntax_error(
            "materialize",
            "--by must list at least one materialising record identifier",
            target=str(target),
            human=human,
        )
        return

    generated_code = _render_materialize_statement(
        binding_name=dsl_binding_name,
        engine_label=label,
        scaffold=scaffold,
        by=by_list,
        rationale=rationale,
        metadata=metadata_dict,
    )
    references = [scaffold, *by_list]
    target_file = normalize_file_option(file)
    proposed_op = ProposedAuthorOp(
        verb="materialize",
        kind="scaffold",
        label=dsl_binding_name,
        references=references,
        generated_code=generated_code,
        required_imports=("materialize",),
        target_file=target_file,
        sibling_imports=build_sibling_imports(references, target_file=target_file),
        export=export,
    )
    run_author_op(
        proposed_op,
        target=target,
        human=human,
        check=check,
        interactive=interactive,
    )

note_command

note_command(content: str = typer.Argument(..., help='Note content (natural-language background).'), dsl_binding_name: str | None = typer.Option(None, '--dsl-binding-name', help='Python module-scope identifier the rendered statement binds to (``<name> = note(...)``). Omit to emit a bare expression. ``note()`` does not take an engine ``label=`` kwarg, so this is the only label-like flag the verb exposes.'), target: str = typer.Option('.', '--target', help='Path to the target Gaia package (default: cwd).'), file: str | None = typer.Option(None, '--file', help='Relative path under src/<import_name>/ to write into. Default: `__init__.py`.'), title: str | None = typer.Option(None, '--title', help='Optional short title for the note.'), metadata: str | None = typer.Option(None, '--metadata', help='Optional JSON-encoded metadata dict.'), export: bool = typer.Option(False, '--export/--no-export', help="Add --dsl-binding-name to the target module's __all__ on a successful write (default off for note: notes are contextual background, not part of the package's public Knowledge surface)."), check: bool = typer.Option(True, '--check/--no-check', help='Run post-write `gaia build check` after a successful write (default on).'), human: bool = typer.Option(False, '--human', help='Render the envelope in human-readable form instead of JSON.'), interactive: bool = typer.Option(False, '--interactive', help='Prompt on pre-write warnings (human mode only).'), json_: bool = typer.Option(True, '--json/--no-json', help='JSON-first output (default; redundant for clarity).')) -> None

Append a note(...) background statement.

Example

gaia author note "Earlier work established the setup." \ --dsl-binding-name background_setup --title "Setup background"

Source code in gaia/cli/commands/author/note.py
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
def note_command(
    content: str = typer.Argument(..., help="Note content (natural-language background)."),
    dsl_binding_name: str | None = typer.Option(
        None,
        "--dsl-binding-name",
        help=(
            "Python module-scope identifier the rendered statement binds to "
            "(``<name> = note(...)``). Omit to emit a bare expression. "
            "``note()`` does not take an engine ``label=`` kwarg, so this "
            "is the only label-like flag the verb exposes."
        ),
    ),
    target: str = typer.Option(
        ".", "--target", help="Path to the target Gaia package (default: cwd)."
    ),
    file: str | None = typer.Option(
        None,
        "--file",
        help=("Relative path under src/<import_name>/ to write into. Default: `__init__.py`."),
    ),
    title: str | None = typer.Option(None, "--title", help="Optional short title for the note."),
    metadata: str | None = typer.Option(
        None, "--metadata", help="Optional JSON-encoded metadata dict."
    ),
    export: bool = typer.Option(
        False,
        "--export/--no-export",
        help=(
            "Add --dsl-binding-name to the target module's __all__ on a "
            "successful write (default off for note: notes are contextual "
            "background, not part of the package's public Knowledge surface)."
        ),
    ),
    check: bool = typer.Option(
        True,
        "--check/--no-check",
        help="Run post-write `gaia build check` after a successful write (default on).",
    ),
    human: bool = typer.Option(
        False, "--human", help="Render the envelope in human-readable form instead of JSON."
    ),
    interactive: bool = typer.Option(
        False, "--interactive", help="Prompt on pre-write warnings (human mode only)."
    ),
    json_: bool = typer.Option(
        True, "--json/--no-json", help="JSON-first output (default; redundant for clarity)."
    ),
) -> None:
    r"""Append a ``note(...)`` background statement.

    Example:
        gaia author note "Earlier work established the setup." \
            --dsl-binding-name background_setup --title "Setup background"
    """
    del json_

    metadata_dict, metadata_error = parse_metadata(metadata)
    if metadata_error:
        emit_syntax_error("note", metadata_error, target=str(target), human=human)
        return

    generated_code = _render_note_statement(
        binding_name=dsl_binding_name,
        content=content,
        title=title,
        metadata=metadata_dict,
    )
    proposed_op = ProposedAuthorOp(
        verb="note",
        kind="reasoning",
        label=dsl_binding_name,
        references=[],
        generated_code=generated_code,
        required_imports=("note",),
        target_file=normalize_file_option(file),
        export=export,
    )
    run_author_op(
        proposed_op,
        target=target,
        human=human,
        check=check,
        interactive=interactive,
    )

observe_command

observe_command(label: str | None = typer.Option(None, '--label', help='Engine `label=` kwarg on the rendered observe(...) call. Distinct from --dsl-binding-name (the Python LHS).'), dsl_binding_name: str | None = typer.Option(None, '--dsl-binding-name', help='Python LHS for the rendered statement (``<name> = observe(...)``). Omit to emit a bare expression.'), conclusion: str | None = typer.Option(None, '--conclusion', help='Identifier of the observed Claim, Variable, or Distribution (must already be declared). Mutually exclusive with --observation-content.'), observation_content: str | None = typer.Option(None, '--observation-content', help='Prose for an auto-generated observation Claim. Mutually exclusive with --conclusion. Cli derives a snake-case slug for the label (override via --observation-label). Only valid for discrete observations; the quantity form (--value / --error) targets a Variable or Distribution and so must use --conclusion.'), observation_prose: str | None = typer.Option(None, '--observation-prose', help="Inline prose passed through the engine's ``observe(conclusion: Claim | str, ...)`` polymorphism. Emits ``observe('<prose>', ...)`` directly with no named binding. Mutually exclusive with --conclusion and --observation-content."), observation_label: str | None = typer.Option(None, '--observation-label', help='Optional explicit label for the auto-generated observation Claim (only meaningful with --observation-content).'), target: str = typer.Option('.', '--target', help='Path to the target Gaia package (default: cwd).'), file: str | None = typer.Option(None, '--file', help='Relative path under src/<import_name>/ to write into. Default: `__init__.py`.'), value: str | None = typer.Option(None, '--value', help="Numeric value (quantity form). Forwarded verbatim — pass a literal (`--value 203`) or a Quantity expression (`--value '203 * ureg.K'`)."), error: str | None = typer.Option(None, '--error', help='Observation error (quantity form): scalar sigma or Distribution.'), given: str | None = typer.Option(None, '--given', help='Comma-separated premise identifiers for a conditional discrete observation.'), background: str | None = typer.Option(None, '--background', help='Comma-separated identifiers passed as the observe() background kwarg.'), source_refs: str | None = typer.Option(None, '--source-refs', help='Comma-separated source reference strings attached to the observation.'), rationale: str | None = typer.Option(None, '--rationale', help='Optional natural-language justification.'), metadata: str | None = typer.Option(None, '--metadata', help='Optional JSON-encoded metadata dict.'), export: bool = typer.Option(False, '--export/--no-export', help="Add --dsl-binding-name to __all__ on a successful write. Default off: exports are the package's curated public Knowledge surface."), check: bool = typer.Option(True, '--check/--no-check', help='Run post-write `gaia build check` after a successful write (default on).'), human: bool = typer.Option(False, '--human', help='Render the envelope in human-readable form instead of JSON.'), interactive: bool = typer.Option(False, '--interactive', help='Prompt on pre-write warnings (human mode only).'), json_: bool = typer.Option(True, '--json/--no-json', help='JSON-first output (default; redundant for clarity).')) -> None

Append an observe(...) measurement event.

Example

gaia author observe --conclusion my_distribution \ --value 203 --error 5 \ --dsl-binding-name temperature_obs --label temperature_obs

Source code in gaia/cli/commands/author/observe.py
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
def observe_command(
    label: str | None = typer.Option(
        None,
        "--label",
        help=(
            "Engine `label=` kwarg on the rendered observe(...) call. "
            "Distinct from --dsl-binding-name (the Python LHS)."
        ),
    ),
    dsl_binding_name: str | None = typer.Option(
        None,
        "--dsl-binding-name",
        help=(
            "Python LHS for the rendered statement (``<name> = "
            "observe(...)``). Omit to emit a bare expression."
        ),
    ),
    conclusion: str | None = typer.Option(
        None,
        "--conclusion",
        help=(
            "Identifier of the observed Claim, Variable, or Distribution "
            "(must already be declared). Mutually exclusive with "
            "--observation-content."
        ),
    ),
    observation_content: str | None = typer.Option(
        None,
        "--observation-content",
        help=(
            "Prose for an auto-generated observation Claim. Mutually exclusive with "
            "--conclusion. Cli derives a snake-case slug for the label (override "
            "via --observation-label). Only valid for discrete observations; the "
            "quantity form (--value / --error) targets a Variable or "
            "Distribution and so must use --conclusion."
        ),
    ),
    observation_prose: str | None = typer.Option(
        None,
        "--observation-prose",
        help=(
            "Inline prose passed through the engine's "
            "``observe(conclusion: Claim | str, ...)`` polymorphism. Emits "
            "``observe('<prose>', ...)`` directly with no named binding. "
            "Mutually exclusive with --conclusion and --observation-content."
        ),
    ),
    observation_label: str | None = typer.Option(
        None,
        "--observation-label",
        help=(
            "Optional explicit label for the auto-generated observation Claim "
            "(only meaningful with --observation-content)."
        ),
    ),
    target: str = typer.Option(
        ".", "--target", help="Path to the target Gaia package (default: cwd)."
    ),
    file: str | None = typer.Option(
        None,
        "--file",
        help=("Relative path under src/<import_name>/ to write into. Default: `__init__.py`."),
    ),
    value: str | None = typer.Option(
        None,
        "--value",
        help=(
            "Numeric value (quantity form). Forwarded verbatim — pass a literal "
            "(`--value 203`) or a Quantity expression (`--value '203 * ureg.K'`)."
        ),
    ),
    error: str | None = typer.Option(
        None,
        "--error",
        help="Observation error (quantity form): scalar sigma or Distribution.",
    ),
    given: str | None = typer.Option(
        None,
        "--given",
        help="Comma-separated premise identifiers for a conditional discrete observation.",
    ),
    background: str | None = typer.Option(
        None,
        "--background",
        help="Comma-separated identifiers passed as the observe() background kwarg.",
    ),
    source_refs: str | None = typer.Option(
        None,
        "--source-refs",
        help="Comma-separated source reference strings attached to the observation.",
    ),
    rationale: str | None = typer.Option(
        None, "--rationale", help="Optional natural-language justification."
    ),
    metadata: str | None = typer.Option(
        None, "--metadata", help="Optional JSON-encoded metadata dict."
    ),
    export: bool = typer.Option(
        False,
        "--export/--no-export",
        help=(
            "Add --dsl-binding-name to __all__ on a successful write. Default off: "
            "exports are the package's curated public Knowledge surface."
        ),
    ),
    check: bool = typer.Option(
        True,
        "--check/--no-check",
        help="Run post-write `gaia build check` after a successful write (default on).",
    ),
    human: bool = typer.Option(
        False, "--human", help="Render the envelope in human-readable form instead of JSON."
    ),
    interactive: bool = typer.Option(
        False, "--interactive", help="Prompt on pre-write warnings (human mode only)."
    ),
    json_: bool = typer.Option(
        True, "--json/--no-json", help="JSON-first output (default; redundant for clarity)."
    ),
) -> None:
    r"""Append an ``observe(...)`` measurement event.

    Example:
        gaia author observe --conclusion my_distribution \
            --value 203 --error 5 \
            --dsl-binding-name temperature_obs --label temperature_obs
    """
    del json_

    # --- mutual-exclusion check on observation-mode ---------------------- #
    if not _check_observation_mode_mutex(
        conclusion=conclusion,
        observation_content=observation_content,
        observation_prose=observation_prose,
        observation_label=observation_label,
        value=value,
        error=error,
        target=str(target),
        human=human,
    ):
        return

    metadata_dict, metadata_error = parse_metadata(metadata)
    if metadata_error:
        emit_syntax_error("observe", metadata_error, target=str(target), human=human)
        return

    csv_pair = _validate_given_and_background(
        given=given, background=background, target=str(target), human=human
    )
    if csv_pair is None:
        return
    given_list, background_list = csv_pair
    # ``--source-refs`` is free-form annotation text — keep the
    # permissive splitter; repr() renders the strings safely into source.
    source_refs_list = split_csv(source_refs)

    # --value / --error are spliced directly into the rendered
    # observe() call. Validate as literal-or-identifier so the postwrite
    # import can't execute crafted argv.
    val_err_refs = _validate_value_and_error(
        value=value, error=error, target=str(target), human=human
    )
    if val_err_refs is None:
        return
    rendered_value, rendered_error, references_sink = val_err_refs

    # Mutual-exclusion sanity for continuous form.
    if not _check_continuous_mutex(
        value=value,
        error=error,
        given_list=given_list,
        target=str(target),
        human=human,
    ):
        return

    # --- resolve observation mode ---------------------------------------- #
    # ``conclusion_expr`` is the Python source spelling at the call site;
    # ``references`` lists the identifiers the pre-write (c) check must
    # resolve in module scope (inline-prose contributes none of its own).
    conclusion_expr, references, observation_kind, prepended = _resolve_observation_mode(
        conclusion=conclusion,
        observation_content=observation_content,
        observation_prose=observation_prose,
        observation_label=observation_label,
        dsl_binding_name=dsl_binding_name,
        given_list=given_list,
        background_list=background_list,
    )

    # Merge value/error bare-identifier references into the verb-level
    # reference list so prewrite resolves them (and Axis 2 inserts
    # cross-file imports when ``--file <sibling>`` lands the statement
    # in a non-init module).
    references = [*references, *references_sink]

    generated_code = _render_observe_statement(
        binding_name=dsl_binding_name,
        engine_label=label,
        conclusion_expr=conclusion_expr,
        value=rendered_value,
        error=rendered_error,
        given=given_list,
        source_refs=source_refs_list,
        rationale=rationale,
        metadata=metadata_dict,
        background=background_list,
    )
    target_file = normalize_file_option(file)
    proposed_op = ProposedAuthorOp(
        verb="observe",
        kind="reasoning",
        label=dsl_binding_name,
        references=references,
        generated_code=generated_code,
        required_imports=("observe",),
        target_file=target_file,
        sibling_imports=build_sibling_imports(references, target_file=target_file),
        prepended_statements=prepended,
        extra_payload={"observation_kind": observation_kind},
        export=export,
    )
    run_author_op(
        proposed_op,
        target=target,
        human=human,
        check=check,
        interactive=interactive,
    )

parameter_command

parameter_command(label: str | None = typer.Option(None, '--label', help='Engine `label=` kwarg on the rendered parameter(...) call. Distinct from --dsl-binding-name (the Python LHS).'), dsl_binding_name: str | None = typer.Option(None, '--dsl-binding-name', help='Python LHS for the rendered statement (``<name> = parameter(...)``). Omit to emit a bare expression.'), variable: str = typer.Option(..., '--variable', help='Identifier of the bound Variable.'), value: str = typer.Option(..., '--value', help="Variable value (literal). Forwarded verbatim — pass `0.5` or `'fast'` etc."), target: str = typer.Option('.', '--target', help='Path to the target Gaia package (default: cwd).'), file: str | None = typer.Option(None, '--file', help='Relative path under src/<import_name>/ to write into. Default: `__init__.py`.'), content: str | None = typer.Option(None, '--content', help='Optional Claim content text (defaults to auto-generated).'), title: str | None = typer.Option(None, '--title', help='Optional short title for the parameter Claim.'), prior: float | None = typer.Option(None, '--prior', help='Optional inline prior in (0, 1).'), rationale: str | None = typer.Option(None, '--rationale', help='Optional natural-language justification (routed through metadata).'), metadata: str | None = typer.Option(None, '--metadata', help='Optional JSON-encoded metadata dict.'), export: bool = typer.Option(False, '--export/--no-export', help="Add --dsl-binding-name to __all__ on a successful write. Default off: exports are the package's curated public Knowledge surface."), check: bool = typer.Option(True, '--check/--no-check', help='Run post-write `gaia build check` after a successful write (default on).'), human: bool = typer.Option(False, '--human', help='Render the envelope in human-readable form instead of JSON.'), interactive: bool = typer.Option(False, '--interactive', help='Prompt on pre-write warnings (human mode only).'), json_: bool = typer.Option(True, '--json/--no-json', help='JSON-first output (default; redundant for clarity).')) -> None

Append a parameter(...) Variable-to-value binding.

Example

gaia author parameter --variable my_theta --value 0.5 \ --dsl-binding-name my_theta_default --prior 0.5

Source code in gaia/cli/commands/author/parameter.py
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
def parameter_command(
    label: str | None = typer.Option(
        None,
        "--label",
        help=(
            "Engine `label=` kwarg on the rendered parameter(...) call. "
            "Distinct from --dsl-binding-name (the Python LHS)."
        ),
    ),
    dsl_binding_name: str | None = typer.Option(
        None,
        "--dsl-binding-name",
        help=(
            "Python LHS for the rendered statement (``<name> = "
            "parameter(...)``). Omit to emit a bare expression."
        ),
    ),
    variable: str = typer.Option(..., "--variable", help="Identifier of the bound Variable."),
    value: str = typer.Option(
        ...,
        "--value",
        help="Variable value (literal). Forwarded verbatim — pass `0.5` or `'fast'` etc.",
    ),
    target: str = typer.Option(
        ".", "--target", help="Path to the target Gaia package (default: cwd)."
    ),
    file: str | None = typer.Option(
        None,
        "--file",
        help=("Relative path under src/<import_name>/ to write into. Default: `__init__.py`."),
    ),
    content: str | None = typer.Option(
        None, "--content", help="Optional Claim content text (defaults to auto-generated)."
    ),
    title: str | None = typer.Option(
        None, "--title", help="Optional short title for the parameter Claim."
    ),
    prior: float | None = typer.Option(None, "--prior", help="Optional inline prior in (0, 1)."),
    rationale: str | None = typer.Option(
        None,
        "--rationale",
        help="Optional natural-language justification (routed through metadata).",
    ),
    metadata: str | None = typer.Option(
        None, "--metadata", help="Optional JSON-encoded metadata dict."
    ),
    export: bool = typer.Option(
        False,
        "--export/--no-export",
        help=(
            "Add --dsl-binding-name to __all__ on a successful write. Default off: "
            "exports are the package's curated public Knowledge surface."
        ),
    ),
    check: bool = typer.Option(
        True,
        "--check/--no-check",
        help="Run post-write `gaia build check` after a successful write (default on).",
    ),
    human: bool = typer.Option(
        False, "--human", help="Render the envelope in human-readable form instead of JSON."
    ),
    interactive: bool = typer.Option(
        False, "--interactive", help="Prompt on pre-write warnings (human mode only)."
    ),
    json_: bool = typer.Option(
        True, "--json/--no-json", help="JSON-first output (default; redundant for clarity)."
    ),
) -> None:
    r"""Append a ``parameter(...)`` Variable-to-value binding.

    Example:
        gaia author parameter --variable my_theta --value 0.5 \
            --dsl-binding-name my_theta_default --prior 0.5
    """
    del json_

    metadata_dict, metadata_error = parse_metadata(metadata)
    if metadata_error:
        emit_syntax_error("parameter", metadata_error, target=str(target), human=human)
        return

    # --value must be a literal or a bare identifier; the rendered
    # statement splices it directly into the parameter() call and the
    # postwrite import would otherwise execute arbitrary Python. The
    # validator pushes a bare identifier into references so prewrite
    # verifies module-scope resolution.
    references: list[str] = [variable]
    try:
        _, rendered_value = parse_literal_or_identifier(value, references_sink=references)
    except PrewriteUnsafeError as exc:
        emit_syntax_error(
            "parameter",
            f"--value rejected: {exc}",
            target=str(target),
            human=human,
            kind="prewrite.expr_unsafe",
        )
        return

    generated_code = _render_parameter_statement(
        binding_name=dsl_binding_name,
        engine_label=label,
        variable=variable,
        value=rendered_value,
        content=content,
        title=title,
        prior=prior,
        rationale=rationale,
        metadata=metadata_dict,
    )
    target_file = normalize_file_option(file)
    proposed_op = ProposedAuthorOp(
        verb="parameter",
        kind="reasoning",
        label=dsl_binding_name,
        references=references,
        generated_code=generated_code,
        required_imports=("parameter",),
        target_file=target_file,
        sibling_imports=build_sibling_imports(references, target_file=target_file),
        export=export,
    )
    run_author_op(
        proposed_op,
        target=target,
        human=human,
        check=check,
        interactive=interactive,
    )

question_command

question_command(content: str = typer.Argument(..., help='Question content (natural-language research question).'), dsl_binding_name: str | None = typer.Option(None, '--dsl-binding-name', help='Python LHS for the rendered statement (``<name> = question(...)``). Omit to emit a bare expression. ``question()`` does not take an engine ``label=`` kwarg, so this is the only label-like flag the verb exposes.'), target: str = typer.Option('.', '--target', help='Path to the target Gaia package (default: cwd).'), file: str | None = typer.Option(None, '--file', help='Relative path under src/<import_name>/ to write into. Default: `__init__.py`.'), title: str | None = typer.Option(None, '--title', help='Optional short title for the question.'), targets: str | None = typer.Option(None, '--targets', help='Comma-separated identifiers of claims the question targets (must resolve in package).'), metadata: str | None = typer.Option(None, '--metadata', help='Optional JSON-encoded metadata dict.'), export: bool = typer.Option(False, '--export/--no-export', help="Add --dsl-binding-name to __all__ on a successful write. Default off: exports are the package's curated public Knowledge surface."), check: bool = typer.Option(True, '--check/--no-check', help='Run post-write `gaia build check` after a successful write (default on).'), human: bool = typer.Option(False, '--human', help='Render the envelope in human-readable form instead of JSON.'), interactive: bool = typer.Option(False, '--interactive', help='Prompt on pre-write warnings (human mode only).'), json_: bool = typer.Option(True, '--json/--no-json', help='JSON-first output (default; redundant for clarity).')) -> None

Append a question(...) research-question statement.

Example

gaia author question "Does X cause Y?" \ --dsl-binding-name rq_x_causes_y \ --targets my_hypothesis_x,my_hypothesis_y

Source code in gaia/cli/commands/author/question.py
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
def question_command(
    content: str = typer.Argument(
        ..., help="Question content (natural-language research question)."
    ),
    dsl_binding_name: str | None = typer.Option(
        None,
        "--dsl-binding-name",
        help=(
            "Python LHS for the rendered statement (``<name> = "
            "question(...)``). Omit to emit a bare expression. "
            "``question()`` does not take an engine ``label=`` kwarg, "
            "so this is the only label-like flag the verb exposes."
        ),
    ),
    target: str = typer.Option(
        ".", "--target", help="Path to the target Gaia package (default: cwd)."
    ),
    file: str | None = typer.Option(
        None,
        "--file",
        help=("Relative path under src/<import_name>/ to write into. Default: `__init__.py`."),
    ),
    title: str | None = typer.Option(
        None, "--title", help="Optional short title for the question."
    ),
    targets: str | None = typer.Option(
        None,
        "--targets",
        help=(
            "Comma-separated identifiers of claims the question targets (must resolve in package)."
        ),
    ),
    metadata: str | None = typer.Option(
        None, "--metadata", help="Optional JSON-encoded metadata dict."
    ),
    export: bool = typer.Option(
        False,
        "--export/--no-export",
        help=(
            "Add --dsl-binding-name to __all__ on a successful write. Default off: "
            "exports are the package's curated public Knowledge surface."
        ),
    ),
    check: bool = typer.Option(
        True,
        "--check/--no-check",
        help="Run post-write `gaia build check` after a successful write (default on).",
    ),
    human: bool = typer.Option(
        False, "--human", help="Render the envelope in human-readable form instead of JSON."
    ),
    interactive: bool = typer.Option(
        False, "--interactive", help="Prompt on pre-write warnings (human mode only)."
    ),
    json_: bool = typer.Option(
        True, "--json/--no-json", help="JSON-first output (default; redundant for clarity)."
    ),
) -> None:
    r"""Append a ``question(...)`` research-question statement.

    Example:
        gaia author question "Does X cause Y?" \
            --dsl-binding-name rq_x_causes_y \
            --targets my_hypothesis_x,my_hypothesis_y
    """
    del json_

    metadata_dict, metadata_error = parse_metadata(metadata)
    if metadata_error:
        emit_syntax_error("question", metadata_error, target=str(target), human=human)
        return

    target_list, target_error = split_csv_idents(targets)
    if target_error:
        emit_syntax_error(
            "question",
            f"--targets rejected: {target_error}",
            target=str(target),
            human=human,
            kind="prewrite.expr_unsafe",
        )
        return
    generated_code = _render_question_statement(
        binding_name=dsl_binding_name,
        content=content,
        title=title,
        targets=target_list,
        metadata=metadata_dict,
    )
    target_file = normalize_file_option(file)
    proposed_op = ProposedAuthorOp(
        verb="question",
        kind="reasoning",
        label=dsl_binding_name,
        references=target_list,
        generated_code=generated_code,
        required_imports=("question",),
        target_file=target_file,
        sibling_imports=build_sibling_imports(target_list, target_file=target_file),
        export=export,
    )
    run_author_op(
        proposed_op,
        target=target,
        human=human,
        check=check,
        interactive=interactive,
    )

register_prior_command

register_prior_command(claim: str = typer.Option(..., '--claim', help='Identifier of the Claim to attach the prior to.'), value: str = typer.Option(..., '--value', help='Prior probability in (CROMWELL_EPS, 1 - CROMWELL_EPS). Accepts either a numeric literal (`--value 0.5`) or a bare Python identifier (`--value PRIOR_MENDELIAN_MODEL`) resolved against the module scope so callers can reference imported constants (e.g. from a sibling `probabilities.py`). Arbitrary Python expressions are refused at the flag boundary.'), justification: str = typer.Option(..., '--justification', help='Required non-empty rationale string (engines reject empty values).'), target: str = typer.Option('.', '--target', help='Path to the target Gaia package (default: cwd).'), file: str | None = typer.Option(None, '--file', help='Relative path under src/<import_name>/ to write into (e.g. `priors.py` to match the hand-authored pattern). Default: `__init__.py`. When writing to a sibling file, the cli will auto-insert `from <import_name> import <claim>` if missing.'), source_id: str | None = typer.Option(None, '--source-id', help="Source identifier. Defaults to the engine's `user_priors`; when omitted on the cli, the rendered call omits the `source_id=` kwarg so the engine default applies (matches the hand-authored omit-when-default pattern). Engines use namespaced ids (e.g. `continuous_inference`, `reviewer_alice`)."), statement_label: str | None = typer.Option(None, '--statement-label', help='Optional trailing-comment label so tooling can scan the source line.'), metadata: str | None = typer.Option(None, '--metadata', help='Optional JSON-encoded metadata dict.'), export: bool = typer.Option(False, '--export/--no-export', help="Add the statement's binding to __all__ on a successful write (default off for register_prior: the call has no LHS binding, so this flag is reserved for surface uniformity)."), check: bool = typer.Option(True, '--check/--no-check', help='Run post-write `gaia build check` after a successful write (default on).'), human: bool = typer.Option(False, '--human', help='Render the envelope in human-readable form instead of JSON.'), interactive: bool = typer.Option(False, '--interactive', help='Prompt on pre-write warnings (human mode only).'), json_: bool = typer.Option(True, '--json/--no-json', help='JSON-first output (default; redundant for clarity).')) -> None

Append a register_prior(...) prior-registration statement.

Example

gaia author register-prior --claim my_hypothesis --value 0.7 \ --justification "Prior elicited from domain expert."

Source code in gaia/cli/commands/author/register_prior.py
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
def register_prior_command(
    claim: str = typer.Option(
        ..., "--claim", help="Identifier of the Claim to attach the prior to."
    ),
    value: str = typer.Option(
        ...,
        "--value",
        help=(
            "Prior probability in (CROMWELL_EPS, 1 - CROMWELL_EPS). Accepts "
            "either a numeric literal (`--value 0.5`) or a bare Python "
            "identifier (`--value PRIOR_MENDELIAN_MODEL`) resolved against "
            "the module scope so callers can reference imported constants "
            "(e.g. from a sibling `probabilities.py`). Arbitrary Python "
            "expressions are refused at the flag boundary."
        ),
    ),
    justification: str = typer.Option(
        ...,
        "--justification",
        help="Required non-empty rationale string (engines reject empty values).",
    ),
    target: str = typer.Option(
        ".", "--target", help="Path to the target Gaia package (default: cwd)."
    ),
    file: str | None = typer.Option(
        None,
        "--file",
        help=(
            "Relative path under src/<import_name>/ to write into (e.g. "
            "`priors.py` to match the hand-authored pattern). Default: "
            "`__init__.py`. When writing to a sibling file, the cli will "
            "auto-insert `from <import_name> import <claim>` if missing."
        ),
    ),
    source_id: str | None = typer.Option(
        None,
        "--source-id",
        help=(
            "Source identifier. Defaults to the engine's `user_priors`; "
            "when omitted on the cli, the rendered call omits the "
            "`source_id=` kwarg so the engine default applies (matches "
            "the hand-authored omit-when-default pattern). Engines use "
            "namespaced ids (e.g. `continuous_inference`, `reviewer_alice`)."
        ),
    ),
    statement_label: str | None = typer.Option(
        None,
        "--statement-label",
        help="Optional trailing-comment label so tooling can scan the source line.",
    ),
    metadata: str | None = typer.Option(
        None, "--metadata", help="Optional JSON-encoded metadata dict."
    ),
    export: bool = typer.Option(
        False,
        "--export/--no-export",
        help=(
            "Add the statement's binding to __all__ on a successful write "
            "(default off for register_prior: the call has no LHS binding, "
            "so this flag is reserved for surface uniformity)."
        ),
    ),
    check: bool = typer.Option(
        True,
        "--check/--no-check",
        help="Run post-write `gaia build check` after a successful write (default on).",
    ),
    human: bool = typer.Option(
        False, "--human", help="Render the envelope in human-readable form instead of JSON."
    ),
    interactive: bool = typer.Option(
        False, "--interactive", help="Prompt on pre-write warnings (human mode only)."
    ),
    json_: bool = typer.Option(
        True, "--json/--no-json", help="JSON-first output (default; redundant for clarity)."
    ),
) -> None:
    r"""Append a ``register_prior(...)`` prior-registration statement.

    Example:
        gaia author register-prior --claim my_hypothesis --value 0.7 \
            --justification "Prior elicited from domain expert."
    """
    del json_

    metadata_dict, metadata_error = parse_metadata(metadata)
    if metadata_error:
        emit_syntax_error("register_prior", metadata_error, target=str(target), human=human)
        return

    if not justification.strip():
        emit_syntax_error(
            "register_prior",
            "--justification must be a non-empty string",
            target=str(target),
            human=human,
        )
        return

    if not validate_identifier_flag(
        claim, verb="register_prior", flag="--claim", target=str(target), human=human
    ):
        return

    # Parse --value: either a numeric literal or a bare identifier.
    # Identifier values are pushed onto the references list so pre-write
    # invariant (c) verifies they resolve in module scope.
    extra_value_refs: list[str] = []
    try:
        _, rendered_value = parse_literal_or_identifier(
            value,
            references_sink=extra_value_refs,
        )
    except PrewriteUnsafeError as exc:
        emit_syntax_error(
            "register_prior",
            f"--value rejected: {exc}",
            target=str(target),
            human=human,
            kind="prewrite.expr_unsafe",
        )
        return

    # Emit ``source_id=`` only when the caller passed ``--source-id``
    # explicitly (any value, including ``user_priors`` if that was the
    # explicit choice). When omitted on the cli, render without the
    # kwarg so the engine default applies and the rendered statement
    # matches the hand-authored omit-when-default pattern.
    emit_source_id = source_id is not None
    effective_source_id = source_id if source_id is not None else _ENGINE_DEFAULT_SOURCE_ID
    generated_code = _render_register_prior_statement(
        claim_ref=claim,
        value=rendered_value,
        justification=justification,
        source_id=effective_source_id,
        emit_source_id=emit_source_id,
        metadata=metadata_dict,
        comment_label=statement_label,
    )
    # register_prior may target a sibling file (e.g. priors.py); the
    # shared ``build_sibling_imports`` helper wires the cross-file
    # import. Tuple is empty when target_file is None or ``__init__.py``
    # (the helper short-circuits) so the default-file behaviour is
    # unchanged.
    target_file = normalize_file_option(file)
    references = [claim, *extra_value_refs]
    proposed_op = ProposedAuthorOp(
        verb="register_prior",
        kind="reasoning",
        # No LHS binding — the call returns None and mutates the claim
        # in place. Pre-write skips the label-collision invariant when
        # label is None (see _prewrite._validate_label_collision).
        label=None,
        references=references,
        generated_code=generated_code,
        required_imports=("register_prior",),
        target_file=target_file,
        sibling_imports=build_sibling_imports(references, target_file=target_file),
        export=export,
    )
    run_author_op(
        proposed_op,
        target=target,
        human=human,
        check=check,
        interactive=interactive,
    )

variable_command

variable_command(dsl_binding_name: str | None = typer.Option(None, '--dsl-binding-name', help='Python LHS for the rendered Variable/Constant declaration (``<name> = Variable(...)``). Omit to emit a bare expression. Variable / Constant do not take an engine ``label=`` kwarg, so this is the only label-like flag the verb exposes.'), symbol: str | None = typer.Option(None, '--symbol', help='Symbol used in formulas (e.g. `k_dominant`). Required unless --const is passed (Constant has no symbol).'), domain: str = typer.Option(..., '--domain', help='Primitive type or user-declared Domain identifier. Built-in primitives: Nat / Real / Bool / Probability. A non-primitive name is treated as a user-declared Domain reference (pre-write verifies it resolves in module scope).'), target: str = typer.Option('.', '--target', help='Path to the target Gaia package (default: cwd).'), file: str | None = typer.Option(None, '--file', help='Relative path under src/<import_name>/ to write into. Default: `__init__.py`.'), value: str | None = typer.Option(None, '--value', help="Bound value: numeric literal (`--value 395`), boolean / None (`--value True`), or a bare Python identifier (`--value DOMINANT_COUNT`) resolved against the package's module scope so authors can reference imported constants (e.g. from a sibling `probabilities.py`). Required for Constant; arbitrary Python expressions are refused at the flag boundary."), content: str | None = typer.Option(None, '--content', help="Optional Variable.content override (defaults to auto-generated `'Variable <symbol>: <domain> = <value>'`)."), metadata: str | None = typer.Option(None, '--metadata', help='Optional JSON-encoded metadata dict.'), const: bool = typer.Option(False, '--const', help='Emit Constant(value, primitive) instead of Variable(...). Constant is a frozen literal term used inside formulas; it does not carry a symbol or bind a runtime value.'), export: bool = typer.Option(False, '--export/--no-export', help='Add --dsl-binding-name to __all__ on a successful write (default off for variable: typed-term declarations are internal scaffolding, not part of the public Knowledge surface).'), check: bool = typer.Option(True, '--check/--no-check', help='Run post-write `gaia build check` after a successful write (default on).'), human: bool = typer.Option(False, '--human', help='Render the envelope in human-readable form instead of JSON.'), interactive: bool = typer.Option(False, '--interactive', help='Prompt on pre-write warnings (human mode only).'), json_: bool = typer.Option(True, '--json/--no-json', help='JSON-first output (default; redundant for clarity).')) -> None

Append a Variable(...) or Constant(...) typed-term declaration.

Example

gaia author variable --symbol my_n --domain Nat --value 395 \ --dsl-binding-name my_count

Source code in gaia/cli/commands/author/variable.py
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
def variable_command(
    dsl_binding_name: str | None = typer.Option(
        None,
        "--dsl-binding-name",
        help=(
            "Python LHS for the rendered Variable/Constant declaration "
            "(``<name> = Variable(...)``). Omit to emit a bare expression. "
            "Variable / Constant do not take an engine ``label=`` kwarg, "
            "so this is the only label-like flag the verb exposes."
        ),
    ),
    symbol: str | None = typer.Option(
        None,
        "--symbol",
        help=(
            "Symbol used in formulas (e.g. `k_dominant`). Required unless "
            "--const is passed (Constant has no symbol)."
        ),
    ),
    domain: str = typer.Option(
        ...,
        "--domain",
        help=(
            "Primitive type or user-declared Domain identifier. Built-in "
            "primitives: Nat / Real / Bool / Probability. A non-primitive "
            "name is treated as a user-declared Domain reference (pre-write "
            "verifies it resolves in module scope)."
        ),
    ),
    target: str = typer.Option(
        ".", "--target", help="Path to the target Gaia package (default: cwd)."
    ),
    file: str | None = typer.Option(
        None,
        "--file",
        help=("Relative path under src/<import_name>/ to write into. Default: `__init__.py`."),
    ),
    value: str | None = typer.Option(
        None,
        "--value",
        help=(
            "Bound value: numeric literal (`--value 395`), boolean / None "
            "(`--value True`), or a bare Python identifier "
            "(`--value DOMINANT_COUNT`) resolved against the package's "
            "module scope so authors can reference imported constants "
            "(e.g. from a sibling `probabilities.py`). Required for "
            "Constant; arbitrary Python expressions are refused at the "
            "flag boundary."
        ),
    ),
    content: str | None = typer.Option(
        None,
        "--content",
        help=(
            "Optional Variable.content override (defaults to auto-generated "
            "`'Variable <symbol>: <domain> = <value>'`)."
        ),
    ),
    metadata: str | None = typer.Option(
        None, "--metadata", help="Optional JSON-encoded metadata dict."
    ),
    const: bool = typer.Option(
        False,
        "--const",
        help=(
            "Emit Constant(value, primitive) instead of Variable(...). "
            "Constant is a frozen literal term used inside formulas; it "
            "does not carry a symbol or bind a runtime value."
        ),
    ),
    export: bool = typer.Option(
        False,
        "--export/--no-export",
        help=(
            "Add --dsl-binding-name to __all__ on a successful write "
            "(default off for variable: typed-term declarations are "
            "internal scaffolding, not part of the public Knowledge surface)."
        ),
    ),
    check: bool = typer.Option(
        True,
        "--check/--no-check",
        help="Run post-write `gaia build check` after a successful write (default on).",
    ),
    human: bool = typer.Option(
        False, "--human", help="Render the envelope in human-readable form instead of JSON."
    ),
    interactive: bool = typer.Option(
        False, "--interactive", help="Prompt on pre-write warnings (human mode only)."
    ),
    json_: bool = typer.Option(
        True, "--json/--no-json", help="JSON-first output (default; redundant for clarity)."
    ),
) -> None:
    r"""Append a ``Variable(...)`` or ``Constant(...)`` typed-term declaration.

    Example:
        gaia author variable --symbol my_n --domain Nat --value 395 \
            --dsl-binding-name my_count
    """
    del json_

    if const:
        # Constant requires --value and --domain only; --symbol is invalid
        # since Constant has no symbol slot.
        if value is None:
            emit_syntax_error(
                "variable",
                "--const requires --value",
                target=str(target),
                human=human,
            )
            return
        if symbol is not None:
            emit_syntax_error(
                "variable",
                "--const is incompatible with --symbol (Constant has no symbol slot)",
                target=str(target),
                human=human,
            )
            return
    else:
        if symbol is None:
            emit_syntax_error(
                "variable",
                "--symbol is required (use --const for a Constant literal)",
                target=str(target),
                human=human,
            )
            return

    metadata_dict, metadata_error = parse_metadata(metadata)
    if metadata_error:
        emit_syntax_error("variable", metadata_error, target=str(target), human=human)
        return

    # References: the domain identifier always resolves either against
    # the standing whitelist of primitives or against a module-scope
    # Domain binding. We push the domain into pre-write's reference list
    # only when it's NOT a primitive — pre-write's symbol scan can't see
    # the built-in primitives that come from ``gaia.engine.lang``.
    references: list[str] = []
    if domain not in _PRIMITIVE_DOMAINS:
        references.append(domain)

    # Every ``--value`` must be a literal or a bare identifier. Anything
    # else (calls, dotted lookups, dunder names) is rejected at the
    # flag boundary so the splice-into-generated-source RCE vector is
    # closed. The validator pushes a bare identifier into ``references``
    # so prewrite invariant (c) verifies it resolves in module scope.
    rendered_value: str | None = None
    if value is not None:
        # Primitive domain names appear as literals/idents — keep them
        # out of the references list since the engine surfaces them as
        # built-in symbols, not module-scope bindings.
        try:
            _, rendered_value = parse_literal_or_identifier(
                value,
                references_sink=references,
            )
        except PrewriteUnsafeError as exc:
            emit_syntax_error(
                "variable",
                f"--value rejected: {exc}",
                target=str(target),
                human=human,
                kind="prewrite.expr_unsafe",
            )
            return
        # Strip primitive-domain false-positives back out of references.
        if rendered_value in _PRIMITIVE_DOMAINS and rendered_value in references:
            references.remove(rendered_value)

    generated_code = _render_variable_statement(
        binding_name=dsl_binding_name,
        symbol=symbol or "",
        domain=domain,
        value=rendered_value,
        content=content,
        metadata=metadata_dict,
        const=const,
    )

    required_imports: tuple[str, ...] = ("Constant",) if const else ("Variable",)
    if domain in _PRIMITIVE_DOMAINS:
        required_imports = (*required_imports, domain)

    target_file = normalize_file_option(file)
    proposed_op = ProposedAuthorOp(
        verb="variable",
        kind="reasoning",
        label=dsl_binding_name,
        references=references,
        generated_code=generated_code,
        required_imports=required_imports,
        target_file=target_file,
        sibling_imports=build_sibling_imports(references, target_file=target_file),
        extra_payload={"variable_kind": "const" if const else "variable"},
        export=export,
    )
    run_author_op(
        proposed_op,
        target=target,
        human=human,
        check=check,
        interactive=interactive,
    )

gaia.cli.commands.pkg.scaffold

gaia pkg scaffold — initialise a fresh Gaia knowledge package.

Maps the agent-first authoring story to package initialisation: given a target directory and a package name, lay down the minimal layout that the rest of the gaia author cycle needs (pyproject.toml with [tool.gaia] block, src/<import_name>/__init__.py template, .gaia/ artifact directory).

Why a new verb instead of reusing the legacy gaia init / gaia build init? Two reasons:

  1. Agent-first envelope. gaia init writes human-oriented text to stdout and shells out to uv; gaia pkg scaffold emits the same uniform {status, code, verb, payload, warnings, diagnostics} shape the gaia author <verb> family does, so an LLM agent can parse one envelope schema across the whole package lifecycle.
  2. Independent pre-validation surface. scaffold knows about -gaia naming, namespace defaults, and --check integration that runs gaia build check against the freshly created package.

The scaffold verb writes its own pre-validation pipeline (target directory absence + name ending + import-name validity) rather than reusing :mod:gaia.cli.commands.author._prewrite — its 4 invariants assume a pre-existing Gaia package, while scaffold creates one from scratch. The JSON envelope is shared verbatim.

scaffold_command

scaffold_command(target: str = typer.Option(..., '--target', help='Path to the directory to initialise (must be empty or non-existent). Author verbs are run from outside the package, with --target <path>.'), name: str | None = typer.Option(None, '--name', help="Package name (must end with '-gaia'); defaults to target dir name."), namespace: str | None = typer.Option(None, '--namespace', help='Package namespace; defaults to the import name.'), description: str | None = typer.Option(None, '--description', help='Short description for pyproject.toml.'), with_uuid: bool = typer.Option(False, '--with-uuid', help='Generate a [tool.gaia].uuid for the package. Default is to omit the field (matches the shipping example packages).'), docstring: str | None = typer.Option(None, '--docstring', help='Module docstring for the generated src/<import_name>/__init__.py. Wrapped in triple quotes at line 1. Default: no docstring.'), check: bool = typer.Option(False, '--check/--no-check', help='Run post-write `gaia build check` on the freshly created package. Default off — a fresh scaffold has no declarations yet, which the engine treats as an error. Re-run with `gaia build check` once author commands have added statements.'), human: bool = typer.Option(False, '--human', help='Render the envelope in human-readable form instead of JSON.'), interactive: bool = typer.Option(False, '--interactive', help='Prompt on pre-write warnings (no-op for scaffold — it emits no warnings).'), json_: bool = typer.Option(True, '--json/--no-json', help='JSON-first output (default; redundant for clarity).')) -> None

Scaffold a fresh Gaia knowledge package.

Example:

.. code-block:: bash

gaia pkg scaffold --target ./mypkg-gaia --name mypkg-gaia \
    --docstring "My package."
Source code in gaia/cli/commands/pkg/scaffold.py
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
def scaffold_command(
    target: str = typer.Option(
        ...,
        "--target",
        help=(
            "Path to the directory to initialise (must be empty or non-existent). "
            "Author verbs are run from outside the package, with --target <path>."
        ),
    ),
    name: str | None = typer.Option(
        None,
        "--name",
        help="Package name (must end with '-gaia'); defaults to target dir name.",
    ),
    namespace: str | None = typer.Option(
        None,
        "--namespace",
        help="Package namespace; defaults to the import name.",
    ),
    description: str | None = typer.Option(
        None, "--description", help="Short description for pyproject.toml."
    ),
    with_uuid: bool = typer.Option(
        False,
        "--with-uuid",
        help=(
            "Generate a [tool.gaia].uuid for the package. Default is to omit "
            "the field (matches the shipping example packages)."
        ),
    ),
    docstring: str | None = typer.Option(
        None,
        "--docstring",
        help=(
            "Module docstring for the generated src/<import_name>/__init__.py. "
            "Wrapped in triple quotes at line 1. Default: no docstring."
        ),
    ),
    check: bool = typer.Option(
        False,
        "--check/--no-check",
        help=(
            "Run post-write `gaia build check` on the freshly created package. "
            "Default off — a fresh scaffold has no declarations yet, which the "
            "engine treats as an error. Re-run with `gaia build check` once "
            "author commands have added statements."
        ),
    ),
    human: bool = typer.Option(
        False, "--human", help="Render the envelope in human-readable form instead of JSON."
    ),
    interactive: bool = typer.Option(
        False,
        "--interactive",
        help="Prompt on pre-write warnings (no-op for scaffold — it emits no warnings).",
    ),
    json_: bool = typer.Option(
        True, "--json/--no-json", help="JSON-first output (default; redundant for clarity)."
    ),
) -> None:
    r"""Scaffold a fresh Gaia knowledge package.

    Example:

    .. code-block:: bash

        gaia pkg scaffold --target ./mypkg-gaia --name mypkg-gaia \
            --docstring "My package."
    """
    del json_, interactive  # see helper-doc rationale

    target_root = Path(target).resolve()

    plan, pre_diagnostics = _validate_inputs(
        target=target_root,
        name=name,
        namespace=namespace,
        description=description,
        with_uuid=with_uuid,
        docstring=docstring,
    )
    if plan is None:
        # Pick semantic exit code from the first diagnostic kind.
        first = pre_diagnostics[0]
        code = {
            "prewrite.target_not_gaia_package": EXIT_SYSTEM_IO,
            "prewrite.target_invalid": EXIT_SYSTEM_IO,
            "prewrite.collision": EXIT_INPUT_SYNTAX,
        }.get(first.kind, EXIT_PREWRITE_STRUCTURAL)
        result = AuthorResult(
            verb="scaffold",
            status="error",
            code=code,
            payload={"target": str(target_root)},
            diagnostics=pre_diagnostics,
        )
        emit(result, human=human)
        return

    try:
        created = _scaffold_layout(plan)
    except (OSError, PermissionError) as exc:
        result = AuthorResult(
            verb="scaffold",
            status="error",
            code=EXIT_SYSTEM_IO,
            payload={"target": str(target_root)},
            diagnostics=[
                Diagnostic(
                    kind="prewrite.target_invalid",
                    level="error",
                    message=f"failed to lay out scaffold under {target_root}: {exc}",
                    source="prewrite",
                    where={"target": str(target_root)},
                )
            ],
        )
        emit(result, human=human)
        return

    post_diagnostics: list[Diagnostic] = []
    post_warnings: list[Diagnostic] = []
    counts: dict[str, int] | None = None
    if check:
        post = postwrite_check(target_root)
        post_diagnostics.extend(post.diagnostics)
        post_warnings.extend(post.warnings)
        if post.ok:
            counts = {
                "knowledge_count": post.knowledge_count,
                "strategy_count": post.strategy_count,
                "operator_count": post.operator_count,
            }

    _emit_scaffold_envelope(
        plan=plan,
        created=created,
        post_diagnostics=post_diagnostics,
        post_warnings=post_warnings,
        counts=counts,
        human=human,
    )
    if not post_diagnostics and human:
        typer.echo("\n" + _next_steps_text(plan))