Skip to content

AIP v1 objects

Status: experimental milestone 1. MUST, MUST NOT, and SHOULD are normative. All canonical objects are immutable. An object’s identity is SHA-256 of its complete deterministic CBOR encoding. IDs are not fields inside their own objects. The envelope is {type: text, version: unsigned integer, data: map}. Versions are scoped to each object type. All six original schemas use version 1; executed evidence uses evidence version 2 below. Unknown type/version pairs, fields, missing fields, and incorrectly typed fields MUST be rejected.

Graph and invariants

An operation references an existing base state, intent, input artifacts, and replacement artifacts. A resulting state references that operation, its parent state, intent, and complete artifact mapping. Evidence references an existing state and optionally operations that produced it. Decisions reference existing objects. References MUST resolve to objects of the expected type.

State -> Operation -> Intent

State -> parents (State), files (Artifact)

Evidence -> State, Operation

Decision -> related objects

There are no mutual hash references. The operation’s resulting states and a state’s evidence are reverse queries over immutable objects, not canonical fields. Under the v1 schema, an operation determines at most one valid resulting state: its base, intent, effects, and operation ID determine every state field. An operation may be stored before its resulting state. Reverse queries return lists so future schemas can extend the relationship. Repository indexes are rebuildable caches and never authoritative protocol objects.

Schemas

All fields below are required. ID is a lowercase 64-character SHA-256 hex text string. IDs is an array sorted lexicographically without duplicates. Empty collections use empty arrays/maps, never null. Times are UTC RFC3339 strings with exactly nine fractional digits, e.g. 2026-01-01T00:00:00.000000000Z. Text MUST be valid UTF-8; it is preserved without Unicode normalization. nonempty text must contain a character outside Unicode White_Space: U+0009 through U+000D, U+0020, U+0085, U+00A0, U+1680, U+2000 through U+200A, U+2028, U+2029, U+202F, U+205F, and U+3000 are whitespace for this requirement.

artifact

kind: "file", content: bytes.

Paths and executable permissions belong to the state’s file entries, allowing identical contents to share an artifact at different paths.

intent

title: nonempty text, goal: nonempty text, constraints: array of text, context: IDs, parents: IDs, created_at: time. Context can reference any known object. Parents MUST reference intents. Constraint order is significant and preserved.

operation

intent: ID, base_state: ID, inputs: IDs, effects: array of effect. An effect is {path: path, before: file-entry or null, after: file-entry or null}. A file entry is {artifact: ID, executable: boolean}. Effects are sorted by path, unique, and MUST change something. Both sides cannot be null. before MUST match the base state’s entry, including absence. Inputs MUST equal the sorted distinct artifact IDs in all non-null before entries. Applying the effects to the base MUST exactly produce the resulting file map. Empty effects are permitted for an explicitly recorded no-change operation.

state

files: map of path to file-entry, parents: IDs, operations: IDs, intents: IDs. Paths are relative slash-separated valid UTF-8, without empty, . or .. components, backslashes, NUL, or a top-level .aip component. A file path cannot be an ancestor of another file path. Paths are case-sensitive.

The root state has empty files, parents, operations, and intents. Every other v1 state has exactly one parent and one producing operation. Its parent MUST equal the operation’s base, and its intents MUST equal the operation’s one intent. Applying the operation MUST produce its files. The schema deliberately uses arrays so future versions can specify synthesis with multiple parents and operations; v1 MUST reject unspecified synthesis semantics.

evidence

state: ID, operations: IDs, kind: nonempty text, command: nonempty text, result: "pass" | "fail" | "unknown", provenance: "reported", recorded_at: time, cwd: text, platform: nonempty text, reporter: nonempty text, notes: text. Operations MUST be a subset of the referenced state’s producing operations. The command is a claim about execution, never executable protocol content. Evidence v1 records user/agent reports; creating it does not run the command or assert that a report is trustworthy. Executed observations use evidence v2 below. Cwd is repository-relative (. for the root). Recorded time is not an asserted execution time. Notes can capture environment, tool versions, execution time, and output when supplied by the reporter.

decision

question: nonempty text, decision: nonempty text, rationale: nonempty text, alternatives: array of text, related: IDs, created_at: time. Alternatives retain their supplied order. Related objects can have any supported type/version pair, including evidence v2.

evidence, version 2: executed

All fields are required:

  • state: ID, operations: IDs, kind: nonempty text.
  • provenance: "executed", argv: nonempty array of text, executable: text, cwd: ".", platform: nonempty text, runner: nonempty text, environment: "inherited".
  • started_at: time, finished_at: time, duration_ns: unsigned integer, timeout_ns: positive unsigned integer.
  • termination: "exited" | "signaled" | "timeout" | "cancelled" | "start_error" | "capture_error", exit_code: unsigned integer or null, error: text, result: "pass" | "fail" | "unknown".
  • stdout: capture, stderr: capture, output_limit: positive unsigned integer.
  • workspace: "clean" | "changed" | "unknown", workspace_error: text.

A capture is {artifact: ID, truncated: boolean}. Artifacts contain the retained raw byte prefix, including empty streams. Each artifact’s content length MUST be at most output_limit; if truncated is true its length MUST equal output_limit. The runner drains and discards bytes beyond the limit. A missing pipe EOF or another capture failure is capture_error, not just truncation. Other termination failures may also leave capture incomplete. Only exited promises complete stream drainage (subject to explicit truncation).

Argv preserves argument order, whitespace, empty arguments, and shell metacharacters literally. The first element MUST be nonempty; no element may contain NUL. Executable is the resolved absolute executable path, or empty only when lookup failed with start_error. Absolute path text begins with /, a drive letter plus :/ or :\, or a UNC \\ prefix followed by a nonempty remainder. Readers accept these forms independently of their host platform. Arguments are never interpreted as protocol instructions. Executables, inherited environment values, external tools, networks, and clocks are not made reproducible merely by recording this object. Environment variable values are not captured. Runner identifies the implementation, not an identity or signature. This provenance describes observation, not authenticated truth.

Exit_code is present only for exited, in range 0..4294967295; otherwise it is null. With successful workspace inspection, normal nonzero exit yields fail and normal zero exit yields pass. Failed inspection yields unknown. All non-exited terminations yield unknown. Error MUST be empty for exited and nonempty otherwise. Workspace error MUST be nonempty exactly when workspace is unknown. A changed workspace does not override a command’s exit result; consumers must inspect both fields.

The same operation membership invariant as v1 applies. Stream references MUST target artifact objects. Elapsed duration uses a monotonic clock; UTC wall times may move backwards and are not required to agree with duration_ns. Timeout is the configured command deadline, not a bound on materialization or post-run inspection.

Workspace is an end-to-end comparison of the materialized project before and after execution, covering artifact bytes and executability. It cannot detect changes subsequently restored. Inspection failure or uncertain subprocess cleanup yields unknown. Creating top-level .aip in the evaluation directory also yields unknown; it must not be silently ignored as metadata. Ordinary empty directories and unversioned metadata do not affect the comparison.

Existing evidence v1 objects and IDs are unchanged. Older implementations MUST reject evidence v2 explicitly. Encoding rules, repository config, and the other five object types remain at version 1. New readers accept both evidence versions.

Verification and evolution

Byte/hash verification and schema verification are necessary but insufficient: graph verification MUST also check reference types and operation/state consistency. Missing references and cycles are invalid. Readers MUST bound object size and nesting before allocating from untrusted lengths. An unsupported version is an error, never permission to reinterpret bytes as v1.

Future semantics require a new version or object type with a written schema. JSON and text are presentation formats only. No Go serialization behavior forms part of the protocol.