Skip to content

gaia build

Create and validate a knowledge package.

gaia build init <name>      Scaffold a new <name>-gaia package
gaia build compile [path]   Lower DSL into .gaia/ir.json + manifests
gaia build check [path]     Validate structure, priors, and warrants
Verb Purpose
init Scaffold a new package with pyproject.toml, src/<import_name>/, and starter DSL
compile Execute the DSL declarations, lower to LocalCanonicalGraph, write IR + manifests + hash
check Validate pyproject.toml, IR hash, schema, naming, priors, warrants, and quality gate

The historical flat build verbs moved under this group (gaia compile <path>gaia build compile <path>). See CLI Commands for workflow examples and use gaia build <verb> --help for the executable option surface.

Implementation

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