Skip to content

Packaging API

Status: Generated from current Python docstrings and type hints.

Gaia package loading, compilation, environment management, and prior application. New in alpha 0: this module hosts the helpers that used to live at gaia.cli._packages, promoted out of the CLI so the engine contract is self-contained.

The error type GaiaPackagingError replaces the old GaiaCliError name — catch sites that imported the old name need both the module path and the class name updated. See Migration to alpha 0 for the full symbol-level mapping.

gaia.engine.packaging

Engine-side package loading + compilation surface.

Public facade gaia.engine.packaging: loading Gaia user packages from disk, compiling them into IR artifacts, priors application, and dependency-graph loading.

CompiledPackage dataclass

CompiledPackage(graph: LocalCanonicalGraph, knowledge_ids_by_object: dict[int, str], strategies_by_object: dict[int, Strategy], action_label_map: dict[str, str] = dict(), target_action_labels_by_id: dict[str, str] = dict(), formalization_manifest: dict[str, Any] = (lambda: {'version': 1, 'dependencies': [], 'materializations': []})(), review: ReviewManifest | None = None)

Compiled Gaia package plus runtime-object to IR-ID mappings.

to_json

to_json() -> dict[str, Any]

Serialize the compiled graph as Gaia IR JSON.

Source code in gaia/engine/lang/compiler/compile.py
132
133
134
def to_json(self) -> dict[str, Any]:
    """Serialize the compiled graph as Gaia IR JSON."""
    return self.graph.model_dump(mode="json", exclude_none=True, serialize_as_any=True)

GaiaPackagingError

Bases: RuntimeError

Engine packaging error surface (raised by load / compile / priors paths).

LoadedGaiaPackage dataclass

LoadedGaiaPackage(pkg_path: Path, config: dict[str, Any], project_config: dict[str, Any], gaia_config: dict[str, Any], project_name: str, import_name: str, source_root: Path, module: ModuleType, package: CollectedPackage)

In-memory result of load_gaia_package.

Bundles pyproject metadata, the imported user module, and the collected runtime DSL objects (Knowledge / Strategy / Operator).

DependencyGraph dataclass

DependencyGraph(import_name: str, dist_name: str, root: Path, graph: Any)

A dependency's compiled IR loaded from disk.

ensure_package_env

ensure_package_env(pkg_path: Path) -> None

Run uv sync in pkg_path so dependencies are importable.

Skipped when the directory has no pyproject.toml or when uv is not on $PATH. Failures are non-fatal (a warning is printed) because the user may manage dependencies another way.

Source code in gaia/engine/packaging.py
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
def ensure_package_env(pkg_path: Path) -> None:
    """Run ``uv sync`` in *pkg_path* so dependencies are importable.

    Skipped when the directory has no ``pyproject.toml`` or when ``uv``
    is not on ``$PATH``.  Failures are non-fatal (a warning is printed)
    because the user may manage dependencies another way.
    """
    if not (pkg_path / "pyproject.toml").exists():
        return
    import shutil

    if shutil.which("uv") is None:
        return
    result = subprocess.run(
        ["uv", "sync", "--quiet"],
        cwd=pkg_path,
        capture_output=True,
        text=True,
    )
    if result.returncode != 0:
        import logging

        logging.getLogger(__name__).debug("uv sync in %s: %s", pkg_path, result.stderr.strip())

load_gaia_package

load_gaia_package(path: str | Path = '.') -> LoadedGaiaPackage

Load a Gaia knowledge package from a local directory.

Source code in gaia/engine/packaging.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
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
def load_gaia_package(path: str | Path = ".") -> LoadedGaiaPackage:
    """Load a Gaia knowledge package from a local directory."""
    pkg_path = Path(path).resolve()
    pyproject = pkg_path / "pyproject.toml"
    config = _load_pyproject_config(pkg_path)
    project_config, gaia_config, project_name, version = _package_identity(config)

    import_name = project_name.removesuffix("-gaia").replace("-", "_")
    reset_inferred_package(pyproject, module_name=import_name)
    _prepend_local_dependency_source_roots(config, pkg_path)
    _prepend_pulled_package_source_roots(pkg_path)
    source_root = _source_root_for_package(pkg_path, import_name, project_name)

    source_root_str = str(source_root)
    if source_root_str not in sys.path:
        sys.path.insert(0, source_root_str)

    module = _import_package_module(import_name)
    _import_package_source_modules(import_name, source_root / import_name)

    pkg = get_inferred_package(pyproject)
    if pkg is None:
        raise GaiaPackagingError(
            "Error: no Gaia declarations found. Declare Knowledge/Strategy/Operator objects "
            "directly in the module and export the public surface via __all__ when needed."
        )

    _assign_labels_for_loaded_modules()

    _record_root_exports(module, pkg)

    module_titles = _module_titles(import_name, pkg)
    if module_titles:
        pkg._module_titles = module_titles

    pkg.name = import_name
    pkg.version = version
    if "namespace" in gaia_config:
        pkg.namespace = gaia_config["namespace"]

    return LoadedGaiaPackage(
        pkg_path=pkg_path,
        config=config,
        project_config=project_config,
        gaia_config=gaia_config,
        project_name=project_name,
        import_name=import_name,
        source_root=source_root,
        module=module,
        package=pkg,
    )

compile_loaded_package

compile_loaded_package(loaded: LoadedGaiaPackage) -> dict[str, Any]

Compile an already loaded Gaia package to IR JSON.

Source code in gaia/engine/packaging.py
513
514
515
516
517
def compile_loaded_package(loaded: LoadedGaiaPackage) -> dict[str, Any]:
    """Compile an already loaded Gaia package to IR JSON."""
    from gaia.engine.lang.compiler import compile_package

    return compile_package(loaded.package)

compile_loaded_package_artifact

compile_loaded_package_artifact(loaded: LoadedGaiaPackage) -> CompiledPackage

Compile an already loaded Gaia package to IR plus runtime mappings.

Source code in gaia/engine/packaging.py
520
521
522
523
524
525
526
527
528
529
def compile_loaded_package_artifact(loaded: LoadedGaiaPackage) -> CompiledPackage:
    """Compile an already loaded Gaia package to IR plus runtime mappings."""
    from gaia.engine.lang.compiler import compile_package_artifact
    from gaia.engine.lang.refs import ReferenceError, load_references

    try:
        references = load_references(loaded.pkg_path / "references.json")
        return compile_package_artifact(loaded.package, references=references)
    except ReferenceError as e:
        raise GaiaPackagingError(str(e)) from e

apply_package_priors

apply_package_priors(loaded: LoadedGaiaPackage) -> None

Resolve multi-source priors and inject the winning value into metadata.

Pipeline:

  1. Auto-import priors.py if present. This runs any register_prior(...) calls inside the module, populating claim.metadata['prior_records'] as a side effect. The legacy PRIORS = {...} dict is rejected with a migration error.
  2. Read the package's optional RESOLUTION_POLICY from priors.py, falling back to :func:default_resolution_policy when absent.
  3. Walk every Claim in the package. For each claim with one or more records under metadata['prior_records'], run the policy and write the winning value/justification to metadata['prior'] / metadata['prior_justification'].

All records (winner and losers) are preserved in prior_records for audit purposes and for the prior_dissent / prior_overridden diagnostics.

Authors may also call register_prior directly from __init__.py or any other module imported during package load — those calls populate prior_records before this function runs, and are resolved identically to those declared in priors.py.

Source code in gaia/engine/packaging.py
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
623
624
625
626
627
def apply_package_priors(loaded: LoadedGaiaPackage) -> None:
    """Resolve multi-source priors and inject the winning value into metadata.

    Pipeline:

    1. Auto-import ``priors.py`` if present. This runs any
       ``register_prior(...)`` calls inside the module, populating
       ``claim.metadata['prior_records']`` as a side effect. The legacy
       ``PRIORS = {...}`` dict is rejected with a migration error.
    2. Read the package's optional ``RESOLUTION_POLICY`` from ``priors.py``,
       falling back to :func:`default_resolution_policy` when absent.
    3. Walk every ``Claim`` in the package. For each claim with one or more
       records under ``metadata['prior_records']``, run the policy and write
       the winning value/justification to ``metadata['prior']`` /
       ``metadata['prior_justification']``.

    All records (winner and losers) are preserved in ``prior_records`` for
    audit purposes and for the ``prior_dissent`` / ``prior_overridden``
    diagnostics.

    Authors may also call ``register_prior`` directly from ``__init__.py`` or
    any other module imported during package load — those calls populate
    ``prior_records`` before this function runs, and are resolved identically
    to those declared in ``priors.py``.
    """
    policy = _load_resolution_policy(loaded)
    loaded.package._resolution_policy = policy
    try:
        resolve_priors_to_metadata(loaded.package.knowledge, policy)
    except (TypeError, ValueError) as exc:
        raise GaiaPackagingError(f"Error resolving priors: {exc}") from exc

validate_fills_relations

validate_fills_relations(loaded: LoadedGaiaPackage, compiled: CompiledPackage) -> None

Validate fills() relations without building full manifests.

Raises GaiaPackagingError if any fills() strategy has an invalid source, target, or dependency configuration. Use this for gaia build check where manifests are not needed — only validation matters.

Source code in gaia/engine/packaging.py
1131
1132
1133
1134
1135
1136
1137
1138
def validate_fills_relations(loaded: LoadedGaiaPackage, compiled: CompiledPackage) -> None:
    """Validate fills() relations without building full manifests.

    Raises GaiaPackagingError if any fills() strategy has an invalid source,
    target, or dependency configuration. Use this for ``gaia build check``
    where manifests are not needed — only validation matters.
    """
    _resolve_fills_relations(loaded, compiled)

build_package_manifests

build_package_manifests(loaded: LoadedGaiaPackage, compiled: CompiledPackage) -> dict[str, dict[str, Any]]

Build package-level interface manifests from compiled IR plus runtime package state.

Emits four sibling manifest files under .gaia/manifests/:

  • exports.json — every knowledge node in the package flagged exported. These are the package's public interface claims that downstream packages may depend on.
  • premises.json — every leaf claim (a claim with no supporting strategy in the local package) that feeds into an exported conclusion. Each entry carries a role field:

    • local_hole — the leaf claim is declared in the current package but has no derivation chain. These are the package's primary evidence and abduction alternatives — i.e. the propositions the author accepts as given inputs to the reasoning graph.
    • foreign_dependency — the leaf claim originates in an upstream *-gaia dependency and is consumed by a local strategy via the dependency's exports.json.
  • holes.json — the subset of premises.json entries whose role is local_hole. Despite the name, a "hole" here does not mean an unresolved cross-package reference (foreign dependencies already have their own resolution path via the dep's exports). A local_hole is a local leaf claim that a downstream package could optionally "fill" with more specific evidence via the fills relation — but the current package is perfectly valid with its leaves unfilled.

  • bridges.jsonfills relations declared in the local package that point at hole qids in an upstream dependency's manifest. Empty for packages with no upstream deps.

Concrete example from the watson-rfdiffusion-2023-gaia package:

  • 7 exports (the paper's exported conclusions, e.g. binder_success_rate)
  • 32 local holes: 20 primary observations (e.g. denoising_process, binder_specificity) + 12 abduction alternatives (alt_nonspecific_binding_p53_mdm2, etc.)
  • 0 foreign dependencies (watson has no upstream *-gaia deps)
  • 0 bridges (watson doesn't fill any upstream holes)

All 32 holes are declared claims in the local package — they appear in ir.json as regular knowledge nodes with exported=false and no supporting strategy. They are reported as "holes" because the holes.json manifest is indexing local leaves that downstream packages could optionally refine, not unresolved references.

See docs/specs/2026-04-08-gaia-lang-hole-fills-design.md §3.2 for the full rationale on why "hole" is a release-scoped interface role rather than a source primitive.

Source code in gaia/engine/packaging.py
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
def build_package_manifests(
    loaded: LoadedGaiaPackage, compiled: CompiledPackage
) -> dict[str, dict[str, Any]]:
    """Build package-level interface manifests from compiled IR plus runtime package state.

    Emits four sibling manifest files under ``.gaia/manifests/``:

    - ``exports.json`` — every knowledge node in the package flagged ``exported``.
      These are the package's public interface claims that downstream packages
      may depend on.
    - ``premises.json`` — every **leaf** claim (a claim with no supporting
      strategy in the local package) that feeds into an exported conclusion.
      Each entry carries a ``role`` field:

        * ``local_hole`` — the leaf claim is declared in the **current** package
          but has no derivation chain. These are the package's primary evidence
          and abduction alternatives — i.e. the propositions the author accepts
          as given inputs to the reasoning graph.
        * ``foreign_dependency`` — the leaf claim originates in an upstream
          ``*-gaia`` dependency and is consumed by a local strategy via the
          dependency's ``exports.json``.

    - ``holes.json`` — the subset of ``premises.json`` entries whose role is
      ``local_hole``. Despite the name, a "hole" here does **not** mean an
      unresolved cross-package reference (foreign dependencies already have
      their own resolution path via the dep's exports). A ``local_hole`` is a
      local leaf claim that a *downstream* package could optionally "fill" with
      more specific evidence via the ``fills`` relation — but the current
      package is perfectly valid with its leaves unfilled.
    - ``bridges.json`` — ``fills`` relations declared in the local package that
      point at hole qids in an upstream dependency's manifest. Empty for
      packages with no upstream deps.

    Concrete example from the ``watson-rfdiffusion-2023-gaia`` package:

    - 7 exports (the paper's exported conclusions, e.g. ``binder_success_rate``)
    - 32 local holes: 20 primary observations (e.g. ``denoising_process``,
      ``binder_specificity``) + 12 abduction alternatives
      (``alt_nonspecific_binding_p53_mdm2``, etc.)
    - 0 foreign dependencies (watson has no upstream ``*-gaia`` deps)
    - 0 bridges (watson doesn't fill any upstream holes)

    All 32 holes are **declared claims** in the local package — they appear in
    ``ir.json`` as regular knowledge nodes with ``exported=false`` and no
    supporting strategy. They are reported as "holes" because the `holes.json`
    manifest is indexing *local leaves that downstream packages could optionally
    refine*, not *unresolved references*.

    See ``docs/specs/2026-04-08-gaia-lang-hole-fills-design.md`` §3.2 for the
    full rationale on why "hole" is a release-scoped interface role rather than
    a source primitive.
    """
    fills_relations = _resolve_fills_relations(loaded, compiled)
    graph = compiled.graph
    knowledge_by_qid, exported_qids, exported_claim_qids = _manifest_graph_sets(graph)
    exports = _manifest_exports(graph)
    premises = _manifest_premises(
        loaded=loaded,
        compiled=compiled,
        knowledge_by_qid=knowledge_by_qid,
        exported_qids=exported_qids,
        exported_claim_qids=exported_claim_qids,
    )

    holes = [
        {key: value for key, value in premise.items() if key != "role" and key != "exported"}
        for premise in premises
        if premise["role"] == "local_hole"
    ]

    return {
        "exports.json": {
            **_manifest_base(loaded, ir_hash=graph.ir_hash or ""),
            "exports": exports,
        },
        "premises.json": {
            **_manifest_base(loaded, ir_hash=graph.ir_hash or ""),
            "premises": premises,
        },
        "holes.json": {
            **_manifest_base(loaded, ir_hash=graph.ir_hash or ""),
            "holes": holes,
        },
        "bridges.json": {
            **_manifest_base(loaded, ir_hash=graph.ir_hash or ""),
            "bridges": fills_relations,
        },
    }

collect_foreign_node_priors

collect_foreign_node_priors(graph: LocalCanonicalGraph, pkg_path: Path) -> dict[str, float]

Collect upstream beliefs for foreign knowledge nodes.

Scans .gaia/dep_beliefs/*.json for belief manifests downloaded by gaia add. For each foreign knowledge node in graph (i.e. a node whose QID does not start with the local {namespace}:{package}:: prefix), if the upstream manifest contains a matching knowledge_id, the upstream belief is included in the returned dict.

The returned dict is suitable for passing as node_priors to lower_local_graph(), which gives these values highest explicit- override priority (above metadata["prior"]).

Source code in gaia/engine/packaging.py
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
def collect_foreign_node_priors(
    graph: LocalCanonicalGraph,
    pkg_path: Path,
) -> dict[str, float]:
    """Collect upstream beliefs for foreign knowledge nodes.

    Scans ``.gaia/dep_beliefs/*.json`` for belief manifests downloaded by
    ``gaia add``.  For each foreign knowledge node in *graph* (i.e. a node
    whose QID does **not** start with the local ``{namespace}:{package}::``
    prefix), if the upstream manifest contains a matching ``knowledge_id``,
    the upstream belief is included in the returned dict.

    The returned dict is suitable for passing as ``node_priors`` to
    ``lower_local_graph()``, which gives these values highest explicit-
    override priority (above ``metadata["prior"]``).
    """
    dep_beliefs_dir = pkg_path / ".gaia" / "dep_beliefs"
    if not dep_beliefs_dir.is_dir():
        return {}

    # Build upstream beliefs mapping from all dep_beliefs files
    upstream_beliefs: dict[str, float] = {}
    for beliefs_file in sorted(dep_beliefs_dir.glob("*.json")):
        try:
            data = json.loads(beliefs_file.read_text())
        except (OSError, json.JSONDecodeError):
            continue
        beliefs_list = data.get("beliefs")
        if not isinstance(beliefs_list, list):
            continue
        for entry in beliefs_list:
            if not isinstance(entry, dict):
                continue
            kid = entry.get("knowledge_id")
            belief = entry.get("belief")
            if isinstance(kid, str) and isinstance(belief, (int, float)):
                upstream_beliefs[kid] = float(belief)

    if not upstream_beliefs:
        return {}

    # Determine local prefix to identify foreign nodes
    local_prefix = f"{graph.namespace}:{graph.package_name}::"

    foreign_priors: dict[str, float] = {}
    for knowledge in graph.knowledges:
        kid = knowledge.id
        if kid is None or kid.startswith(local_prefix):
            continue
        if kid in upstream_beliefs:
            foreign_priors[kid] = upstream_beliefs[kid]

    return foreign_priors

load_dependency_compiled_graphs

load_dependency_compiled_graphs(project_config: dict[str, Any], *, depth: int = 1, _seen: set[str] | None = None) -> list[DependencyGraph]

Discover direct -gaia dependencies and load their compiled IR.

Parameters

project_config: The [project] section of the local pyproject.toml. depth: How many levels of transitive dependencies to load. 1 = direct deps only, 2+ = recurse, -1 = unlimited. _seen: Internal dedup set (QID prefixes already loaded). Callers should not pass this.

Returns:

Flat list of :class:DependencyGraph for all discovered dependencies (deduplicated by namespace:package_name).

Source code in gaia/engine/packaging.py
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
def load_dependency_compiled_graphs(
    project_config: dict[str, Any],
    *,
    depth: int = 1,
    _seen: set[str] | None = None,
) -> list[DependencyGraph]:
    """Discover direct ``-gaia`` dependencies and load their compiled IR.

    Parameters
    ----------
    project_config:
        The ``[project]`` section of the local ``pyproject.toml``.
    depth:
        How many levels of transitive dependencies to load.
        1 = direct deps only, 2+ = recurse, -1 = unlimited.
    _seen:
        Internal dedup set (QID prefixes already loaded). Callers should
        not pass this.

    Returns:
    -------
    Flat list of :class:`DependencyGraph` for all discovered dependencies
    (deduplicated by ``namespace:package_name``).
    """
    from gaia.engine.ir.graphs import LocalCanonicalGraph

    if _seen is None:
        _seen = set()

    _specs, import_to_dist = _parse_gaia_dependencies(project_config)
    result: list[DependencyGraph] = []

    for import_name, dist_name in sorted(import_to_dist.items()):
        root = _locate_dependency_manifest_root(import_name)
        if root is None:
            raise GaiaPackagingError(
                f"Could not locate Gaia package root for dependency '{import_name}'. "
                f"Is '{dist_name}' installed?"
            )
        ir_path = root / ".gaia" / "ir.json"
        if not ir_path.exists():
            raise GaiaPackagingError(
                f"Dependency '{import_name}' is missing .gaia/ir.json. "
                f"Run 'gaia build compile' in {root}."
            )
        ir_data = _load_json_file(ir_path, description=f"{import_name} .gaia/ir.json")
        graph = LocalCanonicalGraph.model_validate(ir_data)

        # Dedup by namespace:package_name
        qid_prefix = f"{graph.namespace}:{graph.package_name}"
        if qid_prefix in _seen:
            continue
        _seen.add(qid_prefix)

        result.append(
            DependencyGraph(
                import_name=import_name,
                dist_name=dist_name,
                root=root,
                graph=graph,
            )
        )

        # Recurse into transitive deps if requested
        if depth > 1 or depth == -1:
            dep_pyproject = root / "pyproject.toml"
            if dep_pyproject.exists():
                try:
                    dep_config = tomllib.loads(dep_pyproject.read_text())
                except Exception:
                    continue
                dep_project = dep_config.get("project", {})
                next_depth = depth - 1 if depth > 1 else -1
                transitive = load_dependency_compiled_graphs(
                    dep_project, depth=next_depth, _seen=_seen
                )
                result.extend(transitive)

    return result

gaia_lang_version

gaia_lang_version() -> str

Return the installed gaia-lang version, or 'unknown' for dev checkouts.

Used by compile (to stamp .gaia/compile_metadata.json) and by tests. We deliberately return a string sentinel instead of raising so that running gaia build compile inside an un-built editable checkout still produces a valid metadata file — downstream consumers can detect 'unknown' and decide.

Source code in gaia/engine/packaging.py
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
def gaia_lang_version() -> str:
    """Return the installed gaia-lang version, or 'unknown' for dev checkouts.

    Used by compile (to stamp `.gaia/compile_metadata.json`) and by tests. We
    deliberately return a string sentinel instead of raising so that running
    `gaia build compile` inside an un-built editable checkout still produces a valid
    metadata file — downstream consumers can detect 'unknown' and decide.
    """
    try:
        return _pkg_version("gaia-lang")
    except PackageNotFoundError:
        return "unknown"

write_text_atomic

write_text_atomic(path: Path, text: str, *, encoding: str = 'utf-8') -> None

Write text via a unique temp file and atomic replace.

Source code in gaia/engine/packaging.py
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
def write_text_atomic(path: Path, text: str, *, encoding: str = "utf-8") -> None:
    """Write text via a unique temp file and atomic replace."""
    path.parent.mkdir(parents=True, exist_ok=True)
    tmp = path.with_name(f"{path.name}.{uuid.uuid4().hex}.tmp")
    try:
        tmp.write_text(text, encoding=encoding)
        tmp.replace(path)
    finally:
        if tmp.exists():
            tmp.unlink()

write_compiled_artifacts

write_compiled_artifacts(pkg_path: Path, ir: dict[str, Any], *, manifests: dict[str, dict[str, Any]] | None = None, formalization_manifest: dict[str, Any] | None = None) -> Path

Write .gaia compilation artifacts and return the output directory.

Source code in gaia/engine/packaging.py
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
def write_compiled_artifacts(
    pkg_path: Path,
    ir: dict[str, Any],
    *,
    manifests: dict[str, dict[str, Any]] | None = None,
    formalization_manifest: dict[str, Any] | None = None,
) -> Path:
    """Write .gaia compilation artifacts and return the output directory."""
    gaia_dir = pkg_path / ".gaia"
    gaia_dir.mkdir(exist_ok=True)
    ir_json = json.dumps(ir, ensure_ascii=False, indent=2, sort_keys=True)
    write_text_atomic(gaia_dir / "ir.json", ir_json)
    write_text_atomic(gaia_dir / "ir_hash", ir["ir_hash"])
    write_text_atomic(gaia_dir / "compile_metadata.json", _render_compile_metadata(ir["ir_hash"]))
    if formalization_manifest is not None:
        write_text_atomic(
            gaia_dir / "formalization_manifest.json",
            render_manifest_json(formalization_manifest),
        )
    if manifests:
        manifests_dir = gaia_dir / "manifests"
        manifests_dir.mkdir(exist_ok=True)
        for filename, payload in manifests.items():
            write_text_atomic(manifests_dir / filename, render_manifest_json(payload))
    return gaia_dir