Skip to content

Authoring workflow

Status: Current v0.5 canonical. The single source of truth for how a Gaia knowledge package is authored — for both humans and agents.

Gaia has one authoring model with two tiers. This page states it and closes the gaps that earlier docs left ambiguous.

The model

Tier 1 — direct SDK authoring (recommended). Write the DSL directly in Python. Run gaia sdk once to drop a self-contained reference plus a one-page CHEATSHEET.md next to your work, read the cheat sheet, then author your statements in src/<pkg>/__init__.py (and your own modules). This is the primary path for everyone — humans and agents alike.

gaia sdk                       # writes ./gaia-sdk/CHEATSHEET.md + full reference
# read CHEATSHEET.md, then author directly:
#   src/my_package/__init__.py
gaia build compile ./my-package-gaia
gaia run infer ./my-package-gaia

Tier 2 — the gaia author CLI (optional convenience). gaia author <verb> CRUDs DSL statements through structured, JSON-enveloped commands. It is a convenience layer over Tier 1 — useful when you want machine-checked appends, identifier-collision guards, and a post-write compile check — not a separate or "agent-first" authoring path. Use it when it helps; skip it when you'd rather write Python directly.

What this resolves

  1. Recommended path = direct SDK authoring (Tier 1). gaia author is Tier 2, optional. Start with gaia sdk, not the CLI.
  2. One on-disk format. A Gaia package is plain Python (.py). There is no second serialized format. CLI output is confined to the package's composed authored/ submodule (see below).
  3. gaia author APPENDS and is NOT idempotent. Re-running the same gaia author command writes the statement again; a later binding of the same name shadows the earlier one. gaia author list reports shadowing (the earlier binding is stamped shadowed_by). If you re-run a command, expect a duplicate — edit the source or rename rather than relying on dedup.
  4. No "agent-first" framing. Both humans and agents author directly first; the CLI is optional for either. There is no privileged agent entry point — gaia sdk is the shared starting move.
  5. Auxiliary files live under authored/. When the CLI manages priors or reviews, they land in authored/priors.py / authored/reviews/… (created via gaia pkg add-module, routed there by gaia author <verb> --file <name>.py). Hand-authored packages may keep a top-level priors.py; both are loaded by the engine.
  6. Mixing contract. Hand-authored DSL lives in your own modules (the package root __init__.py and any siblings you write). CLI-authored DSL lives in authored/. The two compose by import, never by interleaving in one file: the package-root __init__.py imports the authored module, copies its public runtime bindings into root globals for package local references, and merges only authored.__all__ into its own __all__. Only names in the merged root __all__ become the package's exported public surface. Exported names must be local Knowledge objects; returned helpers from relation verbs are allowed and are typed as relation interfaces in manifests.

The authored/ submodule

gaia build init / gaia pkg scaffold create:

my-package-gaia/
  src/my_package/
    __init__.py          # hand-authored DSL + authored import block
    authored/
      __init__.py         # CLI-authored statements land here (__all__ literal)
      priors.py           # optional CLI-managed sibling (gaia pkg add-module)

The package-root __init__.py ends with:

__all__: list[str] = [...]               # your hand-authored public exports

from . import authored as _authored

for _gaia_name, _gaia_value in vars(_authored).items():
    if not _gaia_name.startswith("_"):
        globals()[_gaia_name] = _gaia_value
del _gaia_name, _gaia_value

__all__ = [*__all__, *_authored.__all__]

gaia author never writes the package-root __init__.py; every CLI write goes into authored/. This keeps hand-authored and CLI-authored statements in separate files that compose cleanly. Author commands default to internal writes; pass --export only for bindings that belong in the curated public surface. For relation verbs, --export exports the returned relation helper; for direct formula operands, name the formula with claim(..., formula=...) instead of exporting the implicit lift helper.

Pre-canon alpha packages. You do not add the authored import block by hand. On the first gaia author <verb> write, the CLI automatically creates authored/ and appends the authored import block plus the __all__ merge to your package-root __init__.py. The only manual step for an alpha-era package is relocating any pre-existing CLI-style statements that currently live in the root __init__.py into authored/ if you want them CLI-managed there. There is no migration tooling: CLI-authored and hand-authored .py are byte-identical, so a detector would false-positive — hence the move is manual and the appended block is byte-identical to the scaffolded one. If your root __all__ is a tuple, the merge preserves it as a tuple.

See also