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.