Skip to content

CLI Internals API

Status: Generated from current Python docstrings and type hints.

Typer application wiring and command implementation entrypoints. User-facing CLI behavior is documented in CLI Commands; per-group structure is summarized in the CLI overview.

Alpha 0 moved the 9 historical flat verbs under grouped paths; the current v0.5 surface also includes the agent-facing author, bayes, pkg add-import, pkg add-module, and pkg scaffold commands. See Migration to alpha 0 for the old-to-new verb mapping and the package-loading helpers that moved into gaia.engine.packaging.

gaia.cli.main

Gaia CLI — knowledge package authoring toolkit.

The CLI organizes verbs into explicit top-level groups:

sdk generate the SDK reference + cheat sheet (start here, then author the DSL directly — the primary path) build init / compile / check run infer / render inspect starmap review (empty skeleton — held for downstream reviewer tooling) inquiry (sub-app: focus / review / obligation / hypothesis / tactics / reject) pkg add / add-import / add-module / register / scaffold author OPTIONAL authoring convenience (primary path: gaia sdk + write the DSL directly). claim / artifact / figure / equal / derive / note / question / contradict / exclusive / decompose / observe / compute / infer / associate / parameter / register-prior / variable / depends-on / candidate-relation / materialize / compose / composition bayes model / compare / distribution literals example galileo / mendel (print or save the cli walkthrough for a shipping v0.5 example package) trace (independent sub-app: verify / review / show)

See docs/migration.md for guidance on moving off pre-alpha-0 invocations.

Command implementations

gaia.cli.commands.init

gaia init -- scaffold a new Gaia knowledge package.

init_command

init_command(name: str = typer.Argument(help="Package name (must end with '-gaia')."), 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.')) -> None

Create a new Gaia knowledge package.

Example: gaia build init mypkg-gaia --docstring "My package."

Source code in gaia/cli/commands/init.py
 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
def init_command(
    name: str = typer.Argument(help="Package name (must end with '-gaia')."),
    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."
        ),
    ),
) -> None:
    """Create a new Gaia knowledge package.

    Example: ``gaia build init mypkg-gaia --docstring "My package."``
    """
    # --- validate name suffix ---------------------------------------------------
    if not name.endswith("-gaia"):
        suggested = f"{name}-gaia"
        typer.echo(
            f"Error: package name must end with '-gaia'. Did you mean '{suggested}'?",
            err=True,
        )
        raise typer.Exit(1)

    cwd = Path.cwd()
    pkg_dir = cwd / name

    # --- scaffold with uv init --lib -------------------------------------------
    try:
        _run_uv(["uv", "init", "--lib", name], cwd=cwd)
    except GaiaPackagingError as exc:
        typer.echo(str(exc), err=True)
        raise typer.Exit(1) from exc

    # --- compute import name early (needed for both pyproject patch and rename) --
    import_name = name.removesuffix("-gaia").replace("-", "_")

    # --- patch pyproject.toml with [tool.hatch] + [tool.gaia] sections ---------
    # First strip any ``[project] authors`` block that ``uv init`` populated from
    # local git config — public-tier scaffold leaves authors empty so users
    # don't inadvertently expose internal email domains.
    pyproject_path = pkg_dir / "pyproject.toml"
    existing_pyproject = pyproject_path.read_text()
    cleaned_pyproject = _strip_authors_from_pyproject(existing_pyproject)
    if cleaned_pyproject != existing_pyproject:
        pyproject_path.write_text(cleaned_pyproject)

    gaia_uuid = str(uuid.uuid4())
    extra_sections = (
        f"\n[tool.hatch.build.targets.wheel]\n"
        f'packages = ["src/{import_name}"]\n'
        f"\n[tool.gaia]\n"
        f'type = "knowledge-package"\n'
        f'uuid = "{gaia_uuid}"\n'
    )
    with open(pyproject_path, "a") as f:
        f.write(extra_sections)

    # --- rename src/<uv_default_name>/ → src/<import_name>/ --------------------
    uv_default_name = name.replace("-", "_")
    src_dir = pkg_dir / "src"
    uv_pkg_dir = src_dir / uv_default_name
    target_pkg_dir = src_dir / import_name

    if uv_pkg_dir.exists() and uv_pkg_dir != target_pkg_dir:
        uv_pkg_dir.rename(target_pkg_dir)
    elif not uv_pkg_dir.exists() and not target_pkg_dir.exists():
        # Fallback: create the target directory if uv didn't create expected layout
        target_pkg_dir.mkdir(parents=True, exist_ok=True)

    # --- write DSL template into __init__.py -----------------------------------
    init_py = target_pkg_dir / "__init__.py"
    if docstring is not None:
        init_py.write_text(_DSL_BODY_WITH_DOCSTRING.format(docstring=docstring))
    else:
        init_py.write_text(_DSL_BODY_NO_DOCSTRING)

    # --- create the authored/ submodule ----------------------------------------
    # Every `gaia author <verb>` write lands in authored/; the root
    # __init__.py imports it (ROOT_REEXPORT_BLOCK above).
    authored_dir = target_pkg_dir / "authored"
    authored_dir.mkdir(parents=True, exist_ok=True)
    (authored_dir / "__init__.py").write_text(AUTHORED_INIT_BODY)

    # --- append Gaia ignore patterns to .gitignore --------------------------------
    # .gaia/ir.json and .gaia/ir_hash should be tracked (registry needs them).
    # Inference outputs and dep_beliefs cache should be ignored.
    gitignore_path = pkg_dir / ".gitignore"
    gaia_ignore_patterns = [
        ".gaia/beliefs.json",
        ".gaia/dep_beliefs/",
    ]
    if gitignore_path.exists():
        existing = gitignore_path.read_text()
        # Remove overly broad ".gaia/" if uv or prior init added it
        if ".gaia/\n" in existing:
            existing = existing.replace(".gaia/\n", "")
        missing = [p for p in gaia_ignore_patterns if p not in existing]
        if missing:
            block = "\n# Gaia inference outputs (regenerated by gaia run infer)\n"
            block += "\n".join(missing) + "\n"
            with open(gitignore_path, "w") as f:
                f.write(existing.rstrip() + block)
    else:
        block = "# Gaia inference outputs (regenerated by gaia run infer)\n"
        block += "\n".join(gaia_ignore_patterns) + "\n"
        gitignore_path.write_text(block)

    # --- add gaia-lang dependency (warn on failure) ----------------------------
    try:
        _run_uv(["uv", "add", "gaia-lang"], cwd=pkg_dir)
    except GaiaPackagingError:
        typer.echo(
            f"Warning: could not add gaia-lang to {pkg_dir / 'pyproject.toml'}. "
            "Run 'uv add gaia-lang' from inside the new package directory "
            f"({pkg_dir}) to add it. "
            "This affects the new package's own Python environment — not "
            "the directory you are running gaia from. "
            "Skipping this step is fine if you plan to author with "
            "`gaia author …` from outside the package; the dependency is "
            "required only when the package is imported at runtime or "
            "compiled with `gaia build compile`.",
            err=True,
        )

    typer.echo(f"Created Gaia knowledge package: {name}")
    typer.echo(
        "\nNext steps:\n"
        f"  cd <parent of {name}>\n"
        f'  gaia author claim "..." --target ./{name}\n'
        "\n"
        f"Point every verb at the package with --target ./{name} (author verbs\n"
        f"and `gaia pkg add`) or the path argument (`gaia build compile` / "
        "`gaia run infer`).\n"
        "The same target works from the parent directory or from inside the package.\n"
        f"\n  gaia pkg add --lkm-paper <id> --target ./{name}\n"
        f"  gaia build compile ./{name}\n"
        f"  gaia run infer ./{name}"
    )

gaia.cli.commands.compile

gaia build compile -- compile Python DSL package to Gaia IR v2 JSON.

compile_command

compile_command(path: str = typer.Argument('.', help='Path to knowledge package directory')) -> None

Compile a knowledge package to .gaia/ir.json.

Loads the package's Python DSL, applies any sidecar priors (priors.py), lowers it into the canonical IR v2 JSON, runs the IR validator, and writes .gaia/ir.json + .gaia/ir_hash + .gaia/compile_metadata.json. Downstream verbs (gaia run infer, gaia run render, gaia inspect starmap, gaia pkg register) all require fresh compile artifacts.

Example:

.. code-block:: bash

gaia build compile .
Source code in gaia/cli/commands/compile.py
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
def compile_command(
    path: str = typer.Argument(".", help="Path to knowledge package directory"),
) -> None:
    """Compile a knowledge package to ``.gaia/ir.json``.

    Loads the package's Python DSL, applies any sidecar priors (``priors.py``),
    lowers it into the canonical IR v2 JSON, runs the IR validator, and
    writes ``.gaia/ir.json`` + ``.gaia/ir_hash`` + ``.gaia/compile_metadata.json``.
    Downstream verbs (``gaia run infer``, ``gaia run render``, ``gaia inspect
    starmap``, ``gaia pkg register``) all require fresh compile artifacts.

    Example:

    .. code-block:: bash

        gaia build compile .
    """
    try:
        ensure_package_env(Path(path).resolve())
        loaded = load_gaia_package(path)
        apply_package_priors(loaded)
        compiled = compile_loaded_package_artifact(loaded)
        ir = compiled.to_json()
        manifests = build_package_manifests(loaded, compiled)
    except GaiaPackagingError as exc:
        typer.echo(str(exc), err=True)
        raise typer.Exit(1) from exc

    validation = validate_local_graph(LocalCanonicalGraph(**ir))
    for warning in validation.warnings:
        typer.echo(f"Warning: {warning}")
    if validation.errors:
        for error in validation.errors:
            typer.echo(f"Error: {error}", err=True)
        raise typer.Exit(1)

    gaia_dir = write_compiled_artifacts(
        loaded.pkg_path,
        ir,
        manifests=manifests,
        formalization_manifest=compiled.formalization_manifest,
    )

    typer.echo(
        f"Compiled {len(ir['knowledges'])} knowledge, "
        f"{len(ir['strategies'])} strategies, "
        f"{len(ir['operators'])} operators"
    )
    typer.echo(f"IR hash: {ir['ir_hash'][:16]}...")
    typer.echo(f"Output: {gaia_dir / 'ir.json'}")

gaia.cli.commands.check

gaia build check -- validate a Gaia knowledge package.

check_command

check_command(path: str = typer.Argument('.', help='Path to knowledge package directory'), brief: bool = typer.Option(False, '--brief', '-b', help='Show per-module warrant brief after check'), show: str | None = typer.Option(None, '--show', '-s', help='Expand detail for a module name or claim/strategy label (implies --brief)'), hole: bool = typer.Option(False, '--hole', help='Show detailed prior review report for all independent claims'), warrants: bool = typer.Option(False, '--warrants', help='Show v6 ReviewManifest warrants with audit questions'), blind: bool = typer.Option(False, '--blind', help='With --warrants, omit status values and prior diagnostics'), inquiry: bool = typer.Option(False, '--inquiry', help='Show goal-oriented reasoning progress and review status'), gate: bool = typer.Option(False, '--gate', help='Run quality gate checks and exit non-zero on failure'), refs: bool = typer.Option(False, '--refs', help='Show reference/citation/artifact diagnostics for this package.')) -> None

Validate structure and artifact consistency for a Gaia knowledge package.

Compiles the package in-memory, validates the resulting IR (including Bayes coherence: dangling predictions, unobserved targets, prior coherence), checks that any stored .gaia/ir.json is fresh, and classifies every claim into a role bucket (independent / derived / structural / background / scaffolded / orphaned). Exits non-zero on any error diagnostic. After gaia author <verb> cycles, this is the natural quality probe before gaia build compile.

Common option combinations:

  • --brief / --show <label> — per-module warrant brief or expand a single claim/strategy
  • --hole — list independent claims with no external prior
  • --warrants / --blind — show v6 ReviewManifest warrants (use --blind to hide statuses for self-review)
  • --inquiry — render goal-oriented inquiry trees
  • --gate — apply tool.gaia.quality thresholds, exit non-zero on failure (CI-friendly)
  • --refs — show citation/local-reference/artifact diagnostics

Example:

.. code-block:: bash

gaia build check .
gaia build check . --hole
gaia build check . --refs
gaia build check . --gate
Source code in gaia/cli/commands/check.py
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
def check_command(
    path: str = typer.Argument(".", help="Path to knowledge package directory"),
    brief: bool = typer.Option(
        False, "--brief", "-b", help="Show per-module warrant brief after check"
    ),
    show: str | None = typer.Option(
        None,
        "--show",
        "-s",
        help="Expand detail for a module name or claim/strategy label (implies --brief)",
    ),
    hole: bool = typer.Option(
        False,
        "--hole",
        help="Show detailed prior review report for all independent claims",
    ),
    warrants: bool = typer.Option(
        False,
        "--warrants",
        help="Show v6 ReviewManifest warrants with audit questions",
    ),
    blind: bool = typer.Option(
        False,
        "--blind",
        help="With --warrants, omit status values and prior diagnostics",
    ),
    inquiry: bool = typer.Option(
        False,
        "--inquiry",
        help="Show goal-oriented reasoning progress and review status",
    ),
    gate: bool = typer.Option(
        False,
        "--gate",
        help="Run quality gate checks and exit non-zero on failure",
    ),
    refs: bool = typer.Option(
        False,
        "--refs",
        help="Show reference/citation/artifact diagnostics for this package.",
    ),
) -> None:
    """Validate structure and artifact consistency for a Gaia knowledge package.

    Compiles the package in-memory, validates the resulting IR (including
    Bayes coherence: dangling predictions, unobserved targets, prior
    coherence), checks that any stored ``.gaia/ir.json`` is fresh, and
    classifies every claim into a role bucket (independent / derived /
    structural / background / scaffolded / orphaned). Exits non-zero on
    any error diagnostic. After ``gaia author <verb>`` cycles, this is
    the natural quality probe before ``gaia build compile``.

    Common option combinations:

    * ``--brief`` / ``--show <label>`` — per-module warrant brief or
      expand a single claim/strategy
    * ``--hole`` — list independent claims with no external prior
    * ``--warrants`` / ``--blind`` — show v6 ReviewManifest warrants
      (use ``--blind`` to hide statuses for self-review)
    * ``--inquiry`` — render goal-oriented inquiry trees
    * ``--gate`` — apply ``tool.gaia.quality`` thresholds, exit
      non-zero on failure (CI-friendly)
    * ``--refs`` — show citation/local-reference/artifact diagnostics

    Example:

    .. code-block:: bash

        gaia build check .
        gaia build check . --hole
        gaia build check . --refs
        gaia build check . --gate
    """
    loaded, compiled, ir = _load_check_artifacts(path)
    errors, warnings = _collect_check_diagnostics(loaded, ir)
    _emit_check_diagnostics(errors, warnings)

    typer.echo(
        f"Check passed: {len(ir['knowledges'])} knowledge, "
        f"{len(ir['strategies'])} strategies, "
        f"{len(ir['operators'])} operators"
    )
    refs_report = None
    if refs:
        refs_report = _emit_refs_check_report(loaded, compiled, ir)

    review_manifest = None
    induced_summary = None
    if _check_review_manifest_needed(
        warrants=warrants,
        inquiry=inquiry,
        gate=gate,
        hole=hole,
        blind=blind,
    ):
        review_manifest = _load_check_review_manifest(loaded, compiled)

    if hole or not (warrants and blind):
        induced_summary = _check_induced_summary(ir, compiled, review_manifest)

    if review_manifest is not None:
        _emit_warrant_and_inquiry_sections(
            ir=ir,
            compiled=compiled,
            review_manifest=review_manifest,
            warrants=warrants,
            blind=blind,
            inquiry=inquiry,
        )

    if gate:
        assert review_manifest is not None
        _run_check_quality_gate(
            ir=ir,
            loaded=loaded,
            compiled=compiled,
            review_manifest=review_manifest,
            extra_failures=_refs_gate_failures(refs_report) if refs_report is not None else None,
        )

    if warrants and blind and not (brief or show or hole):
        return

    if brief or show:
        _emit_check_brief_sections(ir, brief=brief, show=show)

    _emit_prior_diagnostic_sections(
        ir=ir,
        compiled=compiled,
        induced_summary=induced_summary,
        blind=blind,
        warrants=warrants,
        hole=hole,
    )

gaia.cli.commands.infer

gaia run infer -- run BP from compiled IR with metadata priors.

infer_command

infer_command(path: str = typer.Argument('.', help='Path to knowledge package directory'), depth: int = typer.Option(0, '--depth', help='Dependency depth for joint inference. 0=flat priors (default), 1=direct deps, -1=all transitive deps.')) -> None

Run BP inference on a compiled knowledge package.

Reads fresh .gaia/ir.json (run gaia build compile first), lowers the IR into a factor graph, runs belief propagation, and writes .gaia/beliefs.json. Priors come from claim metadata (set by priors.py and reason+prior DSL pairing during compilation). Review status is qualitative and never supplies numeric priors; gaia run infer previews the compiled graph without gating unreviewed warrants. Use gaia build check --gate or gaia inquiry review for publish-quality review gating.

With --depth N (N>0), dependency packages' factor graphs are merged for joint cross-package inference instead of using flat prior injection from dep_beliefs/. --depth -1 merges all transitive deps.

Example:

.. code-block:: bash

gaia build compile .
gaia run infer .
gaia run infer . --depth 1
Source code in gaia/cli/commands/infer.py
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
def infer_command(
    path: str = typer.Argument(".", help="Path to knowledge package directory"),
    depth: int = typer.Option(
        0,
        "--depth",
        help="Dependency depth for joint inference. "
        "0=flat priors (default), 1=direct deps, -1=all transitive deps.",
    ),
) -> None:
    """Run BP inference on a compiled knowledge package.

    Reads fresh ``.gaia/ir.json`` (run ``gaia build compile`` first),
    lowers the IR into a factor graph, runs belief propagation, and
    writes ``.gaia/beliefs.json``. Priors come from claim metadata (set
    by ``priors.py`` and ``reason+prior`` DSL pairing during
    compilation). Review status is qualitative and never supplies
    numeric priors; ``gaia run infer`` previews the compiled graph
    without gating unreviewed warrants. Use ``gaia build check --gate``
    or ``gaia inquiry review`` for publish-quality review gating.

    With ``--depth N`` (N>0), dependency packages' factor graphs are
    merged for joint cross-package inference instead of using flat
    prior injection from ``dep_beliefs/``. ``--depth -1`` merges all
    transitive deps.

    Example:

    .. code-block:: bash

        gaia build compile .
        gaia run infer .
        gaia run infer . --depth 1
    """
    loaded, compiled = _load_inference_inputs(path)
    _emit_graph_validation_errors(compiled)
    compiled_json = compiled.to_json()
    _require_fresh_compile_artifacts(loaded, compiled, compiled_json)

    factor_graph = _lower_inference_graph(loaded, compiled, depth=depth)
    _validate_factor_graph(factor_graph)

    engine = InferenceEngine()
    inference_result = engine.run(factor_graph)
    result = inference_result.result

    gaia_dir = loaded.pkg_path / ".gaia"
    gaia_dir.mkdir(exist_ok=True)

    _write_json(gaia_dir / "beliefs.json", _beliefs_payload(compiled, result))

    typer.echo(f"Inferred {len(result.beliefs)} beliefs")
    method_label = inference_result.method_used.upper()
    exact_label = " (exact)" if inference_result.is_exact else ""
    treewidth_label = (
        str(inference_result.treewidth) if inference_result.treewidth >= 0 else "not computed"
    )
    typer.echo(
        f"Method: {method_label}{exact_label}, "
        f"treewidth={treewidth_label}, {inference_result.elapsed_ms:.0f}ms"
    )
    if result.diagnostics.iterations_run:
        typer.echo(
            f"Converged: {result.diagnostics.converged} "
            f"after {result.diagnostics.iterations_run} iterations"
        )
    typer.echo(f"Output: {gaia_dir / 'beliefs.json'}")
    typer.echo(
        "Note: belief = posterior probability of the claim under the "
        "auto-compiled Bayesian network; uniform priors are used by default "
        "(override via priors.py or `gaia author register-prior`)."
    )

gaia.cli.commands.render

gaia run render command for package presentation outputs.

RenderTarget

Bases: StrEnum

Supported presentation targets for gaia run render.

render_command

render_command(path: str = typer.Argument('.', help='Path to knowledge package directory'), target: RenderTarget = _TARGET_OPTION) -> None

Render presentation outputs from a compiled package.

--target docs renders docs/detailed-reasoning.md from the compiled IR alone; when gaia run infer has also been run, the output is enriched with belief and prior values. --target github strictly requires inference results and emits the .github-output/ README/wiki/data bundle. --target obsidian writes gaia-wiki/ and enriches pages with beliefs when fresh inference results are available. --target all (default) always renders docs and adds github when inference results are available, emitting a warning when they are not.

Compile freshness is required; beliefs freshness is required when present. Typical pipeline: gaia build compilegaia run infergaia run render.

Example:

.. code-block:: bash

gaia build compile .
gaia run infer .
gaia run render . --target all
gaia run render . --target obsidian
Source code in gaia/cli/commands/render.py
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
def render_command(
    path: str = typer.Argument(".", help="Path to knowledge package directory"),
    target: RenderTarget = _TARGET_OPTION,
) -> None:
    """Render presentation outputs from a compiled package.

    `--target docs` renders `docs/detailed-reasoning.md` from the compiled IR
    alone; when `gaia run infer` has also been run, the output is enriched with
    belief and prior values. `--target github` strictly requires inference
    results and emits the `.github-output/` README/wiki/data bundle.
    `--target obsidian` writes `gaia-wiki/` and enriches pages with beliefs
    when fresh inference results are available.
    `--target all` (default) always renders docs and adds github when
    inference results are available, emitting a warning when they are not.

    Compile freshness is required; beliefs freshness is required when
    present. Typical pipeline: ``gaia build compile`` → ``gaia run infer``
    → ``gaia run render``.

    Example:

    .. code-block:: bash

        gaia build compile .
        gaia run infer .
        gaia run render . --target all
        gaia run render . --target obsidian
    """
    loaded, compiled = _load_render_inputs(path)
    _emit_render_validation(compiled)
    ir = compiled.to_json()
    _require_render_artifacts_fresh(loaded, compiled, ir)

    # ── Load inference results if available ──
    # beliefs.json lives at .gaia/beliefs.json (written by `gaia run infer`).
    # If present it MUST be fresh (ir_hash matches compiled graph).
    # --target github requires beliefs; --target docs degrades gracefully.
    param_data = param_data_from_ir_metadata(ir)
    beliefs_data = _load_fresh_beliefs(loaded, compiled)
    want_docs, want_github, want_obsidian = _target_plan(target, beliefs_data)

    if want_docs:
        _write_docs_render(
            loaded=loaded,
            ir=ir,
            beliefs_data=beliefs_data,
            param_data=param_data,
        )

    if want_github:
        _write_github_render(
            loaded=loaded,
            ir=ir,
            beliefs_data=beliefs_data,
            param_data=param_data,
        )

    if want_obsidian:
        _write_obsidian_render(
            loaded=loaded,
            ir=ir,
            beliefs_data=beliefs_data,
            param_data=param_data,
        )

gaia.cli.commands.starmap

gaia inspect starmap — emit a starmap of a compiled package (HTML, DOT, or SVG).

starmap_command

starmap_command(path: str = typer.Argument('.', help='Path to knowledge package directory'), out: str = typer.Option(None, '--out', help="Output file. Defaults to '.gaia/starmap.html' (html) or '.gaia/starmap.dot' (dot), relative to the package directory; absolute paths are honored as-is."), fmt: str = typer.Option('html', '--format', help="Output format: 'html' (interactive Sigma.js), 'dot' (paper-ready Graphviz source), or 'svg' (rendered figure, stellaris glow filters baked in)."), theme: str = typer.Option('light', '--theme', help="Visual theme for 'dot' / 'svg' output. 'light' (default) is the flat paper-friendly palette. 'stellaris' (alias: 'dark') is a deep-space dark variant. For 'svg' the stellaris variant gets an injected <defs> block with radial-gradient background and glow filters bound to contradiction / support / root nodes.")) -> None

Emit a starmap of the compiled package.

Three formats are supported:

  • html (default) — single-file interactive Sigma.js visualization. Double-click to open in a browser; no server required.
  • dot — a Graphviz digraph source. Pipe through dot (Graphviz) to get a paper-ready figure. graphviz must be installed separately (brew install graphviz / apt install graphviz).
  • svg — rendered figure, end-to-end. Internally calls dot (light theme) or sfdp (stellaris/dark) on the dot source, then for the stellaris theme injects an SVG <defs> block with a radial gradient background and three glow filters keyed off class="..." markers (contradiction / support / root). Requires graphviz on PATH.

Compile freshness, beliefs freshness, and graph validation gates apply to all formats.

Examples:

Interactive HTML (default):

gaia inspect starmap path/to/pkg

DOT source (manually pipe through dot/sfdp for full control):

gaia inspect starmap path/to/pkg --format dot --out figures/starmap.dot dot -Tsvg figures/starmap.dot -o figures/starmap.svg

End-to-end paper figure (light, no glow):

gaia inspect starmap path/to/pkg --format svg --out figures/starmap.svg

End-to-end paper figure with stellaris glow defs baked in:

gaia inspect starmap path/to/pkg --format svg --theme stellaris \ --out figures/starmap_stellaris.svg

PNG preview at higher DPI from the dot source:

dot -Tpng -Gdpi=200 figures/starmap.dot -o figures/starmap.png

PDF for direct LaTeX \includegraphics inclusion:

dot -Tpdf figures/starmap.dot -o figures/starmap.pdf

Source code in gaia/cli/commands/starmap.py
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
def starmap_command(
    path: str = typer.Argument(".", help="Path to knowledge package directory"),
    out: str = typer.Option(
        None,
        "--out",
        help=(
            "Output file. Defaults to '.gaia/starmap.html' (html) or "
            "'.gaia/starmap.dot' (dot), relative to the package directory; "
            "absolute paths are honored as-is."
        ),
    ),
    fmt: str = typer.Option(
        "html",
        "--format",
        help=(
            "Output format: 'html' (interactive Sigma.js), 'dot' "
            "(paper-ready Graphviz source), or 'svg' (rendered figure, "
            "stellaris glow filters baked in)."
        ),
    ),
    theme: str = typer.Option(
        "light",
        "--theme",
        help=(
            "Visual theme for 'dot' / 'svg' output. 'light' (default) is the "
            "flat paper-friendly palette. 'stellaris' (alias: 'dark') is a "
            "deep-space dark variant. For 'svg' the stellaris variant gets "
            "an injected <defs> block with radial-gradient background and "
            "glow filters bound to contradiction / support / root nodes."
        ),
    ),
) -> None:
    r"""Emit a starmap of the compiled package.

    Three formats are supported:

    * ``html`` (default) — single-file interactive Sigma.js visualization.
      Double-click to open in a browser; no server required.
    * ``dot`` — a Graphviz ``digraph`` source. Pipe through ``dot`` (Graphviz)
      to get a paper-ready figure. ``graphviz`` must be installed separately
      (``brew install graphviz`` / ``apt install graphviz``).
    * ``svg`` — rendered figure, end-to-end. Internally calls ``dot``
      (light theme) or ``sfdp`` (stellaris/dark) on the dot source, then for
      the stellaris theme injects an SVG ``<defs>`` block with a radial
      gradient background and three glow filters keyed off ``class="..."``
      markers (contradiction / support / root). Requires ``graphviz`` on
      ``PATH``.

    Compile freshness, beliefs freshness, and graph validation gates apply to
    all formats.

    Examples:
      # Interactive HTML (default):
      gaia inspect starmap path/to/pkg

      # DOT source (manually pipe through dot/sfdp for full control):
      gaia inspect starmap path/to/pkg --format dot --out figures/starmap.dot
      dot -Tsvg figures/starmap.dot -o figures/starmap.svg

      # End-to-end paper figure (light, no glow):
      gaia inspect starmap path/to/pkg --format svg --out figures/starmap.svg

      # End-to-end paper figure with stellaris glow defs baked in:
      gaia inspect starmap path/to/pkg --format svg --theme stellaris \
          --out figures/starmap_stellaris.svg

      # PNG preview at higher DPI from the dot source:
      dot -Tpng -Gdpi=200 figures/starmap.dot -o figures/starmap.png

      # PDF for direct LaTeX \includegraphics inclusion:
      dot -Tpdf figures/starmap.dot -o figures/starmap.pdf
    """
    _validate_starmap_options(fmt, theme)
    loaded, compiled = _load_starmap_inputs(path)
    _emit_starmap_validation(compiled)
    ir = compiled.to_json()
    _require_starmap_artifacts_fresh(loaded, compiled, ir)

    # Beliefs are optional — degrade gracefully when absent. When present they
    # MUST be fresh, mirroring `render`.
    beliefs_data = _load_starmap_beliefs(loaded, compiled)
    param_data = param_data_from_ir_metadata(ir)
    exported_ids = {k["id"] for k in ir.get("knowledges", []) if k.get("exported")}

    graph_json = generate_graph_json(
        ir,
        beliefs_data=beliefs_data,
        param_data=param_data,
        exported_ids=exported_ids,
    )
    graph_payload = json.loads(graph_json)
    content = _render_starmap_content(graph_json, fmt=fmt, theme=theme)

    out_path = Path(out) if out is not None else Path(_DEFAULT_OUT[fmt])
    if not out_path.is_absolute():
        out_path = loaded.pkg_path / out_path
    out_path.parent.mkdir(parents=True, exist_ok=True)
    out_path.write_text(content, encoding="utf-8")

    node_count = len(graph_payload.get("nodes", []))
    edge_count = len(graph_payload.get("edges", []))
    typer.echo(f"Wrote starmap to {out_path} ({node_count} nodes, {edge_count} edges)")

gaia.cli.commands.add

gaia pkg add — install registered or LKM-backed Gaia packages.

LKMSourceRef dataclass

LKMSourceRef(index_id: str, kind: str, provider_id: str)

Stable source identity for an LKM-backed package candidate.

ref property

ref: str

Return the canonical LKM source ref.

add_command

add_command(package: str | None = typer.Argument(None, help='Package name (e.g., galileo-falling-bodies-gaia) or LKM ref (lkm:<index>:paper:<id> / lkm:<index>:claim:<id>).'), version: str | None = typer.Option(None, '--version', '-v', help='Specific version'), registry: str = typer.Option(DEFAULT_REGISTRY, '--registry', help='Registry GitHub repo'), lkm_index: str = typer.Option(DEFAULT_LKM_INDEX_ID, '--lkm-index', '--lkm-server', help='Configured LKM index id for --lkm-paper / --lkm-claim.'), lkm_paper: str | None = typer.Option(None, '--lkm-paper', help='Materialize this LKM paper id as a local Gaia package and add it.'), lkm_claim: str | None = typer.Option(None, '--lkm-claim', help='Resolve this LKM claim id to its backing paper package.'), target: str = typer.Option('.', '--target', help='Path to the Gaia knowledge package to add the dependency to (default: cwd). Matches the --target convention used by `gaia author` verbs so the whole package lifecycle runs from one place.')) -> None

Install a registered or LKM-backed Gaia knowledge package.

Resolves <package> against the gaia registry (default: SiliconEinstein/gaia-registry on GitHub), runs uv add on the resolved git+<repo>@<sha> spec, and best-effort downloads the upstream beliefs.json into .gaia/dep_beliefs/<import_name>.json so foreign-node priors flow into local inference. Must be run from within a Gaia knowledge package (pyproject.toml carrying a tool.gaia table).

LKM paper refs/flags fetch the paper graph, generate a local Gaia package under .gaia/lkm_packages/, compile it, and add it as an editable dependency with uv add --editable.

--version pins a specific release; omit to take the latest registered version.

Example:

.. code-block:: bash

gaia pkg add galileo-falling-bodies-gaia
gaia pkg add mendel-v0-5-gaia --version 0.1.0
gaia pkg add --lkm-index bohrium --lkm-paper 811827932371615744
gaia pkg add --lkm-index bohrium --lkm-claim gcn_579430355a0e4bbd
gaia pkg add lkm:bohrium:paper:811827932371615744
Source code in gaia/cli/commands/add.py
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 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
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
def add_command(
    package: str | None = typer.Argument(
        None,
        help=(
            "Package name (e.g., galileo-falling-bodies-gaia) or LKM ref "
            "(lkm:<index>:paper:<id> / lkm:<index>:claim:<id>)."
        ),
    ),
    version: str | None = typer.Option(None, "--version", "-v", help="Specific version"),
    registry: str = typer.Option(DEFAULT_REGISTRY, "--registry", help="Registry GitHub repo"),
    lkm_index: str = typer.Option(
        DEFAULT_LKM_INDEX_ID,
        "--lkm-index",
        "--lkm-server",
        help="Configured LKM index id for --lkm-paper / --lkm-claim.",
    ),
    lkm_paper: str | None = typer.Option(
        None,
        "--lkm-paper",
        help="Materialize this LKM paper id as a local Gaia package and add it.",
    ),
    lkm_claim: str | None = typer.Option(
        None,
        "--lkm-claim",
        help="Resolve this LKM claim id to its backing paper package.",
    ),
    target: str = typer.Option(
        ".",
        "--target",
        help=(
            "Path to the Gaia knowledge package to add the dependency to "
            "(default: cwd). Matches the --target convention used by "
            "`gaia author` verbs so the whole package lifecycle runs from "
            "one place."
        ),
    ),
) -> None:
    """Install a registered or LKM-backed Gaia knowledge package.

    Resolves ``<package>`` against the gaia registry (default:
    ``SiliconEinstein/gaia-registry`` on GitHub), runs ``uv add`` on the
    resolved ``git+<repo>@<sha>`` spec, and best-effort downloads the
    upstream ``beliefs.json`` into ``.gaia/dep_beliefs/<import_name>.json``
    so foreign-node priors flow into local inference. Must be run from
    within a Gaia knowledge package (``pyproject.toml`` carrying a
    ``tool.gaia`` table).

    LKM paper refs/flags fetch the paper graph, generate a local Gaia package
    under ``.gaia/lkm_packages/``, compile it, and add it as an editable
    dependency with ``uv add --editable``.

    ``--version`` pins a specific release; omit to take the latest
    registered version.

    Example:

    .. code-block:: bash

        gaia pkg add galileo-falling-bodies-gaia
        gaia pkg add mendel-v0-5-gaia --version 0.1.0
        gaia pkg add --lkm-index bohrium --lkm-paper 811827932371615744
        gaia pkg add --lkm-index bohrium --lkm-claim gcn_579430355a0e4bbd
        gaia pkg add lkm:bohrium:paper:811827932371615744
    """
    try:
        lkm_ref = _resolve_lkm_source_ref(
            package,
            lkm_index=lkm_index,
            lkm_paper=lkm_paper,
            lkm_claim=lkm_claim,
        )
    except GaiaPackagingError as exc:
        typer.echo(f"Error: {exc}", err=True)
        raise typer.Exit(4) from exc
    # Resolve the consumer package once, honoring --target (default cwd). The
    # same --target convention used by `gaia author` verbs / `gaia init` /
    # `gaia pkg scaffold` works here; --target . preserves the historical
    # "run from inside the package" behavior by walking up to the nearest root.
    package_root = _resolve_package_root(target)

    if lkm_ref is not None:
        _handle_lkm_source_add(lkm_ref, package_root=package_root)
        return
    if package is None:
        typer.echo("Error: pass PACKAGE or an LKM source flag.", err=True)
        raise typer.Exit(4)
    _warn_unused_lkm_index(lkm_index)

    try:
        resolved = resolve_package(package, version=version, registry=registry)
    except GaiaPackagingError as exc:
        typer.echo(str(exc), err=True)
        raise typer.Exit(1) from exc

    # Normalize: ensure -gaia suffix for the dep spec
    canonical_name = package if package.endswith("-gaia") else f"{package}-gaia"
    dep_spec = f"{canonical_name} @ git+{resolved.repo}@{resolved.git_sha}"
    typer.echo(f"Resolved {package} v{resolved.version}{resolved.git_sha[:8]}")

    # `uv add` runs in the resolved package root when one was found; otherwise
    # it falls back to the process cwd (matching the prior behavior for the
    # registry path, which uv resolves against the nearest project).
    uv_cwd = package_root if package_root is not None else None
    try:
        result = _run_uv(["uv", "add", dep_spec], cwd=uv_cwd)
    except GaiaPackagingError as exc:
        typer.echo(str(exc), err=True)
        raise typer.Exit(1) from exc
    if result.returncode != 0:
        stderr = result.stderr.strip() or result.stdout.strip()
        typer.echo(f"Error: uv add failed: {stderr}", err=True)
        raise typer.Exit(1)

    typer.echo(f"Added {package} v{resolved.version}")

    # Download upstream beliefs manifest for foreign-node prior injection.
    # This is best-effort: older registry entries may not have beliefs.json.
    if package_root is None:
        typer.echo("Note: not inside a Gaia package; skipping dep_beliefs download")
    else:
        _fetch_dep_beliefs(
            package_name=canonical_name.removesuffix("-gaia"),
            version=resolved.version,
            registry=registry,
            pkg_root=package_root,
        )

gaia.cli.commands.register

gaia pkg register -- prepare or submit a registry registration for a Gaia package.

register_command

register_command(path: str = typer.Argument('.', help='Path to knowledge package directory'), tag: str | None = typer.Option(None, help='Git tag to register. Defaults to v<version>.'), repo: str | None = typer.Option(None, help='GitHub repository URL. Defaults to the git origin remote.'), registry_dir: str | None = typer.Option(None, help='Path to a local checkout of the official registry repository.'), registry_repo: str = typer.Option('SiliconEinstein/gaia-registry', help='Registry GitHub repo slug for PR creation.'), create_pr: bool = typer.Option(False, help='Push the registry branch and open a GitHub PR.')) -> None

Prepare or submit a registration for a tagged GitHub-backed Gaia package.

Reads the freshly-compiled package, validates git state (clean worktree, tag pointing at HEAD, tag pushed to origin), builds the registry-side Package.toml / Versions.toml / Deps.toml + per-release manifests + beliefs.json, and either prints the registration plan as JSON (default) or writes it into a local checkout of the registry repo and opens a PR.

Preconditions checked: [project].name ends with -gaia, [tool.gaia].uuid set to a valid UUID, .gaia/ir_hash matches the freshly-compiled IR (run gaia build compile first), git worktree clean, tag v<version> (or --tag) on HEAD and pushed.

Example:

.. code-block:: bash

# Dry run — print the registration plan as JSON:
gaia pkg register .

# Write a registry branch into a local checkout and open a PR:
gaia pkg register . --registry-dir ~/dev/gaia-registry --create-pr
Source code in gaia/cli/commands/register.py
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
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
740
741
def register_command(
    path: str = typer.Argument(".", help="Path to knowledge package directory"),
    tag: str | None = typer.Option(None, help="Git tag to register. Defaults to v<version>."),
    repo: str | None = typer.Option(
        None, help="GitHub repository URL. Defaults to the git origin remote."
    ),
    registry_dir: str | None = typer.Option(
        None, help="Path to a local checkout of the official registry repository."
    ),
    registry_repo: str = typer.Option(
        "SiliconEinstein/gaia-registry", help="Registry GitHub repo slug for PR creation."
    ),
    create_pr: bool = typer.Option(False, help="Push the registry branch and open a GitHub PR."),
) -> None:
    """Prepare or submit a registration for a tagged GitHub-backed Gaia package.

    Reads the freshly-compiled package, validates git state (clean
    worktree, tag pointing at HEAD, tag pushed to origin), builds the
    registry-side ``Package.toml`` / ``Versions.toml`` / ``Deps.toml`` +
    per-release manifests + ``beliefs.json``, and either prints the
    registration plan as JSON (default) or writes it into a local
    checkout of the registry repo and opens a PR.

    Preconditions checked: ``[project].name`` ends with ``-gaia``,
    ``[tool.gaia].uuid`` set to a valid UUID, ``.gaia/ir_hash`` matches
    the freshly-compiled IR (run ``gaia build compile`` first), git
    worktree clean, tag ``v<version>`` (or ``--tag``) on HEAD and
    pushed.

    Example:

    .. code-block:: bash

        # Dry run — print the registration plan as JSON:
        gaia pkg register .

        # Write a registry branch into a local checkout and open a PR:
        gaia pkg register . --registry-dir ~/dev/gaia-registry --create-pr
    """
    loaded, compiled, ir, manifests = _load_registration_artifacts(path)
    _validate_registration_ir(ir)
    gaia_uuid, _dependencies = _validate_registration_package_config(loaded, ir)
    package_name = loaded.project_name.removesuffix("-gaia")
    version = loaded.project_config["version"]
    tag_name = tag or f"v{version}"
    repo_url, tag_sha = _validated_registration_git_state(loaded, tag_name=tag_name, repo=repo)
    plan, deps, versions, _deps_payload = _build_registration_plan(
        loaded=loaded,
        compiled=compiled,
        ir=ir,
        manifests=manifests,
        gaia_uuid=gaia_uuid,
        tag_name=tag_name,
        tag_sha=tag_sha,
        repo_url=repo_url,
        registry_repo=registry_repo,
    )

    if registry_dir is None:
        typer.echo(json.dumps(plan, ensure_ascii=False, indent=2, sort_keys=True))
        return

    registry_path, package_dir, release_path, versions_toml_path, deps_toml_path, branch_name = (
        _validate_registry_target(registry_dir, package_name, version, gaia_uuid)
    )
    _write_registry_registration(
        registry_path=registry_path,
        package_dir=package_dir,
        release_path=release_path,
        versions_toml_path=versions_toml_path,
        deps_toml_path=deps_toml_path,
        branch_name=branch_name,
        loaded=loaded,
        package_name=package_name,
        version=version,
        plan=plan,
        deps=deps,
        versions=versions,
    )
    typer.echo(f"Prepared registry branch: {branch_name}")
    typer.echo(f"Updated metadata under: {package_dir}")
    _maybe_create_registry_pr(
        registry_path=registry_path,
        branch_name=branch_name,
        registry_repo=registry_repo,
        pr_title=plan["pull_request"]["title"],
        pr_body=plan["pull_request"]["body"],
        create_pr=create_pr,
    )

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.bayes

gaia bayes subcommand group — Bayesian-modelling cli surface.

Exposes :mod:gaia.engine.bayes through structured cli verbs. Mirrors the engine's authoring surface:

  • bayes model — declare a predictive model linking a hypothesis Claim to a Variable observable and a Distribution.
  • bayes compare — compare observed data against one or more predictive models.
  • bayes <dist> — declare a Distribution literal as a standalone binding so subsequent bayes model / observe invocations can reference it by name. <dist> is one of the 11 v0.5 shipping distributions (Binomial / BetaBinomial / Normal / LogNormal / Beta / Exponential / Gamma / StudentT / Cauchy / ChiSquared / Poisson).

Namespace choice: top-level gaia bayes <verb>. The engine's bayes surface is a coherent sub-domain (its own gaia.engine.bayes package), so giving it a top-level cli group mirrors the engine organisation. bayes model / bayes compare reads naturally; flat-under-author would have produced author bayes-model which felt forced.

All bayes verbs share the same JSON envelope + pre-write + post-write pipeline as the gaia author family via the runner in :mod:gaia.cli.commands.author._runner. The distribution-literal verbs build a Distribution(...) binding; the structural verbs model and compare produce helper Claim bindings the same way the engine's bayes.model / bayes.compare do.

compare_command

compare_command(label: str = typer.Option(..., '--label', help='Identifier the comparison helper Claim binds to.'), data: str = typer.Option(..., '--data', help='Comma-separated identifier(s) of observation Claim(s).'), model: str = typer.Option(..., '--model', help='Identifier of the primary bayes.model helper Claim.'), against: str | None = typer.Option(None, '--against', help='Comma-separated identifier(s) of alternative model Claim(s); at least one required.'), exclusivity: str = typer.Option('exhaustive_pairwise_complement', '--exclusivity', help="Structural constraint between the compared hypotheses. exhaustive_pairwise_complement (default): the hypotheses partition the outcome space — exactly one must be true; posterior mass flows pairwise as in standard Bayesian model selection (suitable when the listed models are collectively exhaustive). pairwise_contradiction: the hypotheses are mutually exclusive but not exhaustive — at most one is true; an 'all hypotheses false' joint state is permitted (use when you believe all listed models may be wrong)."), 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`.'), background: str | None = typer.Option(None, '--background', help='Comma-separated identifiers passed as the 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.'), 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

Author a bayes.compare(data, models=[model, ...]) statement.

bayes compare is the quantitative culmination of a qualitative authoring graph (claims, observations, derivations, exclusive operators). For a self-contained two-prior posterior comparison without a qualitative argument, libraries like PyMC or scipy.stats may be a lighter fit.

Example:

.. code-block:: bash

gaia bayes compare \
    --data f2_count_observation \
    --model mendel_count_model \
    --against diffuse_count_model \
    --rationale "Compare Mendel vs diffuse on F2 counts." \
    --label mendel_count_likelihood
Source code in gaia/cli/commands/bayes/compare.py
 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
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
def compare_command(
    label: str = typer.Option(
        ..., "--label", help="Identifier the comparison helper Claim binds to."
    ),
    data: str = typer.Option(
        ...,
        "--data",
        help="Comma-separated identifier(s) of observation Claim(s).",
    ),
    model: str = typer.Option(
        ...,
        "--model",
        help="Identifier of the primary bayes.model helper Claim.",
    ),
    against: str | None = typer.Option(
        None,
        "--against",
        help="Comma-separated identifier(s) of alternative model Claim(s); at least one required.",
    ),
    exclusivity: str = typer.Option(
        "exhaustive_pairwise_complement",
        "--exclusivity",
        help=(
            "Structural constraint between the compared hypotheses. "
            "exhaustive_pairwise_complement (default): the hypotheses "
            "partition the outcome space — exactly one must be true; "
            "posterior mass flows pairwise as in standard Bayesian "
            "model selection (suitable when the listed models are "
            "collectively exhaustive). "
            "pairwise_contradiction: the hypotheses are mutually "
            "exclusive but not exhaustive — at most one is true; an "
            "'all hypotheses false' joint state is permitted (use "
            "when you believe all listed models may be wrong)."
        ),
    ),
    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`."),
    ),
    background: str | None = typer.Option(
        None,
        "--background",
        help="Comma-separated identifiers passed as the 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."
    ),
    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"""Author a ``bayes.compare(data, models=[model, ...])`` statement.

    ``bayes compare`` is the quantitative culmination of a qualitative
    authoring graph (claims, observations, derivations, exclusive
    operators). For a self-contained two-prior posterior comparison
    without a qualitative argument, libraries like PyMC or
    scipy.stats may be a lighter fit.

    Example:

    .. code-block:: bash

        gaia bayes compare \
            --data f2_count_observation \
            --model mendel_count_model \
            --against diffuse_count_model \
            --rationale "Compare Mendel vs diffuse on F2 counts." \
            --label mendel_count_likelihood
    """
    del json_

    if exclusivity not in _EXCLUSIVITY_VALUES:
        allowed = ", ".join(sorted(_EXCLUSIVITY_VALUES))
        emit_syntax_error(
            "bayes.compare",
            f"--exclusivity must be one of: {allowed} (got {exclusivity!r})",
            target=str(target),
            human=human,
        )
        return

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

    if not validate_identifier_flag(
        model, verb="bayes.compare", flag="--model", target=str(target), human=human
    ):
        return
    data_list, data_error = split_csv_idents(data)
    if data_error:
        emit_syntax_error(
            "bayes.compare",
            f"--data rejected: {data_error}",
            target=str(target),
            human=human,
            kind="prewrite.expr_unsafe",
        )
        return
    if not data_list:
        emit_syntax_error(
            "bayes.compare",
            "--data must list at least one observation identifier",
            target=str(target),
            human=human,
        )
        return
    against_list, against_error = split_csv_idents(against)
    if against_error:
        emit_syntax_error(
            "bayes.compare",
            f"--against rejected: {against_error}",
            target=str(target),
            human=human,
            kind="prewrite.expr_unsafe",
        )
        return
    if not against_list:
        emit_syntax_error(
            "bayes.compare",
            "--against must list at least one alternative model",
            target=str(target),
            human=human,
        )
        return
    background_list, background_error = split_csv_idents(background)
    if background_error:
        emit_syntax_error(
            "bayes.compare",
            f"--background rejected: {background_error}",
            target=str(target),
            human=human,
            kind="prewrite.expr_unsafe",
        )
        return

    generated_code = _render_compare_statement(
        label=label,
        data=data_list,
        model=model,
        against=against_list,
        background=background_list,
        rationale=rationale,
        exclusivity=exclusivity,
        metadata=metadata_dict,
    )
    references = [*data_list, model, *against_list, *background_list]
    target_file = normalize_file_option(file)
    proposed_op = ProposedAuthorOp(
        verb="bayes.compare",
        kind="reasoning",
        label=label,
        references=references,
        generated_code=generated_code,
        required_imports=("bayes",),
        target_file=target_file,
        sibling_imports=build_sibling_imports(references, target_file=target_file),
    )
    run_author_op(
        proposed_op,
        target=target,
        human=human,
        check=check,
        interactive=interactive,
    )

beta_command

beta_command(label: str = typer.Option(..., '--label', help='Identifier the Beta binding takes.'), alpha: str = typer.Option(..., '--alpha', help='Shape alpha (>0): literal or identifier.'), beta: str = typer.Option(..., '--beta', help='Shape beta (>0): literal or identifier.'), target: str = typer.Option('.', '--target', help='Target package path.'), file: str | None = typer.Option(None, '--file', help='Relative file under src/<pkg>/.'), metadata: str | None = typer.Option(None, '--metadata', help='JSON metadata.'), check: bool = typer.Option(True, '--check/--no-check'), human: bool = typer.Option(False, '--human'), interactive: bool = typer.Option(False, '--interactive')) -> None

Declare a Beta(content, alpha=..., beta=...) literal binding.

Renders Beta(alpha, beta) on (0, 1) — the canonical conjugate prior for Bernoulli / Binomial success rates. alpha=1, beta=1 is uniform. Each parameter accepts a numeric literal or a bare module-scope identifier.

Example:

.. code-block:: bash

gaia bayes beta --label success_rate_prior --alpha 2.0 --beta 5.0
Source code in gaia/cli/commands/bayes/distributions.py
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
def beta_command(
    label: str = typer.Option(..., "--label", help="Identifier the Beta binding takes."),
    alpha: str = typer.Option(..., "--alpha", help="Shape alpha (>0): literal or identifier."),
    beta: str = typer.Option(..., "--beta", help="Shape beta (>0): literal or identifier."),
    target: str = typer.Option(".", "--target", help="Target package path."),
    file: str | None = typer.Option(None, "--file", help="Relative file under src/<pkg>/."),
    metadata: str | None = typer.Option(None, "--metadata", help="JSON metadata."),
    check: bool = typer.Option(True, "--check/--no-check"),
    human: bool = typer.Option(False, "--human"),
    interactive: bool = typer.Option(False, "--interactive"),
) -> None:
    r"""Declare a ``Beta(content, alpha=..., beta=...)`` literal binding.

    Renders Beta(alpha, beta) on (0, 1) — the canonical conjugate prior
    for Bernoulli / Binomial success rates. ``alpha=1, beta=1`` is
    uniform. Each parameter accepts a numeric literal or a bare
    module-scope identifier.

    Example:

    .. code-block:: bash

        gaia bayes beta --label success_rate_prior --alpha 2.0 --beta 5.0
    """
    metadata_dict, metadata_error = parse_metadata(metadata)
    if metadata_error:
        emit_syntax_error("bayes.Beta", metadata_error, target=str(target), human=human)
        return
    _run_dist_op(
        label=label,
        dist_name="Beta",
        params=[("alpha", alpha), ("beta", beta)],
        target=target,
        file=file,
        human=human,
        check=check,
        interactive=interactive,
        metadata_dict=metadata_dict,
    )

betabinomial_command

betabinomial_command(label: str = typer.Option(..., '--label', help='Identifier the BetaBinomial binding takes.'), n: str = typer.Option(..., '--n', help='Trial count: numeric literal or bare identifier.'), alpha: str = typer.Option(..., '--alpha', help='Beta prior alpha (>0): literal or identifier.'), beta: str = typer.Option(..., '--beta', help='Beta prior beta (>0): literal or identifier.'), target: str = typer.Option('.', '--target', help='Target package path.'), file: str | None = typer.Option(None, '--file', help='Relative file under src/<pkg>/.'), metadata: str | None = typer.Option(None, '--metadata', help='JSON metadata.'), check: bool = typer.Option(True, '--check/--no-check'), human: bool = typer.Option(False, '--human'), interactive: bool = typer.Option(False, '--interactive')) -> None

Declare a BetaBinomial(content, n=..., alpha=..., beta=...) literal.

Renders the BetaBinomial(n, alpha, beta) predictive distribution as a standalone binding; common as the diffuse / uniform-mean reference model in a likelihood comparison (alpha=1, beta=1 collapses to the uniform 1/(n+1) over counts). Each parameter accepts a numeric literal or a bare module-scope identifier.

Example:

.. code-block:: bash

gaia bayes beta-binomial --label diffuse_binomial \\
    --n 395 --alpha 1.0 --beta 1.0
Source code in gaia/cli/commands/bayes/distributions.py
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
def betabinomial_command(
    label: str = typer.Option(..., "--label", help="Identifier the BetaBinomial binding takes."),
    n: str = typer.Option(..., "--n", help="Trial count: numeric literal or bare identifier."),
    alpha: str = typer.Option(..., "--alpha", help="Beta prior alpha (>0): literal or identifier."),
    beta: str = typer.Option(..., "--beta", help="Beta prior beta (>0): literal or identifier."),
    target: str = typer.Option(".", "--target", help="Target package path."),
    file: str | None = typer.Option(None, "--file", help="Relative file under src/<pkg>/."),
    metadata: str | None = typer.Option(None, "--metadata", help="JSON metadata."),
    check: bool = typer.Option(True, "--check/--no-check"),
    human: bool = typer.Option(False, "--human"),
    interactive: bool = typer.Option(False, "--interactive"),
) -> None:
    r"""Declare a ``BetaBinomial(content, n=..., alpha=..., beta=...)`` literal.

    Renders the BetaBinomial(n, alpha, beta) predictive distribution as
    a standalone binding; common as the diffuse / uniform-mean reference
    model in a likelihood comparison (``alpha=1, beta=1`` collapses to
    the uniform 1/(n+1) over counts). Each parameter accepts a numeric
    literal or a bare module-scope identifier.

    Example:

    .. code-block:: bash

        gaia bayes beta-binomial --label diffuse_binomial \\
            --n 395 --alpha 1.0 --beta 1.0
    """
    metadata_dict, metadata_error = parse_metadata(metadata)
    if metadata_error:
        emit_syntax_error("bayes.BetaBinomial", metadata_error, target=str(target), human=human)
        return
    _run_dist_op(
        label=label,
        dist_name="BetaBinomial",
        params=[("n", n), ("alpha", alpha), ("beta", beta)],
        target=target,
        file=file,
        human=human,
        check=check,
        interactive=interactive,
        metadata_dict=metadata_dict,
    )

binomial_command

binomial_command(label: str = typer.Option(..., '--label', help='Identifier the Binomial binding takes.'), n: str = typer.Option(..., '--n', help='Trial count: numeric literal or bare identifier.'), p: str = typer.Option(..., '--p', help='Success probability: numeric literal or bare identifier.'), target: str = typer.Option('.', '--target', help='Target package path.'), file: str | None = typer.Option(None, '--file', help='Relative file under src/<pkg>/.'), metadata: str | None = typer.Option(None, '--metadata', help='JSON metadata.'), check: bool = typer.Option(True, '--check/--no-check'), human: bool = typer.Option(False, '--human'), interactive: bool = typer.Option(False, '--interactive')) -> None

Declare a Binomial(content, n=..., p=...) literal binding.

Renders <label> = Binomial('<label>', n=..., p=...) into the target module. --n and --p accept a numeric literal (--n 395, --p 0.75) or a bare module-scope identifier (--n TOTAL_COUNT, --p MENDELIAN_DOMINANT_PROBABILITY); each identifier must already be importable in the target file.

Example:

.. code-block:: bash

gaia bayes binomial --label mendel_binomial --n 395 --p 0.75
Source code in gaia/cli/commands/bayes/distributions.py
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
def binomial_command(
    label: str = typer.Option(..., "--label", help="Identifier the Binomial binding takes."),
    n: str = typer.Option(..., "--n", help="Trial count: numeric literal or bare identifier."),
    p: str = typer.Option(
        ..., "--p", help="Success probability: numeric literal or bare identifier."
    ),
    target: str = typer.Option(".", "--target", help="Target package path."),
    file: str | None = typer.Option(None, "--file", help="Relative file under src/<pkg>/."),
    metadata: str | None = typer.Option(None, "--metadata", help="JSON metadata."),
    check: bool = typer.Option(True, "--check/--no-check"),
    human: bool = typer.Option(False, "--human"),
    interactive: bool = typer.Option(False, "--interactive"),
) -> None:
    r"""Declare a ``Binomial(content, n=..., p=...)`` literal binding.

    Renders ``<label> = Binomial('<label>', n=..., p=...)`` into the target
    module. ``--n`` and ``--p`` accept a numeric literal (``--n 395``,
    ``--p 0.75``) or a bare module-scope identifier (``--n TOTAL_COUNT``,
    ``--p MENDELIAN_DOMINANT_PROBABILITY``); each identifier must already
    be importable in the target file.

    Example:

    .. code-block:: bash

        gaia bayes binomial --label mendel_binomial --n 395 --p 0.75
    """
    metadata_dict, metadata_error = parse_metadata(metadata)
    if metadata_error:
        emit_syntax_error("bayes.Binomial", metadata_error, target=str(target), human=human)
        return
    _run_dist_op(
        label=label,
        dist_name="Binomial",
        params=[("n", n), ("p", p)],
        target=target,
        file=file,
        human=human,
        check=check,
        interactive=interactive,
        metadata_dict=metadata_dict,
    )

cauchy_command

cauchy_command(label: str = typer.Option(..., '--label', help='Identifier the Cauchy binding takes.'), mu: str = typer.Option(..., '--mu', help='Location parameter.'), gamma: str = typer.Option(..., '--gamma', help='Scale parameter (>0).'), target: str = typer.Option('.', '--target', help='Target package path.'), file: str | None = typer.Option(None, '--file', help='Relative file under src/<pkg>/.'), metadata: str | None = typer.Option(None, '--metadata', help='JSON metadata.'), check: bool = typer.Option(True, '--check/--no-check'), human: bool = typer.Option(False, '--human'), interactive: bool = typer.Option(False, '--interactive')) -> None

Declare a Cauchy(content, mu=..., gamma=...) literal binding.

Renders Cauchy(mu, gamma) — undefined mean / variance; useful as a heavy-tailed robust alternative to Normal. Each parameter accepts a numeric literal or a bare module-scope identifier.

Example:

.. code-block:: bash

gaia bayes cauchy --label heavy_tail_noise --mu 0.0 --gamma 1.0
Source code in gaia/cli/commands/bayes/distributions.py
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
def cauchy_command(
    label: str = typer.Option(..., "--label", help="Identifier the Cauchy binding takes."),
    mu: str = typer.Option(..., "--mu", help="Location parameter."),
    gamma: str = typer.Option(..., "--gamma", help="Scale parameter (>0)."),
    target: str = typer.Option(".", "--target", help="Target package path."),
    file: str | None = typer.Option(None, "--file", help="Relative file under src/<pkg>/."),
    metadata: str | None = typer.Option(None, "--metadata", help="JSON metadata."),
    check: bool = typer.Option(True, "--check/--no-check"),
    human: bool = typer.Option(False, "--human"),
    interactive: bool = typer.Option(False, "--interactive"),
) -> None:
    r"""Declare a ``Cauchy(content, mu=..., gamma=...)`` literal binding.

    Renders Cauchy(mu, gamma) — undefined mean / variance; useful as a
    heavy-tailed robust alternative to Normal. Each parameter accepts a
    numeric literal or a bare module-scope identifier.

    Example:

    .. code-block:: bash

        gaia bayes cauchy --label heavy_tail_noise --mu 0.0 --gamma 1.0
    """
    metadata_dict, metadata_error = parse_metadata(metadata)
    if metadata_error:
        emit_syntax_error("bayes.Cauchy", metadata_error, target=str(target), human=human)
        return
    _run_dist_op(
        label=label,
        dist_name="Cauchy",
        params=[("mu", mu), ("gamma", gamma)],
        target=target,
        file=file,
        human=human,
        check=check,
        interactive=interactive,
        metadata_dict=metadata_dict,
    )

chisquared_command

chisquared_command(label: str = typer.Option(..., '--label', help='Identifier the ChiSquared binding takes.'), df: str = typer.Option(..., '--df', help='Degrees of freedom (>0).'), target: str = typer.Option('.', '--target', help='Target package path.'), file: str | None = typer.Option(None, '--file', help='Relative file under src/<pkg>/.'), metadata: str | None = typer.Option(None, '--metadata', help='JSON metadata.'), check: bool = typer.Option(True, '--check/--no-check'), human: bool = typer.Option(False, '--human'), interactive: bool = typer.Option(False, '--interactive')) -> None

Declare a ChiSquared(content, df=...) literal binding.

Renders ChiSquared(df) on positive reals — sum of squared standard Normals with df degrees of freedom. --df accepts a numeric literal or a bare module-scope identifier.

Example:

.. code-block:: bash

gaia bayes chi-squared --label variance_test_stat --df 5
Source code in gaia/cli/commands/bayes/distributions.py
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
def chisquared_command(
    label: str = typer.Option(..., "--label", help="Identifier the ChiSquared binding takes."),
    df: str = typer.Option(..., "--df", help="Degrees of freedom (>0)."),
    target: str = typer.Option(".", "--target", help="Target package path."),
    file: str | None = typer.Option(None, "--file", help="Relative file under src/<pkg>/."),
    metadata: str | None = typer.Option(None, "--metadata", help="JSON metadata."),
    check: bool = typer.Option(True, "--check/--no-check"),
    human: bool = typer.Option(False, "--human"),
    interactive: bool = typer.Option(False, "--interactive"),
) -> None:
    r"""Declare a ``ChiSquared(content, df=...)`` literal binding.

    Renders ChiSquared(df) on positive reals — sum of squared standard
    Normals with ``df`` degrees of freedom. ``--df`` accepts a numeric
    literal or a bare module-scope identifier.

    Example:

    .. code-block:: bash

        gaia bayes chi-squared --label variance_test_stat --df 5
    """
    metadata_dict, metadata_error = parse_metadata(metadata)
    if metadata_error:
        emit_syntax_error("bayes.ChiSquared", metadata_error, target=str(target), human=human)
        return
    _run_dist_op(
        label=label,
        dist_name="ChiSquared",
        params=[("df", df)],
        target=target,
        file=file,
        human=human,
        check=check,
        interactive=interactive,
        metadata_dict=metadata_dict,
    )

exponential_command

exponential_command(label: str = typer.Option(..., '--label', help='Identifier the Exponential binding takes.'), rate: str = typer.Option(..., '--rate', help='Rate (>0): literal or identifier.'), target: str = typer.Option('.', '--target', help='Target package path.'), file: str | None = typer.Option(None, '--file', help='Relative file under src/<pkg>/.'), metadata: str | None = typer.Option(None, '--metadata', help='JSON metadata.'), check: bool = typer.Option(True, '--check/--no-check'), human: bool = typer.Option(False, '--human'), interactive: bool = typer.Option(False, '--interactive')) -> None

Declare a Exponential(content, rate=...) literal binding.

Renders the single-parameter Exponential(rate) distribution on positive reals — mean 1/rate. --rate accepts a numeric literal or a bare module-scope identifier.

Example:

.. code-block:: bash

gaia bayes exponential --label decay_time --rate 0.5
Source code in gaia/cli/commands/bayes/distributions.py
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
def exponential_command(
    label: str = typer.Option(..., "--label", help="Identifier the Exponential binding takes."),
    rate: str = typer.Option(..., "--rate", help="Rate (>0): literal or identifier."),
    target: str = typer.Option(".", "--target", help="Target package path."),
    file: str | None = typer.Option(None, "--file", help="Relative file under src/<pkg>/."),
    metadata: str | None = typer.Option(None, "--metadata", help="JSON metadata."),
    check: bool = typer.Option(True, "--check/--no-check"),
    human: bool = typer.Option(False, "--human"),
    interactive: bool = typer.Option(False, "--interactive"),
) -> None:
    r"""Declare a ``Exponential(content, rate=...)`` literal binding.

    Renders the single-parameter Exponential(rate) distribution on
    positive reals — mean ``1/rate``. ``--rate`` accepts a numeric
    literal or a bare module-scope identifier.

    Example:

    .. code-block:: bash

        gaia bayes exponential --label decay_time --rate 0.5
    """
    metadata_dict, metadata_error = parse_metadata(metadata)
    if metadata_error:
        emit_syntax_error("bayes.Exponential", metadata_error, target=str(target), human=human)
        return
    _run_dist_op(
        label=label,
        dist_name="Exponential",
        params=[("rate", rate)],
        target=target,
        file=file,
        human=human,
        check=check,
        interactive=interactive,
        metadata_dict=metadata_dict,
    )

gamma_command

gamma_command(label: str = typer.Option(..., '--label', help='Identifier the Gamma binding takes.'), alpha: str = typer.Option(..., '--alpha', help='Shape parameter (>0).'), rate: str = typer.Option(..., '--rate', help='Rate parameter (>0).'), target: str = typer.Option('.', '--target', help='Target package path.'), file: str | None = typer.Option(None, '--file', help='Relative file under src/<pkg>/.'), metadata: str | None = typer.Option(None, '--metadata', help='JSON metadata.'), check: bool = typer.Option(True, '--check/--no-check'), human: bool = typer.Option(False, '--human'), interactive: bool = typer.Option(False, '--interactive')) -> None

Declare a Gamma(content, alpha=..., rate=...) literal binding.

Renders Gamma(alpha, rate) on positive reals — mean alpha/rate. Common as a conjugate prior for Poisson rates. Each parameter accepts a numeric literal or a bare module-scope identifier.

Example:

.. code-block:: bash

gaia bayes gamma --label rate_prior --alpha 2.0 --rate 0.5
Source code in gaia/cli/commands/bayes/distributions.py
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
def gamma_command(
    label: str = typer.Option(..., "--label", help="Identifier the Gamma binding takes."),
    alpha: str = typer.Option(..., "--alpha", help="Shape parameter (>0)."),
    rate: str = typer.Option(..., "--rate", help="Rate parameter (>0)."),
    target: str = typer.Option(".", "--target", help="Target package path."),
    file: str | None = typer.Option(None, "--file", help="Relative file under src/<pkg>/."),
    metadata: str | None = typer.Option(None, "--metadata", help="JSON metadata."),
    check: bool = typer.Option(True, "--check/--no-check"),
    human: bool = typer.Option(False, "--human"),
    interactive: bool = typer.Option(False, "--interactive"),
) -> None:
    r"""Declare a ``Gamma(content, alpha=..., rate=...)`` literal binding.

    Renders Gamma(alpha, rate) on positive reals — mean ``alpha/rate``.
    Common as a conjugate prior for Poisson rates. Each parameter accepts
    a numeric literal or a bare module-scope identifier.

    Example:

    .. code-block:: bash

        gaia bayes gamma --label rate_prior --alpha 2.0 --rate 0.5
    """
    metadata_dict, metadata_error = parse_metadata(metadata)
    if metadata_error:
        emit_syntax_error("bayes.Gamma", metadata_error, target=str(target), human=human)
        return
    _run_dist_op(
        label=label,
        dist_name="Gamma",
        params=[("alpha", alpha), ("rate", rate)],
        target=target,
        file=file,
        human=human,
        check=check,
        interactive=interactive,
        metadata_dict=metadata_dict,
    )

lognormal_command

lognormal_command(label: str = typer.Option(..., '--label', help='Identifier the LogNormal binding takes.'), mu: str = typer.Option(..., '--mu', help='Location parameter of the underlying Normal.'), sigma: str = typer.Option(..., '--sigma', help='Scale of the underlying Normal (>0).'), target: str = typer.Option('.', '--target', help='Target package path.'), file: str | None = typer.Option(None, '--file', help='Relative file under src/<pkg>/.'), metadata: str | None = typer.Option(None, '--metadata', help='JSON metadata.'), check: bool = typer.Option(True, '--check/--no-check'), human: bool = typer.Option(False, '--human'), interactive: bool = typer.Option(False, '--interactive')) -> None

Declare a LogNormal(content, mu=..., sigma=...) literal binding.

Renders LogNormal(mu, sigma) — the distribution of exp(X) for X ~ Normal(mu, sigma). Common for positively-supported multiplicative noise. Each parameter accepts a numeric literal or a bare module-scope identifier.

Example:

.. code-block:: bash

gaia bayes log-normal --label price_noise --mu 0.0 --sigma 0.3
Source code in gaia/cli/commands/bayes/distributions.py
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
def lognormal_command(
    label: str = typer.Option(..., "--label", help="Identifier the LogNormal binding takes."),
    mu: str = typer.Option(..., "--mu", help="Location parameter of the underlying Normal."),
    sigma: str = typer.Option(..., "--sigma", help="Scale of the underlying Normal (>0)."),
    target: str = typer.Option(".", "--target", help="Target package path."),
    file: str | None = typer.Option(None, "--file", help="Relative file under src/<pkg>/."),
    metadata: str | None = typer.Option(None, "--metadata", help="JSON metadata."),
    check: bool = typer.Option(True, "--check/--no-check"),
    human: bool = typer.Option(False, "--human"),
    interactive: bool = typer.Option(False, "--interactive"),
) -> None:
    r"""Declare a ``LogNormal(content, mu=..., sigma=...)`` literal binding.

    Renders LogNormal(mu, sigma) — the distribution of ``exp(X)`` for
    ``X ~ Normal(mu, sigma)``. Common for positively-supported
    multiplicative noise. Each parameter accepts a numeric literal or a
    bare module-scope identifier.

    Example:

    .. code-block:: bash

        gaia bayes log-normal --label price_noise --mu 0.0 --sigma 0.3
    """
    metadata_dict, metadata_error = parse_metadata(metadata)
    if metadata_error:
        emit_syntax_error("bayes.LogNormal", metadata_error, target=str(target), human=human)
        return
    _run_dist_op(
        label=label,
        dist_name="LogNormal",
        params=[("mu", mu), ("sigma", sigma)],
        target=target,
        file=file,
        human=human,
        check=check,
        interactive=interactive,
        metadata_dict=metadata_dict,
    )

normal_command

normal_command(label: str = typer.Option(..., '--label', help='Identifier the Normal binding takes.'), mu: str = typer.Option(..., '--mu', help='Location parameter: literal or identifier.'), sigma: str = typer.Option(..., '--sigma', help='Scale parameter (>0): literal or identifier.'), target: str = typer.Option('.', '--target', help='Target package path.'), file: str | None = typer.Option(None, '--file', help='Relative file under src/<pkg>/.'), metadata: str | None = typer.Option(None, '--metadata', help='JSON metadata.'), check: bool = typer.Option(True, '--check/--no-check'), human: bool = typer.Option(False, '--human'), interactive: bool = typer.Option(False, '--interactive')) -> None

Declare a Normal(content, mu=..., sigma=...) literal binding.

Renders the Normal(mu, sigma) distribution as a standalone binding. Each parameter accepts a numeric literal or a bare module-scope identifier.

Example:

.. code-block:: bash

gaia bayes normal --label measurement_noise --mu 0.0 --sigma 1.5
Source code in gaia/cli/commands/bayes/distributions.py
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
def normal_command(
    label: str = typer.Option(..., "--label", help="Identifier the Normal binding takes."),
    mu: str = typer.Option(..., "--mu", help="Location parameter: literal or identifier."),
    sigma: str = typer.Option(..., "--sigma", help="Scale parameter (>0): literal or identifier."),
    target: str = typer.Option(".", "--target", help="Target package path."),
    file: str | None = typer.Option(None, "--file", help="Relative file under src/<pkg>/."),
    metadata: str | None = typer.Option(None, "--metadata", help="JSON metadata."),
    check: bool = typer.Option(True, "--check/--no-check"),
    human: bool = typer.Option(False, "--human"),
    interactive: bool = typer.Option(False, "--interactive"),
) -> None:
    r"""Declare a ``Normal(content, mu=..., sigma=...)`` literal binding.

    Renders the Normal(mu, sigma) distribution as a standalone binding.
    Each parameter accepts a numeric literal or a bare module-scope
    identifier.

    Example:

    .. code-block:: bash

        gaia bayes normal --label measurement_noise --mu 0.0 --sigma 1.5
    """
    metadata_dict, metadata_error = parse_metadata(metadata)
    if metadata_error:
        emit_syntax_error("bayes.Normal", metadata_error, target=str(target), human=human)
        return
    _run_dist_op(
        label=label,
        dist_name="Normal",
        params=[("mu", mu), ("sigma", sigma)],
        target=target,
        file=file,
        human=human,
        check=check,
        interactive=interactive,
        metadata_dict=metadata_dict,
    )

poisson_command

poisson_command(label: str = typer.Option(..., '--label', help='Identifier the Poisson binding takes.'), rate: str = typer.Option(..., '--rate', help='Poisson rate (>0): literal or identifier.'), target: str = typer.Option('.', '--target', help='Target package path.'), file: str | None = typer.Option(None, '--file', help='Relative file under src/<pkg>/.'), metadata: str | None = typer.Option(None, '--metadata', help='JSON metadata.'), check: bool = typer.Option(True, '--check/--no-check'), human: bool = typer.Option(False, '--human'), interactive: bool = typer.Option(False, '--interactive')) -> None

Declare a Poisson(content, rate=...) literal binding.

Renders the single-parameter Poisson(rate) distribution as a standalone binding. --rate accepts a numeric literal or a bare module-scope identifier (e.g. an expected-count constant).

Example:

.. code-block:: bash

gaia bayes poisson --label arrival_rate_model --rate 4.2
Source code in gaia/cli/commands/bayes/distributions.py
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
def poisson_command(
    label: str = typer.Option(..., "--label", help="Identifier the Poisson binding takes."),
    rate: str = typer.Option(..., "--rate", help="Poisson rate (>0): literal or identifier."),
    target: str = typer.Option(".", "--target", help="Target package path."),
    file: str | None = typer.Option(None, "--file", help="Relative file under src/<pkg>/."),
    metadata: str | None = typer.Option(None, "--metadata", help="JSON metadata."),
    check: bool = typer.Option(True, "--check/--no-check"),
    human: bool = typer.Option(False, "--human"),
    interactive: bool = typer.Option(False, "--interactive"),
) -> None:
    r"""Declare a ``Poisson(content, rate=...)`` literal binding.

    Renders the single-parameter Poisson(rate) distribution as a
    standalone binding. ``--rate`` accepts a numeric literal or a bare
    module-scope identifier (e.g. an expected-count constant).

    Example:

    .. code-block:: bash

        gaia bayes poisson --label arrival_rate_model --rate 4.2
    """
    metadata_dict, metadata_error = parse_metadata(metadata)
    if metadata_error:
        emit_syntax_error("bayes.Poisson", metadata_error, target=str(target), human=human)
        return
    _run_dist_op(
        label=label,
        dist_name="Poisson",
        params=[("rate", rate)],
        target=target,
        file=file,
        human=human,
        check=check,
        interactive=interactive,
        metadata_dict=metadata_dict,
    )

studentt_command

studentt_command(label: str = typer.Option(..., '--label', help='Identifier the StudentT binding takes.'), df: str = typer.Option(..., '--df', help='Degrees of freedom (>0).'), mu: str = typer.Option('0.0', '--mu', help='Location parameter (default 0.0).'), sigma: str = typer.Option('1.0', '--sigma', help='Scale parameter (default 1.0, >0).'), target: str = typer.Option('.', '--target', help='Target package path.'), file: str | None = typer.Option(None, '--file', help='Relative file under src/<pkg>/.'), metadata: str | None = typer.Option(None, '--metadata', help='JSON metadata.'), check: bool = typer.Option(True, '--check/--no-check'), human: bool = typer.Option(False, '--human'), interactive: bool = typer.Option(False, '--interactive')) -> None

Declare a StudentT(content, df=..., mu=..., sigma=...) literal binding.

Renders Student's t-distribution. --mu and --sigma are optional location/scale (defaults 0.0 / 1.0); --df (degrees of freedom) is required. Heavier-tailed than Normal; useful for robust-likelihood comparisons.

Example:

.. code-block:: bash

gaia bayes student-t --label robust_noise --df 4.0 --mu 0.0 --sigma 1.0
Source code in gaia/cli/commands/bayes/distributions.py
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
def studentt_command(
    label: str = typer.Option(..., "--label", help="Identifier the StudentT binding takes."),
    df: str = typer.Option(..., "--df", help="Degrees of freedom (>0)."),
    mu: str = typer.Option("0.0", "--mu", help="Location parameter (default 0.0)."),
    sigma: str = typer.Option("1.0", "--sigma", help="Scale parameter (default 1.0, >0)."),
    target: str = typer.Option(".", "--target", help="Target package path."),
    file: str | None = typer.Option(None, "--file", help="Relative file under src/<pkg>/."),
    metadata: str | None = typer.Option(None, "--metadata", help="JSON metadata."),
    check: bool = typer.Option(True, "--check/--no-check"),
    human: bool = typer.Option(False, "--human"),
    interactive: bool = typer.Option(False, "--interactive"),
) -> None:
    r"""Declare a ``StudentT(content, df=..., mu=..., sigma=...)`` literal binding.

    Renders Student's t-distribution. ``--mu`` and ``--sigma`` are
    optional location/scale (defaults 0.0 / 1.0); ``--df`` (degrees of
    freedom) is required. Heavier-tailed than Normal; useful for
    robust-likelihood comparisons.

    Example:

    .. code-block:: bash

        gaia bayes student-t --label robust_noise --df 4.0 --mu 0.0 --sigma 1.0
    """
    metadata_dict, metadata_error = parse_metadata(metadata)
    if metadata_error:
        emit_syntax_error("bayes.StudentT", metadata_error, target=str(target), human=human)
        return
    _run_dist_op(
        label=label,
        dist_name="StudentT",
        params=[("df", df), ("mu", mu), ("sigma", sigma)],
        target=target,
        file=file,
        human=human,
        check=check,
        interactive=interactive,
        metadata_dict=metadata_dict,
    )

model_command

model_command(label: str = typer.Option(..., '--label', help='Identifier the predictive-model helper Claim binds to.'), hypothesis: str = typer.Option(..., '--hypothesis', help='Identifier of the hypothesis Claim being modelled.'), observable: str = typer.Option(..., '--observable', help='Identifier of the Variable the model predicts.'), distribution: str = typer.Option(..., '--distribution', help="Distribution binding. Accepts either (a) a bare identifier of a Distribution binding (created via `bayes binomial` / `bayes normal` / ...) — resolved in module scope, or (b) an inline Distribution expression like `Binomial('k under H', n=395, p=3/4)` — validated via the formula sandbox and emitted verbatim into the `distribution=` slot."), 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`.'), background: str | None = typer.Option(None, '--background', help='Comma-separated identifiers passed as the 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.'), references: str | None = typer.Option(None, '--references', help='Comma-separated identifiers to whitelist inside the formula sandbox when --distribution carries an inline expression referencing module-scope constants (e.g. `--distribution "Binomial(\'k under H\', n=TOTAL_COUNT, p=MENDELIAN_DOMINANT_PROBABILITY)" --references TOTAL_COUNT,MENDELIAN_DOMINANT_PROBABILITY`). Each name is also pushed into pre-write reference resolution so module-scope binding is verified. No effect when --distribution is a bare identifier.'), 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

Author a bayes.model(hypothesis, observable=..., distribution=...) statement.

Example:

.. code-block:: bash

gaia bayes model \
    --hypothesis mendelian_segregation_model \
    --observable f2_dominant_count \
    --distribution mendel_binomial \
    --background monohybrid_cross_setup,dominance_background \
    --rationale "Mendel predicts Binomial(N, 3/4) for F2 dominant counts." \
    --label mendel_count_model
Source code in gaia/cli/commands/bayes/model.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
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
def model_command(
    label: str = typer.Option(
        ..., "--label", help="Identifier the predictive-model helper Claim binds to."
    ),
    hypothesis: str = typer.Option(
        ...,
        "--hypothesis",
        help="Identifier of the hypothesis Claim being modelled.",
    ),
    observable: str = typer.Option(
        ...,
        "--observable",
        help="Identifier of the Variable the model predicts.",
    ),
    distribution: str = typer.Option(
        ...,
        "--distribution",
        help=(
            "Distribution binding. Accepts either (a) a bare identifier "
            "of a Distribution binding (created via `bayes binomial` / "
            "`bayes normal` / ...) — resolved in module scope, or "
            "(b) an inline Distribution expression like "
            "`Binomial('k under H', n=395, p=3/4)` — validated via the formula "
            "sandbox and emitted verbatim into the `distribution=` slot."
        ),
    ),
    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`."),
    ),
    background: str | None = typer.Option(
        None,
        "--background",
        help="Comma-separated identifiers passed as the 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."
    ),
    references: str | None = typer.Option(
        None,
        "--references",
        help=(
            "Comma-separated identifiers to whitelist inside the formula "
            "sandbox when --distribution carries an inline expression "
            "referencing module-scope constants (e.g. `--distribution "
            "\"Binomial('k under H', n=TOTAL_COUNT, p=MENDELIAN_DOMINANT_PROBABILITY)\" "
            "--references TOTAL_COUNT,MENDELIAN_DOMINANT_PROBABILITY`). "
            "Each name is also pushed into pre-write reference resolution "
            "so module-scope binding is verified. No effect when "
            "--distribution is a bare identifier."
        ),
    ),
    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"""Author a ``bayes.model(hypothesis, observable=..., distribution=...)`` statement.

    Example:

    .. code-block:: bash

        gaia bayes model \
            --hypothesis mendelian_segregation_model \
            --observable f2_dominant_count \
            --distribution mendel_binomial \
            --background monohybrid_cross_setup,dominance_background \
            --rationale "Mendel predicts Binomial(N, 3/4) for F2 dominant counts." \
            --label mendel_count_model
    """
    del json_

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

    # Axis 1 identifier-shape gates on --hypothesis / --observable.
    if not validate_identifier_flag(
        hypothesis, verb="bayes.model", flag="--hypothesis", target=str(target), human=human
    ):
        return
    if not validate_identifier_flag(
        observable, verb="bayes.model", flag="--observable", target=str(target), human=human
    ):
        return

    # Detect inline Distribution expression vs bare identifier.
    # Bare identifier → push into references for pre-write resolution.
    # Inline expression (anything with parentheses or attribute syntax) →
    # validate via the formula sandbox (which whitelists Distribution
    # factories imported from gaia.engine.lang) and skip the
    # reference-resolution path for the distribution itself.
    references_list, references_error = split_csv_idents(references)
    if references_error:
        emit_syntax_error(
            "bayes.model",
            f"--references rejected: {references_error}",
            target=str(target),
            human=human,
            kind="prewrite.expr_unsafe",
        )
        return

    distribution_is_inline = not _BARE_IDENTIFIER_RE.match(distribution)
    if distribution_is_inline:
        try:
            validate_formula_expr(distribution, extra_names=frozenset(references_list))
        except FormulaSandboxError as exc:
            emit_syntax_error(
                "bayes.model",
                f"--distribution rejected by sandbox: {exc}",
                target=str(target),
                human=human,
                kind="prewrite.expr_unsafe",
            )
            return
        # Semantic-type check on the inline shape:
        # ``ast.parse(mode="eval")`` and require Call / Attribute /
        # Name. A bare literal (``1`` / ``"foo"`` / ``[1, 2]``) fails
        # the engine at load time; reject it here at the flag boundary
        # with a precise error.
        import ast as _ast

        try:
            tree = _ast.parse(distribution, mode="eval")
        except SyntaxError as exc:
            emit_syntax_error(
                "bayes.model",
                f"--distribution is not a valid expression: {exc.msg}",
                target=str(target),
                human=human,
                kind="prewrite.expr_unsafe",
            )
            return
        root = tree.body
        if not isinstance(root, (_ast.Call, _ast.Attribute, _ast.Name)):
            emit_syntax_error(
                "bayes.model",
                (
                    f"--distribution must be a Distribution factory call or "
                    f"reference (got {type(root).__name__}); literal values "
                    "are not Distributions"
                ),
                target=str(target),
                human=human,
                kind="prewrite.expr_unsafe",
            )
            return

    background_list, background_error = split_csv_idents(background)
    if background_error:
        emit_syntax_error(
            "bayes.model",
            f"--background rejected: {background_error}",
            target=str(target),
            human=human,
            kind="prewrite.expr_unsafe",
        )
        return
    generated_code = _render_model_statement(
        label=label,
        hypothesis=hypothesis,
        observable=observable,
        distribution=distribution,
        background=background_list,
        rationale=rationale,
        metadata=metadata_dict,
    )
    all_references = [hypothesis, observable, *background_list, *references_list]
    required_imports: tuple[str, ...] = ("bayes",)
    if not distribution_is_inline:
        all_references.insert(2, distribution)
    else:
        required_imports = ("bayes", *extract_engine_lang_names(distribution))
    target_file = normalize_file_option(file)
    proposed_op = ProposedAuthorOp(
        verb="bayes.model",
        kind="reasoning",
        label=label,
        references=all_references,
        generated_code=generated_code,
        # ``bayes`` and any inline Distribution factories must be importable
        # in the target file. The writer keeps those imports narrow and
        # idempotent, which lets the scaffold stay minimal.
        required_imports=required_imports,
        target_file=target_file,
        sibling_imports=build_sibling_imports(all_references, target_file=target_file),
    )
    run_author_op(
        proposed_op,
        target=target,
        human=human,
        check=check,
        interactive=interactive,
    )

gaia.cli.commands.pkg.add_import

gaia pkg add-import — inject from <module> import <names> into a target file.

Plain-Python imports between sibling modules of a Gaia knowledge package are a precondition the author verbs can't handle on their own. When gaia author variable --value DOMINANT_COUNT is invoked against __init__.py, the cli has no way to know DOMINANT_COUNT lives in ./probabilities.py — the per-verb sibling_imports machinery short-circuits whenever the target file is __init__.py since a package can't re-import from itself.

This verb closes that gap with a small, explicit utility: it inserts from .<module> import <names> (or from <dotted.module> import <names>) into the target file, idempotently. Each name that already appears in any matching from line is silently skipped; everything new is folded into a single import line, alphabetically sorted, placed after the docstring + from __future__ block.

The verb covers the "plain Python data plumbing" gap surfaced when reproducing example packages whose sibling modules carry plain numeric constants (probabilities.py) or NamedTuple helpers — content that isn't DSL and so isn't author-verb material, yet still needs to be referenced from __init__.py.

add_import_command

add_import_command(from_: str = typer.Option(..., '--from', help="Module to import from. A bare identifier (`probabilities`) or leading-dot form (`.probabilities`) resolves against the target package's import name as `<import_name>.probabilities`. A dotted absolute form (`other_pkg.helpers`) is used verbatim."), names: str = typer.Option(..., '--names', help='Comma-separated Python identifiers to import. Each must be a valid identifier; duplicates and entries already imported are silently skipped.'), target: str = typer.Option('.', '--target', help='Path to the target Gaia package (default: cwd).'), file: str = typer.Option('__init__.py', '--file', help='Relative path under src/<import_name>/ to write into. Default: `__init__.py`. The file must already exist; use `gaia pkg add-module` first if you need to seed a fresh sibling.'), human: bool = typer.Option(False, '--human', help='Render the envelope in human-readable form instead of JSON.'), json_: bool = typer.Option(True, '--json/--no-json', help='JSON-first output (default; redundant for clarity).')) -> None

Inject from <module> import <names> into a Gaia package file.

Example:

.. code-block:: bash

gaia pkg add-import --from probabilities \
    --names DOMINANT_COUNT,RECESSIVE_COUNT,TOTAL_COUNT
Source code in gaia/cli/commands/pkg/add_import.py
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
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
def add_import_command(
    from_: str = typer.Option(
        ...,
        "--from",
        help=(
            "Module to import from. A bare identifier (`probabilities`) or "
            "leading-dot form (`.probabilities`) resolves against the target "
            "package's import name as `<import_name>.probabilities`. A dotted "
            "absolute form (`other_pkg.helpers`) is used verbatim."
        ),
    ),
    names: str = typer.Option(
        ...,
        "--names",
        help=(
            "Comma-separated Python identifiers to import. Each must be a "
            "valid identifier; duplicates and entries already imported are "
            "silently skipped."
        ),
    ),
    target: str = typer.Option(
        ".", "--target", help="Path to the target Gaia package (default: cwd)."
    ),
    file: str = typer.Option(
        "__init__.py",
        "--file",
        help=(
            "Relative path under src/<import_name>/ to write into. Default: "
            "`__init__.py`. The file must already exist; use `gaia pkg "
            "add-module` first if you need to seed a fresh sibling."
        ),
    ),
    human: bool = typer.Option(
        False, "--human", help="Render the envelope in human-readable form instead of JSON."
    ),
    json_: bool = typer.Option(
        True, "--json/--no-json", help="JSON-first output (default; redundant for clarity)."
    ),
) -> None:
    r"""Inject ``from <module> import <names>`` into a Gaia package file.

    Example:

    .. code-block:: bash

        gaia pkg add-import --from probabilities \
            --names DOMINANT_COUNT,RECESSIVE_COUNT,TOTAL_COUNT
    """
    del json_

    target_root = Path(target).resolve()

    # Validate the names CSV before structural checks so syntax errors
    # short-circuit on the cheap path.
    raw_names = [item.strip() for item in names.split(",") if item.strip()]
    if not raw_names:
        emit(
            AuthorResult(
                verb="add_import",
                status="error",
                code=EXIT_INPUT_SYNTAX,
                payload={"target": str(target_root)},
                diagnostics=[
                    Diagnostic(
                        kind="prewrite.syntax",
                        level="error",
                        message="--names must list at least one identifier",
                        source="prewrite",
                    )
                ],
            ),
            human=human,
        )
        return
    for n in raw_names:
        if not _is_valid_identifier(n):
            emit(
                AuthorResult(
                    verb="add_import",
                    status="error",
                    code=EXIT_INPUT_SYNTAX,
                    payload={"target": str(target_root)},
                    diagnostics=[
                        Diagnostic(
                            kind="prewrite.syntax",
                            level="error",
                            message=(f"--names entry {n!r} is not a valid Python identifier"),
                            source="prewrite",
                        )
                    ],
                ),
                human=human,
            )
            return

    # Pre-write target-structure invariant (a) — reuse the prewrite_check
    # probe so we share the toml validation + source-root discovery with
    # the rest of the family.
    probe_op = ProposedAuthorOp(
        verb="add_import",
        kind="scaffold",
        label=None,
        references=[],
        generated_code="pass\n",
        required_imports=(),
    )
    pre = prewrite_check(target_root, probe_op)
    if not pre.ok:
        emit(
            AuthorResult(
                verb="add_import",
                status="error",
                code=pre.exit_code,
                payload={"target": str(target_root)},
                diagnostics=pre.diagnostics,
            ),
            human=human,
        )
        return

    assert pre.source_root is not None
    source_root = pre.source_root
    import_name = pre.import_name or ""

    resolved_module, resolve_err = _resolve_module(from_, default_package=import_name or None)
    if resolve_err is not None:
        emit(
            AuthorResult(
                verb="add_import",
                status="error",
                code=EXIT_INPUT_SYNTAX,
                payload={"target": str(target_root)},
                diagnostics=[
                    Diagnostic(
                        kind="prewrite.syntax",
                        level="error",
                        message=resolve_err,
                        source="prewrite",
                    )
                ],
            ),
            human=human,
        )
        return
    assert resolved_module is not None

    target_file_rel = file.strip().lstrip("./")
    if not target_file_rel:
        target_file_rel = "__init__.py"
    if not target_file_rel.endswith(".py"):
        target_file_rel = f"{target_file_rel}.py"
    target_path = source_root / target_file_rel
    if not target_path.exists():
        emit(
            AuthorResult(
                verb="add_import",
                status="error",
                code=EXIT_PREWRITE_STRUCTURAL,
                payload={
                    "target": str(target_root),
                    "file": str(target_path),
                },
                diagnostics=[
                    Diagnostic(
                        kind="prewrite.target_invalid",
                        level="error",
                        message=(
                            f"target file {target_path} does not exist; run "
                            "`gaia pkg add-module --name <name>` first to "
                            "scaffold a sibling module"
                        ),
                        source="prewrite",
                    )
                ],
            ),
            human=human,
        )
        return

    needed: tuple[tuple[str, str], ...] = tuple((n, resolved_module) for n in raw_names)
    try:
        source = target_path.read_text()
    except OSError as exc:
        emit(
            AuthorResult(
                verb="add_import",
                status="error",
                code=EXIT_SYSTEM_IO,
                payload={"target": str(target_root), "file": str(target_path)},
                diagnostics=[
                    Diagnostic(
                        kind="prewrite.target_invalid",
                        level="error",
                        message=f"failed to read target file {target_path}: {exc}",
                        source="prewrite",
                    )
                ],
            ),
            human=human,
        )
        return

    new_source, added = _ensure_sibling_imports(
        source,
        needed,
        default_package=None,  # all entries carry a non-empty package explicitly
    )
    if added:
        try:
            target_path.write_text(new_source)
        except OSError as exc:
            emit(
                AuthorResult(
                    verb="add_import",
                    status="error",
                    code=EXIT_SYSTEM_IO,
                    payload={"target": str(target_root), "file": str(target_path)},
                    diagnostics=[
                        Diagnostic(
                            kind="prewrite.target_invalid",
                            level="error",
                            message=f"failed to write target file {target_path}: {exc}",
                            source="prewrite",
                        )
                    ],
                ),
                human=human,
            )
            return

    payload: dict[str, Any] = {
        "target": str(target_root),
        "file": str(target_path),
        "import_name": import_name,
        "from": resolved_module,
        "names_requested": list(raw_names),
        "names_added": list(added),
        "names_already_present": [n for n in raw_names if n not in added],
    }
    emit(
        AuthorResult(
            verb="add_import",
            status="ok",
            code=EXIT_OK,
            payload=payload,
        ),
        human=human,
    )

gaia.cli.commands.pkg.add_module

gaia pkg add-module — scaffold a fresh sibling Python module.

Sibling files in a Gaia knowledge package (e.g. priors.py, probabilities.py) need a precondition step before gaia author <verb> --file <relative> can write into them. This verb fills the gap by:

  1. Validating the target package is a Gaia knowledge package (via the existing pre-write invariant (a) machinery).
  2. Creating src/<import_name>/<module>.py with a minimal header and an empty __all__ list.
  3. Optionally seeding from gaia.engine.lang import <verbs> based on the --imports flag (CSV).

The verb emits the same uniform JSON envelope as the rest of the gaia author / gaia pkg scaffold family so an agent consumer can chain it into authoring pipelines.

Why a separate sub-verb instead of letting the author verbs auto-create their target file? Two reasons:

  • Pre-write hygiene. Author verbs assume the target file already exists so the (c) collision-and-reference scan has stable inputs; letting them silently mint a fresh file would diverge their pre-write invariants.

  • Explicit intent. Adding a sibling module is a structural choice about package organisation. Splitting it from the per-statement verb keeps the agent's actions auditable.

add_module_command

add_module_command(name: str = typer.Option(..., '--name', help='Module name relative to the package source root. Accepts a bare identifier (`priors`) or a filename (`priors.py`). Required.'), target: str = typer.Option('.', '--target', help='Path to the target Gaia package (default: cwd).'), imports: str | None = typer.Option(None, '--imports', help="Comma-separated DSL verbs to seed the module's imports (e.g. `register_prior,note`). Default: no imports beyond `from __future__ import annotations`."), docstring: str | None = typer.Option(None, '--docstring', help='Module docstring for the generated sibling file. Wrapped in triple quotes at line 1. Default: no docstring.'), human: bool = typer.Option(False, '--human', help='Render the envelope in human-readable form instead of JSON.'), json_: bool = typer.Option(True, '--json/--no-json', help='JSON-first output (default; redundant for clarity).')) -> None

Scaffold a sibling Python module under src/<import_name>/authored/.

The module is created inside the composed authored/ submodule so a later gaia author <verb> --file <name>.py (which routes into authored/) can write into it.

Example:

.. code-block:: bash

gaia pkg add-module --name priors --imports register_prior \
    --target ./mypkg-gaia --docstring "Priors module."
Source code in gaia/cli/commands/pkg/add_module.py
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
def add_module_command(
    name: str = typer.Option(
        ...,
        "--name",
        help=(
            "Module name relative to the package source root. Accepts a bare "
            "identifier (`priors`) or a filename (`priors.py`). Required."
        ),
    ),
    target: str = typer.Option(
        ".", "--target", help="Path to the target Gaia package (default: cwd)."
    ),
    imports: str | None = typer.Option(
        None,
        "--imports",
        help=(
            "Comma-separated DSL verbs to seed the module's imports "
            "(e.g. `register_prior,note`). Default: no imports beyond "
            "`from __future__ import annotations`."
        ),
    ),
    docstring: str | None = typer.Option(
        None,
        "--docstring",
        help=(
            "Module docstring for the generated sibling file. Wrapped in "
            "triple quotes at line 1. Default: no docstring."
        ),
    ),
    human: bool = typer.Option(
        False, "--human", help="Render the envelope in human-readable form instead of JSON."
    ),
    json_: bool = typer.Option(
        True, "--json/--no-json", help="JSON-first output (default; redundant for clarity)."
    ),
) -> None:
    r"""Scaffold a sibling Python module under ``src/<import_name>/authored/``.

    The module is created inside the composed ``authored/`` submodule so
    a later ``gaia author <verb> --file <name>.py`` (which routes into
    ``authored/``) can write into it.

    Example:

    .. code-block:: bash

        gaia pkg add-module --name priors --imports register_prior \
            --target ./mypkg-gaia --docstring "Priors module."
    """
    del json_

    target_root = Path(target).resolve()
    module_name = name[:-3] if name.endswith(".py") else name
    pre_errors = _validate_inputs(target=target_root, module_name=module_name)
    if pre_errors:
        first = pre_errors[0]
        result = AuthorResult(
            verb="add_module",
            status="error",
            code=EXIT_INPUT_SYNTAX,
            payload={"target": str(target_root), "module_name": module_name},
            diagnostics=list(pre_errors),
        )
        del first
        emit(result, human=human)
        return

    # Run the pre-write target-structure invariant (a) by constructing a
    # throwaway no-op proposed op against the entrypoint. The
    # ``prewrite_check`` already encapsulates the toml validation +
    # source-root discovery; reusing it avoids drift.
    probe_op = ProposedAuthorOp(
        verb="add_module",
        kind="scaffold",
        label=None,
        references=[],
        generated_code="pass\n",
        required_imports=(),
    )
    pre = prewrite_check(target_root, probe_op)
    if not pre.ok:
        # Hand-pick the structural errors (we know the probe statement
        # itself is syntactically clean).
        result = AuthorResult(
            verb="add_module",
            status="error",
            code=pre.exit_code,
            payload={"target": str(target_root)},
            diagnostics=pre.diagnostics,
        )
        emit(result, human=human)
        return

    assert pre.source_root is not None
    assert pre.source_init_path is not None
    source_root = pre.source_root
    import_name = pre.import_name or ""

    # Sibling modules for CLI-authored statements live inside the composed
    # ``authored/`` submodule (canon), so ``gaia author <verb>
    # --file <name>.py`` (which routes to ``authored/<name>.py``) finds the
    # module this verb created. Ensure the submodule exists first.
    authored_init = ensure_authored_submodule(source_root, pre.source_init_path)
    module_path = authored_init.parent / f"{module_name}.py"
    if module_path.exists():
        result = AuthorResult(
            verb="add_module",
            status="error",
            code=EXIT_PREWRITE_STRUCTURAL,
            payload={
                "target": str(target_root),
                "module_path": str(module_path),
            },
            diagnostics=[
                Diagnostic(
                    kind="prewrite.collision",
                    level="error",
                    message=(
                        f"module {module_name!r} already exists at {module_path}; "
                        "delete it or pick a different --name"
                    ),
                    source="prewrite",
                )
            ],
        )
        emit(result, human=human)
        return

    imports_tuple: tuple[str, ...] = tuple(
        item.strip() for item in (imports or "").split(",") if item.strip()
    )

    try:
        module_path.write_text(_build_module_text(imports=imports_tuple, docstring=docstring))
    except (OSError, PermissionError) as exc:
        result = AuthorResult(
            verb="add_module",
            status="error",
            code=EXIT_SYSTEM_IO,
            payload={"target": str(target_root)},
            diagnostics=[
                Diagnostic(
                    kind="prewrite.target_invalid",
                    level="error",
                    message=f"failed to write module {module_path}: {exc}",
                    source="prewrite",
                )
            ],
        )
        emit(result, human=human)
        return

    payload: dict[str, Any] = {
        "target": str(target_root),
        "module_name": module_name,
        "module_path": str(module_path),
        "relative_path": f"{module_name}.py",
        "import_name": import_name,
        "imports": list(imports_tuple),
    }
    result = AuthorResult(
        verb="add_module",
        status="ok",
        code=EXIT_OK,
        payload=payload,
    )
    emit(result, human=human)

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))

gaia.cli.commands.inquiry

gaia inquiry — public CLI sub-app.

Public commands per spec §G3: focus, review. Additional ProofState-extension subcommands (Round A1) exposed under inquiry obligation / hypothesis / reject / tactics to support Lean-like reasoning process tracking, all implemented via state.json only — no mutation of .py source / IR / priors / beliefs.

focus_command

focus_command(target: str | None = typer.Argument(None, help='Focus target.'), clear: bool = typer.Option(False, '--clear', help='Clear current focus.'), push: bool = typer.Option(False, '--push', help='Push current focus and set new.'), pop: bool = typer.Option(False, '--pop', help='Pop saved focus off the stack.'), show_stack: bool = typer.Option(False, '--stack', help='Print focus stack.'), path: str = typer.Option('.', '--path', help='Package path.')) -> None

Inspect or update the current inquiry focus.

The inquiry focus is a state.json-only pointer to the claim, strategy, or freeform topic the agent is currently working on. focus is inspect-or-set: no args reads the current focus; a bare TARGET sets it. The focus stack supports push/pop semantics so the agent can dive into a sub-claim and resume.

Example:

.. code-block:: bash

# Read current focus:
gaia inquiry focus

# Set focus to a claim label:
gaia inquiry focus my_claim_label

# Push current focus and switch to a new one:
gaia inquiry focus other_claim --push

# Pop back to the prior focus:
gaia inquiry focus --pop

# Inspect the focus stack:
gaia inquiry focus --stack

# Clear the focus entirely:
gaia inquiry focus --clear
Source code in gaia/cli/commands/inquiry.py
 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
@inquiry_app.command("focus")
def focus_command(
    target: str | None = typer.Argument(None, help="Focus target."),
    clear: bool = typer.Option(False, "--clear", help="Clear current focus."),
    push: bool = typer.Option(False, "--push", help="Push current focus and set new."),
    pop: bool = typer.Option(False, "--pop", help="Pop saved focus off the stack."),
    show_stack: bool = typer.Option(False, "--stack", help="Print focus stack."),
    # NB: no ``--target`` alias here — this command already takes a positional
    # ``target`` (the focus subject), so ``--target`` would clash with that
    # established meaning. Package selection stays ``--path`` only.
    path: str = typer.Option(".", "--path", help="Package path."),
) -> None:
    """Inspect or update the current inquiry focus.

    The inquiry focus is a state.json-only pointer to the claim, strategy,
    or freeform topic the agent is currently working on. ``focus`` is
    inspect-or-set: no args reads the current focus; a bare ``TARGET``
    sets it. The focus stack supports push/pop semantics so the agent
    can dive into a sub-claim and resume.

    Example:

    .. code-block:: bash

        # Read current focus:
        gaia inquiry focus

        # Set focus to a claim label:
        gaia inquiry focus my_claim_label

        # Push current focus and switch to a new one:
        gaia inquiry focus other_claim --push

        # Pop back to the prior focus:
        gaia inquiry focus --pop

        # Inspect the focus stack:
        gaia inquiry focus --stack

        # Clear the focus entirely:
        gaia inquiry focus --clear
    """
    flags_set = sum([bool(clear), bool(push), bool(pop), bool(show_stack)])
    if flags_set > 1:
        typer.echo("Error: --clear/--push/--pop/--stack are mutually exclusive.", err=True)
        raise typer.Exit(2)

    state = load_state(path)
    graph = resolve_graph(path)

    if clear:
        push_focus_frame(state)  # preserve history — not required but cheap
        state.focus_stack.pop()  # we actually don't want the frame on clear
        state.focus = None
        state.focus_kind = None
        state.focus_resolved_id = None
        save_state(path, state)
        append_tactic_event(path, "focus_clear", {})
        typer.echo("focus cleared")
        return

    if show_stack:
        typer.echo(f"current: {state.focus or '(none)'}")
        if not state.focus_stack:
            typer.echo("stack: (empty)")
            return
        typer.echo("stack (top → bottom):")
        for frame in reversed(state.focus_stack):
            raw = frame.get("focus") or "(none)"
            kind = frame.get("focus_kind") or "none"
            typer.echo(f"  - {raw}  [{kind}]")
        return

    if pop:
        if not state.focus_stack:
            typer.echo("Error: focus stack is empty.", err=True)
            raise typer.Exit(1)
        old = pop_focus_frame(state)
        save_state(path, state)
        append_tactic_event(
            path,
            "focus_pop",
            {"old": (old or {}).get("focus"), "restored": state.focus},
        )
        typer.echo(f"popped: {(old or {}).get('focus')}{state.focus or '(none)'}")
        return

    if push:
        if not target:
            typer.echo("Error: --push requires a TARGET.", err=True)
            raise typer.Exit(2)
        push_focus_frame(state)
        binding = resolve_focus_target(target, graph)
        state.focus = binding.raw
        state.focus_kind = binding.kind
        state.focus_resolved_id = binding.resolved_id
        save_state(path, state)
        append_tactic_event(path, "focus_push", {"target": target})
        typer.echo(f"focus pushed: {target}")
        return

    if target is None:
        if state.focus is None:
            typer.echo("focus: (none)")
        else:
            kind = state.focus_kind or "freeform"
            rid = state.focus_resolved_id
            suffix = f"; id={rid}" if rid else ""
            typer.echo(f"focus: {state.focus}  [{kind}{suffix}]")
        return

    binding = resolve_focus_target(target, graph)
    state.focus = binding.raw
    state.focus_kind = binding.kind
    state.focus_resolved_id = binding.resolved_id
    save_state(path, state)
    append_tactic_event(path, "focus_set", {"target": target, "kind": binding.kind})
    typer.echo(f"focus set: {binding.raw}  [{binding.kind}]")

obligation_add

obligation_add(target_qid: str = typer.Argument(..., help='QID the obligation is about.'), content: str = typer.Option(..., '-c', '--content', help='What must be shown.'), kind: str = typer.Option('other', '--kind', help='Diagnostic kind.'), path: str = typer.Option('.', '--path', '--target', help='Package path (--target accepted as an alias).')) -> None

Add a synthetic obligation for an inquiry target.

Records a "what must be shown" note against a target QID — a ProofState-style obligation visible in the inquiry review report. Pure state.json mutation; no IR / priors / beliefs change.

--kind selects the diagnostic kind; allowed values are focus_weakness / other (default) / prior_hole / structural_hole / support_weak (see VALID_OBLIGATION_KINDS in :mod:gaia.engine.inquiry.state).

Example:

.. code-block:: bash

gaia inquiry obligation add my_claim \
    -c "Show the prior is justified by the cited evidence." \
    --kind prior_hole
Source code in gaia/cli/commands/inquiry.py
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
@obligation_app.command("add")
def obligation_add(
    target_qid: str = typer.Argument(..., help="QID the obligation is about."),
    content: str = typer.Option(..., "-c", "--content", help="What must be shown."),
    kind: str = typer.Option("other", "--kind", help="Diagnostic kind."),
    path: str = typer.Option(
        ".", "--path", "--target", help="Package path (--target accepted as an alias)."
    ),
) -> None:
    r"""Add a synthetic obligation for an inquiry target.

    Records a "what must be shown" note against a target QID — a
    ProofState-style obligation visible in the inquiry review report.
    Pure state.json mutation; no IR / priors / beliefs change.

    ``--kind`` selects the diagnostic kind; allowed values are
    ``focus_weakness`` / ``other`` (default) / ``prior_hole`` /
    ``structural_hole`` / ``support_weak`` (see ``VALID_OBLIGATION_KINDS``
    in :mod:`gaia.engine.inquiry.state`).

    Example:

    .. code-block:: bash

        gaia inquiry obligation add my_claim \
            -c "Show the prior is justified by the cited evidence." \
            --kind prior_hole
    """
    if kind not in VALID_OBLIGATION_KINDS:
        typer.echo(
            f"Error: invalid --kind {kind!r}; allowed: {sorted(VALID_OBLIGATION_KINDS)}",
            err=True,
        )
        raise typer.Exit(2)

    state = load_state(path)
    qid = mint_qid("oblig")
    state.synthetic_obligations.append(
        SyntheticObligation(qid=qid, target_qid=target_qid, content=content, diagnostic_kind=kind)
    )
    save_state(path, state)
    append_tactic_event(
        path,
        "obligation_add",
        {"qid": qid, "target_qid": target_qid, "kind": kind},
    )
    typer.echo(f"obligation added {qid}")

obligation_list

obligation_list(json_out: bool = typer.Option(False, '--json'), path: str = typer.Option('.', '--path', '--target')) -> None

List open synthetic obligations.

Prints every open obligation (kind, qid, target_qid, content) in state.json. --json emits the same rows as a JSON array for machine consumption.

Example:

.. code-block:: bash

gaia inquiry obligation list
gaia inquiry obligation list --json
Source code in gaia/cli/commands/inquiry.py
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
@obligation_app.command("list")
def obligation_list(
    json_out: bool = typer.Option(False, "--json"),
    path: str = typer.Option(".", "--path", "--target"),
) -> None:
    """List open synthetic obligations.

    Prints every open obligation (kind, qid, target_qid, content) in
    state.json. ``--json`` emits the same rows as a JSON array for
    machine consumption.

    Example:

    .. code-block:: bash

        gaia inquiry obligation list
        gaia inquiry obligation list --json
    """
    state = load_state(path)
    rows = [
        {
            "qid": o.qid,
            "target_qid": o.target_qid,
            "content": o.content,
            "diagnostic_kind": o.diagnostic_kind,
            "created_at": o.created_at,
        }
        for o in state.synthetic_obligations
    ]
    if json_out:
        typer.echo(json.dumps(rows, ensure_ascii=False, indent=2))
        return
    if not rows:
        typer.echo("(no open obligations)")
        return
    for r in rows:
        typer.echo(f"- [{r['diagnostic_kind']}] {r['qid']}{r['target_qid']}: {r['content']}")

obligation_close

obligation_close(qid: str = typer.Argument(...), path: str = typer.Option('.', '--path', '--target')) -> None

Close a synthetic obligation by QID.

Removes the obligation with the given QID from state.json; logs a obligation_close tactic event. Exits non-zero when no obligation matches the QID. QIDs are printed by gaia inquiry obligation list.

Example:

.. code-block:: bash

gaia inquiry obligation close oblig_a1b2c3d4
Source code in gaia/cli/commands/inquiry.py
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
@obligation_app.command("close")
def obligation_close(
    qid: str = typer.Argument(...),
    path: str = typer.Option(".", "--path", "--target"),
) -> None:
    """Close a synthetic obligation by QID.

    Removes the obligation with the given QID from state.json; logs a
    ``obligation_close`` tactic event. Exits non-zero when no obligation
    matches the QID. QIDs are printed by ``gaia inquiry obligation
    list``.

    Example:

    .. code-block:: bash

        gaia inquiry obligation close oblig_a1b2c3d4
    """
    state = load_state(path)
    before = len(state.synthetic_obligations)
    state.synthetic_obligations = [o for o in state.synthetic_obligations if o.qid != qid]
    if len(state.synthetic_obligations) == before:
        typer.echo(f"Error: no obligation with qid {qid!r}.", err=True)
        raise typer.Exit(1)
    save_state(path, state)
    append_tactic_event(path, "obligation_close", {"qid": qid})
    typer.echo(f"obligation closed {qid}")

hypothesis_add

hypothesis_add(content: str = typer.Argument(..., help='Hypothesis content.'), scope: str | None = typer.Option(None, '--scope', help='Scope QID.'), path: str = typer.Option('.', '--path', '--target')) -> None

Add a working hypothesis to inquiry state.

Records a tentative hypothesis the agent wants to track during inquiry without committing it to the DSL / IR. Pure state.json mutation. --scope optionally pins the hypothesis to a specific target QID so the inquiry review report can group it with related claims.

Example:

.. code-block:: bash

gaia inquiry hypothesis add "The prior is robust to ±10% perturbation."
gaia inquiry hypothesis add "Bayes factor > 10 under diffuse alt." \
    --scope my_claim_id
Source code in gaia/cli/commands/inquiry.py
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
@hypothesis_app.command("add")
def hypothesis_add(
    content: str = typer.Argument(..., help="Hypothesis content."),
    scope: str | None = typer.Option(None, "--scope", help="Scope QID."),
    path: str = typer.Option(".", "--path", "--target"),
) -> None:
    r"""Add a working hypothesis to inquiry state.

    Records a tentative hypothesis the agent wants to track during
    inquiry without committing it to the DSL / IR. Pure state.json
    mutation. ``--scope`` optionally pins the hypothesis to a specific
    target QID so the inquiry review report can group it with related
    claims.

    Example:

    .. code-block:: bash

        gaia inquiry hypothesis add "The prior is robust to ±10% perturbation."
        gaia inquiry hypothesis add "Bayes factor > 10 under diffuse alt." \
            --scope my_claim_id
    """
    state = load_state(path)
    qid = mint_qid("hyp")
    state.synthetic_hypotheses.append(
        SyntheticHypothesis(qid=qid, content=content, scope_qid=scope)
    )
    save_state(path, state)
    append_tactic_event(path, "hypothesis_add", {"qid": qid, "scope": scope})
    typer.echo(f"hypothesis added {qid}")

hypothesis_list

hypothesis_list(json_out: bool = typer.Option(False, '--json'), path: str = typer.Option('.', '--path', '--target')) -> None

List working hypotheses from inquiry state.

Prints every recorded hypothesis (qid, scope_qid, content) from state.json. --json emits a JSON array for machine consumption.

Example:

.. code-block:: bash

gaia inquiry hypothesis list
gaia inquiry hypothesis list --json
Source code in gaia/cli/commands/inquiry.py
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
@hypothesis_app.command("list")
def hypothesis_list(
    json_out: bool = typer.Option(False, "--json"),
    path: str = typer.Option(".", "--path", "--target"),
) -> None:
    """List working hypotheses from inquiry state.

    Prints every recorded hypothesis (qid, scope_qid, content) from
    state.json. ``--json`` emits a JSON array for machine consumption.

    Example:

    .. code-block:: bash

        gaia inquiry hypothesis list
        gaia inquiry hypothesis list --json
    """
    state = load_state(path)
    rows = [
        {
            "qid": h.qid,
            "content": h.content,
            "scope_qid": h.scope_qid,
            "created_at": h.created_at,
        }
        for h in state.synthetic_hypotheses
    ]
    if json_out:
        typer.echo(json.dumps(rows, ensure_ascii=False, indent=2))
        return
    if not rows:
        typer.echo("(no hypotheses)")
        return
    for r in rows:
        scope = f" @ {r['scope_qid']}" if r["scope_qid"] else ""
        typer.echo(f"- {r['qid']}{scope}: {r['content']}")

hypothesis_remove

hypothesis_remove(qid: str = typer.Argument(...), path: str = typer.Option('.', '--path', '--target')) -> None

Remove a working hypothesis by QID.

Drops the hypothesis with the given QID from state.json. QIDs are printed by gaia inquiry hypothesis list.

Example:

.. code-block:: bash

gaia inquiry hypothesis remove hyp_a1b2c3d4
Source code in gaia/cli/commands/inquiry.py
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
@hypothesis_app.command("remove")
def hypothesis_remove(
    qid: str = typer.Argument(...),
    path: str = typer.Option(".", "--path", "--target"),
) -> None:
    """Remove a working hypothesis by QID.

    Drops the hypothesis with the given QID from state.json. QIDs are
    printed by ``gaia inquiry hypothesis list``.

    Example:

    .. code-block:: bash

        gaia inquiry hypothesis remove hyp_a1b2c3d4
    """
    state = load_state(path)
    before = len(state.synthetic_hypotheses)
    state.synthetic_hypotheses = [h for h in state.synthetic_hypotheses if h.qid != qid]
    if len(state.synthetic_hypotheses) == before:
        typer.echo(f"Error: no hypothesis with qid {qid!r}.", err=True)
        raise typer.Exit(1)
    save_state(path, state)
    append_tactic_event(path, "hypothesis_remove", {"qid": qid})
    typer.echo(f"hypothesis removed {qid}")

reject_command

reject_command(strategy: str = typer.Argument(..., help='Target strategy label/id.'), content: str = typer.Option(..., '-c', '--content', help='Reason.'), path: str = typer.Option('.', '--path', '--target')) -> None

Record a synthetic rejection for a strategy.

Annotates a strategy with a free-text rejection note (e.g. "the cited evidence does not actually support this", "argument is circular"). Pure state.json mutation; the strategy stays in the IR. Useful as a self-review affordance: a rejected strategy can later be revisited or removed from the DSL source.

Example:

.. code-block:: bash

gaia inquiry reject my_strategy_label \
    -c "Premise A is not yet established."
Source code in gaia/cli/commands/inquiry.py
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
@inquiry_app.command("reject")
def reject_command(
    strategy: str = typer.Argument(..., help="Target strategy label/id."),
    content: str = typer.Option(..., "-c", "--content", help="Reason."),
    path: str = typer.Option(".", "--path", "--target"),
) -> None:
    r"""Record a synthetic rejection for a strategy.

    Annotates a strategy with a free-text rejection note (e.g. "the
    cited evidence does not actually support this", "argument is
    circular"). Pure state.json mutation; the strategy stays in the IR.
    Useful as a self-review affordance: a rejected strategy can later be
    revisited or removed from the DSL source.

    Example:

    .. code-block:: bash

        gaia inquiry reject my_strategy_label \
            -c "Premise A is not yet established."
    """
    state = load_state(path)
    qid = mint_qid("rej")
    state.synthetic_rejections.append(
        SyntheticRejection(qid=qid, target_strategy=strategy, content=content)
    )
    save_state(path, state)
    append_tactic_event(path, "reject", {"qid": qid, "strategy": strategy})
    typer.echo(f"strategy rejected {qid} ({strategy})")

tactics_log

tactics_log(json_out: bool = typer.Option(False, '--json'), path: str = typer.Option('.', '--path', '--target')) -> None

Print the inquiry tactic event log.

Renders the append-only audit log of every inquiry state mutation (focus / obligation / hypothesis / reject / review events) in chronological order. --json emits one JSON record per entry for machine consumption.

Example:

.. code-block:: bash

gaia inquiry tactics log
gaia inquiry tactics log --json
Source code in gaia/cli/commands/inquiry.py
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
@tactics_app.command("log")
def tactics_log(
    json_out: bool = typer.Option(False, "--json"),
    path: str = typer.Option(".", "--path", "--target"),
) -> None:
    """Print the inquiry tactic event log.

    Renders the append-only audit log of every inquiry state mutation
    (focus / obligation / hypothesis / reject / review events) in
    chronological order. ``--json`` emits one JSON record per entry for
    machine consumption.

    Example:

    .. code-block:: bash

        gaia inquiry tactics log
        gaia inquiry tactics log --json
    """
    from gaia.engine.inquiry.state import read_tactic_log

    rows = read_tactic_log(path)
    if json_out:
        typer.echo(json.dumps(rows, ensure_ascii=False, indent=2))
        return
    if not rows:
        typer.echo("(no tactic log entries)")
        return
    for rec in rows:
        ts = rec.get("timestamp", "")
        ev = rec.get("event", "")
        payload = rec.get("payload", {})
        typer.echo(f"{ts}  {ev}  {json.dumps(payload, ensure_ascii=False)}")

context_command

context_command(path: str = typer.Argument('.', help='Package path.'), focus_: str | None = typer.Option(None, '--focus'), trajectory: str = typer.Option('most_uncertain', '--trajectory'), order: str = typer.Option('backward', '--order'), json_out: bool = typer.Option(False, '--json')) -> None

Render a focus-centered context packet for the current inquiry claim.

Source code in gaia/cli/commands/inquiry.py
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
@inquiry_app.command("context")
def context_command(
    path: str = typer.Argument(".", help="Package path."),
    focus_: str | None = typer.Option(None, "--focus"),
    trajectory: str = typer.Option("most_uncertain", "--trajectory"),
    order: str = typer.Option("backward", "--order"),
    json_out: bool = typer.Option(False, "--json"),
) -> None:
    """Render a focus-centered context packet for the current inquiry claim."""
    if trajectory not in {"most_uncertain", "shortest"}:
        typer.echo(
            "Error: --trajectory must be one of: most_uncertain, shortest.",
            err=True,
        )
        raise typer.Exit(2)
    if order not in {"backward", "forward"}:
        typer.echo("Error: --order must be one of: backward, forward.", err=True)
        raise typer.Exit(2)

    try:
        packet = build_context_packet(
            path,
            focus_override=focus_,
            trajectory=cast(TrajectorySelector, trajectory),
            order=cast(RenderOrder, order),
        )
    except ValueError as exc:
        typer.echo(str(exc), err=True)
        raise typer.Exit(2) from exc
    except Exception as exc:
        typer.echo(f"Error: {exc}", err=True)
        raise typer.Exit(1) from exc

    if json_out:
        typer.echo(json.dumps(context_to_json_dict(packet), ensure_ascii=False, indent=2))
    else:
        typer.echo(render_context_markdown(packet), nl=False)

review_command

review_command(path: str = typer.Argument('.', help='Package path.'), focus_: str | None = typer.Option(None, '--focus'), mode: str = typer.Option('auto', '--mode'), no_infer: bool = typer.Option(False, '--no-infer'), depth: int = typer.Option(0, '--depth'), since: str | None = typer.Option(None, '--since'), json_out: bool = typer.Option(False, '--json'), markdown_out: bool = typer.Option(False, '--markdown'), strict: bool = typer.Option(False, '--strict')) -> None

Run the local semantic inquiry review.

Executes the semantic inquiry loop on the package: graph-health diagnostics, focus-relevance analysis, optional inference, and publish-readiness blockers. Writes a timestamped review artifact under .gaia/inquiry/reviews/.

--mode selects the review profile:

  • auto (default) — pick a profile based on the package state
  • formalize — emphasise scaffolded / unformalised claims
  • explore — bias towards weak / orphaned claims
  • verify — emphasise prior coherence + Bayes diagnostics
  • publish — full publish-readiness gates (combine with --strict)

--no-infer skips the BP inference step (faster but misses belief-derived diagnostics); --depth N controls dependency inference depth as in gaia run infer.

Example:

.. code-block:: bash

gaia inquiry review .
gaia inquiry review . --mode publish --strict
gaia inquiry review . --no-infer --json
gaia inquiry review . --focus my_claim_label --markdown > review.md
Source code in gaia/cli/commands/inquiry.py
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
@inquiry_app.command("review")
def review_command(
    path: str = typer.Argument(".", help="Package path."),
    focus_: str | None = typer.Option(None, "--focus"),
    mode: str = typer.Option("auto", "--mode"),
    no_infer: bool = typer.Option(False, "--no-infer"),
    depth: int = typer.Option(0, "--depth"),
    since: str | None = typer.Option(None, "--since"),
    json_out: bool = typer.Option(False, "--json"),
    markdown_out: bool = typer.Option(False, "--markdown"),
    strict: bool = typer.Option(False, "--strict"),
) -> None:
    """Run the local semantic inquiry review.

    Executes the semantic inquiry loop on the package: graph-health
    diagnostics, focus-relevance analysis, optional inference, and
    publish-readiness blockers. Writes a timestamped review artifact
    under ``.gaia/inquiry/reviews/``.

    ``--mode`` selects the review profile:

    * ``auto`` (default) — pick a profile based on the package state
    * ``formalize`` — emphasise scaffolded / unformalised claims
    * ``explore`` — bias towards weak / orphaned claims
    * ``verify`` — emphasise prior coherence + Bayes diagnostics
    * ``publish`` — full publish-readiness gates (combine with ``--strict``)

    ``--no-infer`` skips the BP inference step (faster but misses
    belief-derived diagnostics); ``--depth N`` controls dependency
    inference depth as in ``gaia run infer``.

    Example:

    .. code-block:: bash

        gaia inquiry review .
        gaia inquiry review . --mode publish --strict
        gaia inquiry review . --no-infer --json
        gaia inquiry review . --focus my_claim_label --markdown > review.md
    """
    if mode not in {"auto", "formalize", "explore", "verify", "publish"}:
        typer.echo(f"Error: invalid --mode {mode!r}.", err=True)
        raise typer.Exit(2)
    if json_out and markdown_out:
        typer.echo("Error: --json and --markdown are mutually exclusive.", err=True)
        raise typer.Exit(2)

    report = run_review(
        path,
        focus_override=focus_,
        mode=mode,
        no_infer=no_infer,
        depth=depth,
        since=since,
        strict=strict,
    )
    append_tactic_event(
        Path(path),
        "review",
        {"review_id": report.review_id, "mode": mode, "no_infer": no_infer},
    )

    if json_out:
        typer.echo(json.dumps(report.to_json_dict(), ensure_ascii=False, indent=2))
    elif markdown_out:
        typer.echo(render_markdown(report))
    else:
        typer.echo(render_text(report))

    if report.graph_health.get("errors"):
        raise typer.Exit(1)
    if strict and mode == "publish":
        blockers = publish_blockers(report)
        if blockers:
            for b in blockers:
                typer.echo(f"[publish-strict] {b}", err=True)
            raise typer.Exit(1)
    elif strict and report.graph_health.get("warnings"):
        raise typer.Exit(1)

gaia.cli.commands.trace

Public gaia trace CLI sub-app.

Commands per ARM Trace v1: verify — 仅 schema + hash chain 校验,秒级 fail-fast review — 完整八段 review,stdout/json/markdown show — 列事件流(tactic_log 风格)

Exit codes: 0 clean / no errors 1 tampered / errors found 2 schema-broken / bad CLI args

verify_command

verify_command(trace_path: str = typer.Argument(..., help='Path to trace file (.json/.jsonl).'), quiet: bool = typer.Option(False, '--quiet', help='Suppress non-error output.')) -> None

Verify trace schema and hash chain.

Fast schema + hash-chain check (seconds, fail-fast). Loads the trace, validates each event's prev_hash forms an unbroken chain from GENESIS, then checks the manifest's events_root and manifest_hash against recomputed values.

Exit codes
  • 0 — clean
  • 1 — hash chain / manifest mismatch (tampering)
  • 2 — schema error / unreadable file

Example:

.. code-block:: bash

gaia trace verify path/to/trace.json
gaia trace verify path/to/trace.jsonl --quiet
Source code in gaia/cli/commands/trace.py
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
@trace_app.command("verify")
def verify_command(
    trace_path: str = typer.Argument(..., help="Path to trace file (.json/.jsonl)."),
    quiet: bool = typer.Option(False, "--quiet", help="Suppress non-error output."),
) -> None:
    """Verify trace schema and hash chain.

    Fast schema + hash-chain check (seconds, fail-fast). Loads the trace,
    validates each event's prev_hash forms an unbroken chain from
    GENESIS, then checks the manifest's ``events_root`` and
    ``manifest_hash`` against recomputed values.

    Exit codes:
      * 0 — clean
      * 1 — hash chain / manifest mismatch (tampering)
      * 2 — schema error / unreadable file

    Example:

    .. code-block:: bash

        gaia trace verify path/to/trace.json
        gaia trace verify path/to/trace.jsonl --quiet
    """
    res = load_trace(trace_path)
    if res.issues:
        if not quiet:
            typer.echo(f"[schema] {len(res.issues)} issue(s):", err=True)
            for s in res.issues:
                typer.echo(f"  - {s.location}: {s.message}", err=True)
        raise typer.Exit(2)

    trace = res.trace
    assert trace is not None  # 没有 issues 就一定有 trace

    errors = _trace_verify_errors(trace, recompute_chain(trace.events))
    if errors:
        _raise_trace_verify_failure(errors, quiet=quiet)
    _emit_trace_verify_ok(trace, quiet=quiet)

review_command

review_command(trace_path: str = typer.Argument(..., help='Path to trace file (.json/.jsonl).'), mode: str = typer.Option('trace', '--mode', help='Ranking mode: trace|publish.'), package: str | None = typer.Option(None, '--package', help='Gaia package path used to resolve claim_ref review_ids.'), json_out: bool = typer.Option(False, '--json', help='Emit JSON report (deterministic).'), markdown_out: bool = typer.Option(False, '--markdown', help='Emit Markdown report.'), snapshot_dir: str | None = typer.Option(None, '--snapshot-dir', help='Override snapshot output directory.'), strict: bool = typer.Option(False, '--strict', help='Exit non-zero whenever any error/warning diagnostic is present.')) -> None

Run the full ARM trace review (eight-section review).

Performs the complete ARM trace review: schema + chain + decision soundness + tool-call validity + retry hygiene + claim_ref resolution + manifest coherence + ranking. --mode trace (default) emits the generic review; --mode publish adds publish-readiness gates. --package lets the reviewer resolve claim_ref review_ids against a Gaia package's review manifest.

Exit codes
  • 0 — clean (no error diagnostics; no warning blockers under --strict)
  • 1 — error-level diagnostic, or any warning under --strict
  • 2 — invalid CLI argument combination

Example:

.. code-block:: bash

gaia trace review path/to/trace.json
gaia trace review path/to/trace.json --mode publish --strict
gaia trace review path/to/trace.json --json > review.json
gaia trace review path/to/trace.json --markdown > review.md
Source code in gaia/cli/commands/trace.py
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
@trace_app.command("review")
def review_command(
    trace_path: str = typer.Argument(..., help="Path to trace file (.json/.jsonl)."),
    mode: str = typer.Option("trace", "--mode", help="Ranking mode: trace|publish."),
    package: str | None = typer.Option(
        None, "--package", help="Gaia package path used to resolve claim_ref review_ids."
    ),
    json_out: bool = typer.Option(False, "--json", help="Emit JSON report (deterministic)."),
    markdown_out: bool = typer.Option(False, "--markdown", help="Emit Markdown report."),
    snapshot_dir: str | None = typer.Option(
        None, "--snapshot-dir", help="Override snapshot output directory."
    ),
    strict: bool = typer.Option(
        False,
        "--strict",
        help="Exit non-zero whenever any error/warning diagnostic is present.",
    ),
) -> None:
    """Run the full ARM trace review (eight-section review).

    Performs the complete ARM trace review: schema + chain + decision
    soundness + tool-call validity + retry hygiene + claim_ref resolution
    + manifest coherence + ranking. ``--mode trace`` (default) emits the
    generic review; ``--mode publish`` adds publish-readiness gates.
    ``--package`` lets the reviewer resolve ``claim_ref`` review_ids against
    a Gaia package's review manifest.

    Exit codes:
      * 0 — clean (no error diagnostics; no warning blockers under --strict)
      * 1 — error-level diagnostic, or any warning under ``--strict``
      * 2 — invalid CLI argument combination

    Example:

    .. code-block:: bash

        gaia trace review path/to/trace.json
        gaia trace review path/to/trace.json --mode publish --strict
        gaia trace review path/to/trace.json --json > review.json
        gaia trace review path/to/trace.json --markdown > review.md
    """
    if mode not in _SUPPORTED_REVIEW_MODES:
        typer.echo(
            f"Error: invalid --mode {mode!r}; allowed: {sorted(_SUPPORTED_REVIEW_MODES)}",
            err=True,
        )
        raise typer.Exit(2)
    if json_out and markdown_out:
        typer.echo("Error: --json and --markdown are mutually exclusive.", err=True)
        raise typer.Exit(2)

    report = run_trace_review(
        trace_path,
        mode=mode,
        package_path=package,
        snapshot_dir=snapshot_dir,
    )

    if json_out:
        typer.echo(render_json(report))
    elif markdown_out:
        typer.echo(render_markdown(report))
    else:
        typer.echo(render_text(report))

    has_error = any(d.severity == "error" for d in report.diagnostics)
    has_warning = any(d.severity == "warning" for d in report.diagnostics)
    if has_error:
        raise typer.Exit(1)
    if strict and has_warning:
        raise typer.Exit(1)

show_command

show_command(trace_path: str = typer.Argument(..., help='Path to trace file (.json/.jsonl).'), limit: int = typer.Option(50, '--limit', help='Max events to print (0 = all).'), kind: str | None = typer.Option(None, '--kind', help='Filter by event kind (decision/tool_call/...).'), json_out: bool = typer.Option(False, '--json', help='Emit JSONL of selected events.')) -> None

Print the trace event stream (tactic_log style).

Walks the events list in seq order and prints a compact one-line summary per event (kind, actor, ts, plus kind-specific detail). On schema violations the loadable events are still printed; schema issues go to stderr.

--kind filters by event kind (decision / tool_call / retry / etc.); --limit 0 shows every event; --json emits one JSON line per event for machine consumption.

Example:

.. code-block:: bash

gaia trace show path/to/trace.json
gaia trace show path/to/trace.json --kind tool_call --limit 0
gaia trace show path/to/trace.json --json > events.jsonl
Source code in gaia/cli/commands/trace.py
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
@trace_app.command("show")
def show_command(
    trace_path: str = typer.Argument(..., help="Path to trace file (.json/.jsonl)."),
    limit: int = typer.Option(50, "--limit", help="Max events to print (0 = all)."),
    kind: str | None = typer.Option(
        None, "--kind", help="Filter by event kind (decision/tool_call/...)."
    ),
    json_out: bool = typer.Option(False, "--json", help="Emit JSONL of selected events."),
) -> None:
    """Print the trace event stream (tactic_log style).

    Walks the events list in seq order and prints a compact one-line
    summary per event (kind, actor, ts, plus kind-specific detail). On
    schema violations the loadable events are still printed; schema
    issues go to stderr.

    ``--kind`` filters by event kind (``decision`` / ``tool_call`` /
    ``retry`` / etc.); ``--limit 0`` shows every event; ``--json``
    emits one JSON line per event for machine consumption.

    Example:

    .. code-block:: bash

        gaia trace show path/to/trace.json
        gaia trace show path/to/trace.json --kind tool_call --limit 0
        gaia trace show path/to/trace.json --json > events.jsonl
    """
    res = load_trace(trace_path)
    if res.issues and not res.trace:
        typer.echo("[show] schema errors prevent loading the trace:", err=True)
        for s in res.issues:
            typer.echo(f"  - {s.location}: {s.message}", err=True)
        raise typer.Exit(2)

    trace = res.trace
    assert trace is not None

    events = trace.events
    if kind:
        events = [e for e in events if e.kind == kind]
    if limit and limit > 0:
        events = events[:limit]

    if json_out:
        for e in events:
            typer.echo(e.model_dump_json())
        return

    # 简单 text 模式
    typer.echo(f"trace_id     : {trace.manifest.trace_id}")
    typer.echo(f"arm_id       : {trace.manifest.arm_id}")
    typer.echo(f"session_id   : {trace.manifest.session_id}")
    typer.echo(f"events shown : {len(events)} (of {len(trace.events)} total)")
    typer.echo("-" * 72)
    for e in events:
        marker = e.kind.ljust(18)
        actor = e.actor.ljust(8)
        suffix = ""
        if e.kind == "tool_call" and e.tool:
            suffix = f"  tool={e.tool}"
        elif e.kind == "decision" and e.reason:
            txt = (e.reason or "").splitlines()[0][:60]
            suffix = f"  reason={txt!r}"
        elif e.kind == "retry" and e.error:
            suffix = f"  err={e.error[:40]!r}"
        typer.echo(f"  seq={e.seq:<3} {marker} actor={actor} ts={e.ts.isoformat()}{suffix}")