orchestrata_ast.roots
Reference › orchestrata_ast › roots
This module is the single reader of the declared AST root set, the list of source trees that count as "our source". Its consumers are scripts.serialize_codebase_ast, which writes the snapshot, and scripts.ast_live_sync, which pushes saved files into the knowledge graph. Both are deployed under /opt/orchestrata-tools, reached through the ast and ast_live_sync launchers in its bin directory. The module exists because the two once disagreed about which trees to cover, so the snapshot held far more modules than the graph.
The set is declared in the environment variable ORCHESTRATA_AST_ROOTS, loaded from a tracked env file by the shell environment and by the systemd units. declared_roots parses it, accepting whitespace or colons as separators, dropping empty tokens and keeping declaration order. An env mapping can be passed for tests. split_roots returns the first root as primary and the rest as extras. existing_roots partitions roots into present and missing directories under a repository root and leaves the policy to the caller. undeclared_message is the one sentence printed when the set is empty. The module deliberately supplies no fallback list.
At a glance
| Source | src/orchestrata_ast/roots.py |
| Lines of code | 73 |
| Classes | 0 |
| Functions | 4 |
| Imports (internal) | none |
| Imported by | scripts.ast_live_sync, scripts.serialize_codebase_ast |
| Tests that import it | tests/scripts/test_ast_live_sync.py, tests/scripts/test_ast_live_sync_main.py, tests/test_orchestrata_ast_roots.py |
Class diagram
No classes; see Functions.
Dependency diagram
API
Functions
| Function | Signature | Summary |
|---|---|---|
declared_roots |
declared_roots(env: Mapping[str, str] | None = None) -> list[str] |
Return the declared root set, in declaration order, or [] when unset. |
split_roots |
split_roots(roots: list[str]) -> tuple[str | None, tuple[str, ...]] |
(primary, extra): the first root and the rest, or (None, ()) when empty. |
existing_roots |
existing_roots(roots: list[str], repo_root: Path) -> tuple[list[str], list[str]] |
Partition roots into (present, missing) relative to repo_root. |
undeclared_message |
undeclared_message() -> str |
The one sentence every consumer prints when the set is empty. |
Source
"""The ONE reader of the declared AST root set.
Every consumer of the code-intelligence snapshot -- ``scripts/serialize_codebase_ast.py``
(which writes ``plans/ast-ultimate.json``), ``scripts/ast_live_sync.py`` (which pushes
saves into the ``orchkg`` graph), and the Prolog KB built from the snapshot -- must agree
on WHICH trees are "our source". Until 2026-10-09 the serializer read the declaration
from ``$ORCHESTRATA_AST_ROOTS`` while the live sync carried its own literal
(``src/orchestrata_ultimate``), so the snapshot held 3,171 modules across six roots and
the graph held 905: every ``scripts/``, ``claude/`` and ``tests/`` module was in the AST
and absent from the graph, and nothing compared the two.
The declaration itself lives in ``env/ast-roots.env`` (tracked), loaded by ``.envrc`` for
interactive shells and by the systemd units via ``EnvironmentFile=``. This module only
PARSES it; it never supplies a fallback list, because a silent fallback is exactly the
narrowing this exists to prevent (the serializer exits 2 on an empty set and the live
sync refuses to start -- see their call sites).
"""
from __future__ import annotations
import os
from collections.abc import Mapping
from pathlib import Path
#: Environment key holding the declared root set, whitespace- or colon-separated,
#: repo-relative, FIRST entry primary (bare dotted module ids), every later entry
#: prefixed with its own directory name (``tests.gates.test_gate_rules``).
AST_ROOTS_ENV = "ORCHESTRATA_AST_ROOTS"
#: The tracked file that declares the set; named in error messages so a reader can
#: find the declaration instead of guessing which shell forgot to run direnv.
AST_ROOTS_FILE = "env/ast-roots.env"
def declared_roots(env: Mapping[str, str] | None = None) -> list[str]:
"""Return the declared root set, in declaration order, or ``[]`` when unset.
``env`` is the injection seam (tests pass an explicit dict); ``None`` reads the
process environment. Empty tokens are dropped; ``:`` is accepted as a separator
alongside whitespace so a PATH-style declaration also parses.
"""
source = os.environ if env is None else env
raw = source.get(AST_ROOTS_ENV, "")
return [tok for tok in raw.replace(":", " ").split() if tok]
def split_roots(roots: list[str]) -> tuple[str | None, tuple[str, ...]]:
"""``(primary, extra)``: the first root and the rest, or ``(None, ())`` when empty."""
if not roots:
return None, ()
return roots[0], tuple(roots[1:])
def existing_roots(roots: list[str], repo_root: Path) -> tuple[list[str], list[str]]:
"""Partition *roots* into ``(present, missing)`` relative to *repo_root*.
Declared roots may legitimately be absent in a given checkout (``vizint-ui``,
``infra``); the serializer treats a missing one as an error on the command
line, while a service that must keep running reports and skips it. Both
decisions belong to the caller -- this only measures.
"""
present = [r for r in roots if (repo_root / r).is_dir()]
missing = [r for r in roots if r not in present]
return present, missing
def undeclared_message() -> str:
"""The one sentence every consumer prints when the set is empty."""
return (
f"No AST roots declared: ${AST_ROOTS_ENV} is empty. It is declared in "
f"{AST_ROOTS_FILE} (loaded by .envrc and by the systemd units' "
"EnvironmentFile=); if it is unset here, that file was not loaded."
)