Agent control contracts

Repository-internal shared contracts for agent-system composition

Agent control contracts

This project owns the repository-internal shared vocabulary used to compose agent-system declarations and derived catalogs. It does not own component payload schemas, user authority, runtime observations, or generated catalogs.

The initial api/v1alpha1 package defines stable references, atomic effects, authority and budget envelopes, independent path-policy axes, information and retention classes, availability reasons, evidence applicability, and the common operation and artifact envelopes. Validation fails closed for unknown effects and malformed identities. JSON decoding is strict and canonical encoding is deterministic.

The Phase 1 registry names the closed registration authorities, declares skill metadata and supported direct binaries, links owner-local operation files, and records generated-artifact ownership. Run the report-only completeness check with:

bazel_agent bazel run //tools/agents/cmd/phase1_check -- \
  --workspace-root "$PWD" \
  --report out/<task>/phase1-report.json

The adjacent criteria-revision-bound resource baseline records numeric ceilings separately from observations. Unavailable observations carry a reason instead of an estimate.

Project roots use exactly one lifecycle value from active, in_progress, maintenance, experimental, finished, or abandoned. These values describe project maintenance state; they do not imply publication or information policy.

Bounded topology catalog

The topology compiler derives a deterministic, offline TopologyCatalog from the registered projects/* universe, top-level boundary READMEs, and tracked MODULE.bazel roots. The portable JSON at tools/agents/catalogs/topology.json is authoritative; the adjacent Markdown renders the same data and states the JSON digest.

Regenerate with:

bazel_agent bazel run //tools/agents/cmd/topology_check:topology_update

Verify the checked artifacts are current with:

bazel_agent bazel test //tools/agents/cmd/topology_check:topology_check_check

The catalog contains no timestamps, absolute checkout paths, or nested authority; every input is bound by a content digest and the catalog digest covers the canonical bytes. Completeness is complete only when every eligible registered project, workspace, and boundary tree is emitted without conflicts or limitations.

Bounded context capsule

The agent_system command emits a bounded offline context capsule for a workspace-relative path or local Bazel label. It joins checked catalogs with the applicable AGENTS.md chain, nearest owner README, and policy source paths. Its digest-bound document links include the nearest package BUILD.bazel or BUILD, include.MODULE.bazel, and workspace MODULE.bazel when present. These locate the source to inspect; they do not select targets. JSON is the default; --markdown or --json=false renders Markdown. Existing symlinks resolve to their canonical workspace paths before ownership and policy selection. Escaping, dangling, or cyclic symlinks are refused; new paths may extend a verified existing parent.

bazel_agent bazel run //tools/agents/cmd/agent_system -- \
  --workspace-root "$PWD" --path projects/agents --markdown

The legacy agent_system_update wrapper also forwards these query flags and does not update tracked files. --task records a caller-supplied task ID; it does not select a goal. Input digests bind accepted catalog and document inputs.

Relative --workspace-root values resolve against BUILD_WORKSPACE_DIRECTORY under bazel run, so --workspace-root . means the source workspace. Outside Bazel they resolve against the current directory. normalize, coverage, and aggregate apply the same rule to relative input, catalog, and output file flags, with optional --workspace-root selecting another base. Absolute file paths retain their meaning; output parent directories must already exist. For example:

bazel_agent bazel run //tools/agents/cmd/agent_system -- aggregate \
  --catalog tools/agents/catalogs/capability.json \
  --input tools/agents/catalogs/skill-cases.json \
  --output out/<task>/coverage.json

The context command observes local Git HEAD and dirty state under the selected workspace, including untracked files and excluding ignored files. The identity.git object separates these observations from optional caller --revision and --dirty-inputs declarations. Without overrides, successful observations populate the existing identity fields; revisionSource and dirtyInputsSource name their basis. Failed Git reads leave git.dirty: null or the revision absent with explicit reasons. Legacy identity fields then retain an input digest and a conservative dirty default, clearly labelled.

Git commands have a combined three-second deadline and a 65,536-byte output limit each, disable optional index writes and fsmonitor hooks, and ignore ambient GIT_* variables. The report contains no changed-path list or Git stderr. Git and catalog reads are separate observations, not an atomic snapshot or proof that generated catalogs match HEAD.

The capsule reports the actual read time, unknown catalog freshness, and partial completeness. It does not observe runtime health, infer authority, resolve effective CODEOWNERS, or associate an unrelated active goal. Its capability list is an inventory of root-workspace and selected-workspace candidates, not task-intent routing or a complete cross-workspace dependency closure. Workspace check phases are references, not instructions to run a broad check.

identity.byteSize measures the complete canonical JSON, including its final newline. --max-bytes caps the selected JSON or Markdown output at 65,536 bytes by default; oversized output fails before writing a partial document. The limit may be set explicitly between 1,024 and 1,048,576 bytes. Verification:

bazel_agent bazel test //tools/agents/cmd/agent_system:agent_system_check

Runtime control kernel and status

The tools/agents/control package owns the fixed runtime control kernel: per-package states and deadlines, account/lock acquisition, expires-checked leases, and expected-revision asset publication with namespace isolation. It persists an offline-readable packages.json snapshot.

Package registration preserves the caller’s scope, desired revision, and contract hash. Activation deadlines are fixed at registration time; KernelOptions.Deadlines are fixed at kernel creation for packages introduced through Mark. Reading status does not extend those deadlines. Lifecycle transitions do not establish revision identity, so observedRevision remains empty: the current API has no observed revision input.

The control_status command renders the snapshot/asset status for a control root without creating directories or starting a kernel (Markdown with --markdown):

bazel_agent bazel run //tools/agents/cmd/control_status:control_status -- \
  --workspace-root "$PWD" [--markdown]

The JSON healthy field describes the persisted package observations, with healthBasis: persisted-package-snapshot. Ready, degraded, and disabled packages count as healthy, matching the kernel. Missing, empty, malformed, or rejected observations produce healthy: null; consumers must support this unavailable value rather than assuming a boolean is always present. snapshotPath identifies the source. The report’s observedAt is its read time; each package retains the writer’s observationTime.

Freshness is unknown unless the caller supplies a positive duration such as --max-snapshot-age 5m. That bound yields within-age-bound or stale; stale observations make snapshot health unavailable. This is an explicit caller policy, not a heartbeat or runtime liveness guarantee. Live runtime health remains unavailable even when every observation is within the bound. The snapshot format does not identify its writer, so report runtimeId is empty with an unavailable reason. An asset’s runtimeId identifies its publisher only.

Verification:

bazel_agent bazel test //tools/agents/control:all //tools/agents/cmd/control_status:all