Skip to content

Committed-tree checks

Git execution uses the shared runtime with global --no-lazy-fetch; missing objects fail rather than triggering a network fetch. See usage modes for the common execution boundaries.

yaga tree check validates the tracked leaf paths in one explicit commit against one explicit versioned policy. It is a snapshot invariant check: change check answers whether one delta has the required companion changes, while tree check answers whether the resulting committed tree contains required project files and excludes known local or generated artifacts.

# .yaga/tree-policy.toml
tree-policy-version = 1
required-paths = [
  ".yaga/tree-policy.toml",
  "LICENSE",
  "README.md",
  "SECURITY.md",
  "pyproject.toml",
  "uv.lock",
]
forbidden-patterns = [
  "**/.env",
  "**/__pycache__/**",
  "**/.venv/**",
  "**/*.pyc",
  "build/**",
  "dist/**",
]
yaga tree check \
  --policy .yaga/tree-policy.toml \
  --revision HEAD

Both arrays are required in schema version 1 and either may be empty, but the policy must contain one through 128 entries in total. Required paths are exact. Forbidden patterns are anchored and case-sensitive, with component-local * and whole-component **; a required path that is also forbidden makes the policy invalid. Paths are canonical repository-relative UTF-8 POSIX names, bounded to 4,096 bytes and 64 components. Selected trees are bounded to 50,000 leaf paths and 64 MiB of Git output. Pattern evaluation shares a hard 10,000,000-work-unit ceiling.

YAGA resolves the selected value as exactly one commit and enumerates its tree with bounded, NUL-delimited Git output. It never fetches, reads tracked file contents, follows a submodule, or consults the worktree, index, or untracked files. Regular and executable files, symlinks, and gitlinks all count as leaf paths. Legacy graft overlays are rejected before revision resolution and rechecked after enumeration. A shallow checkout is acceptable when the selected commit and tree objects already exist. The runtime checks the outermost enclosing repository even when --repo names a worktree subdirectory, plus bounded resolved .git, linked-worktree commondir, object-directory, and local alternate-object-store metadata. Alternate chains are cycle-safe and limited to 128 directories. Repository pointer and alternate metadata must be bounded regular files that retain their identity while opened; special files fail closed before Git starts. This is path policy, not secret-content detection.

Missing paths report tree.required; tracked paths matching the first configured forbidden pattern report tree.forbidden. Text, versioned JSON, and escaped GitHub reports return exit 0 for a clean tree, exit 1 for findings, and exit 2 for policy, revision, repository, Git, or resource-limit errors. Reports bound displayed diagnostics while retaining exact checked-entry and finding counts.

The policy file is an explicit runtime input and may differ from the selected tree; the CLI does not claim policy provenance. CI should check out the exact source commit, keep the workflow unprivileged, and pass that same commit through a quoted environment variable:

- name: Check committed-tree policy
  env:
    YAGA_TREE_REVISION: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.sha }}
  run: >-
    uv run yaga tree check
    --policy .yaga/tree-policy.toml
    --revision "$YAGA_TREE_REVISION"
    --format github

The Git boundary uses documented rev-parse --verify --end-of-options type peeling and recursive, full-tree, NUL-delimited ls-tree enumeration. Git metadata and alternate object-store resolution follows the documented gitrepository-layout.

See command index for other checks and adoption recipes for CI setup.