This document describes the AI object model in git-internal: the types,
lifecycle, relationships, and usage patterns for AI-assisted code generation
workflows. All types live under src/internal/object/.
The AI object model extends Git's native object types (Blob, Tree, Commit, Tag) with a set of workflow objects that capture the full lifecycle of an AI-assisted code change β from the initial user request to the final committed patch.
User input
β
βΌ
Intent βββΆ Plan βββΆ Task βββΆ Run βββΆ PatchSet βββΆ Decision
β
ββββΆ ToolInvocation (action log)
ββββΆ Evidence (validation results)
ββββΆ Provenance (LLM metadata)
Context is tracked by two complementary mechanisms:
- ContextSnapshot β static capture of files/URLs/snippets at Run start
- ContextPipeline β dynamic accumulation of incremental context frames throughout execution
β User input
β
βΌ
β‘ Intent (Draft β Active)
β
ββββΆ ContextPipeline β seeded with IntentAnalysis frame
β
βΌ
β’ Plan (pipeline, fwindow, steps)
β
ββ PlanStepβ (inline)
ββ PlanStepβ ββtaskβββΆ sub-Task (recursive)
ββ PlanStepβ (inline)
β
βΌ
β£ Task (Draft β Running)
β
βΌ
β€ Run (Created β Patching β Validating β Completed/Failed)
β
ββββΆ Provenance (1:1, LLM config + token usage)
ββββΆ ContextSnapshot (optional, static context at start)
β
β ββββ agent execution loop ββββ
β β β
β β β₯ ToolInvocation (1:N) β β action log
β β β β
β β βΌ β
β β β¦ PatchSet (Proposed) β β candidate diff
β β β β
β β βΌ β
β β β§ Evidence (1:N) β β test/lint/build
β β β β
β β ββ pass ββββββββββββββ
β β ββ fail β new PatchSet (retry within Run)
β ββββββββββββββββββββββββββββββ
β
βΌ
β¨ Decision (terminal verdict)
β
ββ Commit β apply PatchSet, record result_commit
ββ Retry β create new Run β€ for same Task
ββ Abandon β mark Task as Failed
ββ Checkpoint β save state, resume later
ββ Rollback β revert applied PatchSet
β
βΌ
β© Intent (Completed) β commit recorded
-
User input β the user provides a natural-language request.
-
Intent β captures the raw prompt and the AI's structured interpretation. Status transitions from
Draft(prompt only) toActive(analysis complete). Supports conversational refinement viaparentchain. -
Plan β a sequence of
PlanSteps derived from the Intent. References aContextPipelineand records the visible frame range (fwindow). Steps track consumed/produced frames by stable ID (iframes/oframes). A step may spawn a sub-Task for recursive decomposition. Plans form a revision chain viaprevious. -
Task β a unit of work with title, constraints, and acceptance criteria. May link back to its originating Intent. Accumulates Runs in
runs(chronological execution history). -
Run β a single execution attempt of a Task. Records the baseline
commit, the Plan version being executed (snapshot reference), and the hostenvironment. AProvenance(1:1) captures the LLM configuration and token usage. -
ToolInvocation β the finest-grained record: one per tool call (read file, run command, etc.). Forms a chronological action log for the Run. Tracks file I/O via
io_footprint. -
PatchSet β a candidate diff generated by the agent. Contains the diff
artifact, file-leveltouchedsummary, andrationale. Starts asProposed; transitions toAppliedorRejected. Ordering is by position inRun.patchsets. -
Evidence β output of a validation tool (test, lint, build) run against a PatchSet. One per tool invocation. Carries
exit_code,summary, andreport_artifacts. Feeds into the Decision. -
Decision β the terminal verdict of a Run. Selects a PatchSet to apply (
Commit), retries the Task (Retry), gives up (Abandon), saves progress (Checkpoint), or reverts (Rollback). Recordsrationaleandresult_commit_sha. -
Intent completed β the orchestrator records the final git commit in
Intent.commitand transitions status toCompleted.
| From | Field | To | Cardinality |
|---|---|---|---|
| Intent | parent |
Intent | 0..1 |
| Intent | plan |
Plan | 0..1 |
| Plan | previous |
Plan | 0..1 |
| Plan | pipeline |
ContextPipeline | 0..1 |
| PlanStep | task |
Task | 0..1 |
| Task | parent |
Task | 0..1 |
| Task | intent |
Intent | 0..1 |
| Task | runs |
Run | 0..N |
| Task | dependencies |
Task | 0..N |
| Run | task |
Task | 1 |
| Run | plan |
Plan | 0..1 |
| Run | snapshot |
ContextSnapshot | 0..1 |
| Run | patchsets |
PatchSet | 0..N |
| PatchSet | run |
Run | 1 |
| Evidence | run_id |
Run | 1 |
| Evidence | patchset_id |
PatchSet | 0..1 |
| Decision | run_id |
Run | 1 |
| Decision | chosen_patchset_id |
PatchSet | 0..1 |
| Provenance | run_id |
Run | 1 |
| ToolInvocation | run_id |
Run | 1 |
The entry point of every AI-assisted workflow. Captures the verbatim user
request (prompt) and the AI's structured interpretation (content).
Draft βββΆ Active βββΆ Completed
β β
ββββββββββββ΄βββΆ Cancelled
- Draft: Prompt recorded, AI has not analyzed it yet.
content = None. - Active: AI interpretation available in
content. Plan, Tasks, and Runs may be in progress. - Completed: All downstream work finished.
commitcontains the result git commit hash. - Cancelled: Abandoned before completion (user interrupt, timeout, budget).
| Field | Type | Description |
|---|---|---|
header |
Header |
Common metadata (ID, type, timestamps, creator) |
prompt |
String |
Verbatim user input, immutable after creation |
content |
Option<String> |
AI-analyzed interpretation; None in Draft |
parent |
Option<Uuid> |
Predecessor Intent for conversational refinement |
commit |
Option<IntegrityHash> |
Result git commit, set at step β© |
plan |
Option<Uuid> |
Latest Plan revision derived from this Intent |
statuses |
Vec<StatusEntry> |
Append-only status transition history |
Intentβ ("Add pagination")
β²
β parent
Intentβ ("Also add cursor-based pagination")
β²
β parent
Intentβ ("Use opaque cursors, not offsets")
Each follow-up Intent links to its predecessor via parent, forming a
singly-linked list from newest to oldest.
use git_internal::internal::object::intent::{Intent, IntentStatus};
use git_internal::internal::object::types::ActorRef;
// 1. Create from user input
let actor = ActorRef::human("alice")?;
let mut intent = Intent::new(actor, "Add pagination to the user list API")?;
assert_eq!(intent.status(), &IntentStatus::Draft);
// 2. AI analyzes the prompt
intent.set_content(Some("Add offset/limit pagination to GET /users".into()));
intent.set_status(IntentStatus::Active);
// 3. After execution completes
intent.set_status_with_reason(IntentStatus::Completed, "All tasks finished");A sequence of PlanSteps derived from an Intent's analyzed content. Defines
what to do (strategy and decomposition); Run handles how to execute.
When the agent encounters obstacles, it creates a revised Plan via
Plan::new_revision. Each revision links back via previous:
Intent.plan βββΆ Plan_v3 (latest)
β previous
βΌ
Plan_v2
β previous
βΌ
Plan_v1 (original, previous = None)
Intent.plan always points to the latest revision. Each Run.plan is a
snapshot reference that never changes.
A Plan references a ContextPipeline via pipeline and records the visible
frame range fwindow = (start, end) β the half-open range [start..end) of
frames that were visible at creation time.
ContextPipeline.frames: [Fβ, Fβ, Fβ, Fβ, Fβ, Fβ
, ...]
^^^^^^^^^^^^^^^^
fwindow = (0, 4)
| Field | Type | Description |
|---|---|---|
header |
Header |
Common metadata |
previous |
Option<Uuid> |
Predecessor Plan in revision chain |
pipeline |
Option<Uuid> |
ContextPipeline used as context basis |
fwindow |
Option<(u32, u32)> |
Visible frame range [start..end) |
steps |
Vec<PlanStep> |
Ordered sequence of steps |
Each step describes one unit of work within a Plan.
| Field | Type | Description |
|---|---|---|
description |
String |
What this step should accomplish |
inputs |
Option<Value> |
Expected inputs (JSON) |
outputs |
Option<Value> |
Outputs after execution (JSON) |
checks |
Option<Value> |
Validation criteria (JSON) |
iframes |
Vec<u64> |
Stable pipeline frame IDs consumed (survives eviction) |
oframes |
Vec<u64> |
Stable pipeline frame IDs produced (survives eviction) |
task |
Option<Uuid> |
Sub-Task for recursive decomposition |
statuses |
Vec<StepStatusEntry> |
Append-only status history |
Step status transitions:
Pending βββΆ Progressing βββΆ Completed
β β
βββββββββββββββ΄βββΆ Failed
ββββββββββββββββββββΆ Skipped
A step can spawn a sub-Task via its task field:
Plan
ββ Stepβ (inline β executed by current Run)
ββ Stepβ ββtaskβββΆ Taskβ
β ββ Run β Plan
β ββ Stepβββ
β ββ Stepβββ
ββ Stepβ (inline)
use git_internal::internal::object::plan::{Plan, PlanStep};
use git_internal::internal::object::types::ActorRef;
let actor = ActorRef::agent("planner")?;
// Create initial plan
let mut plan = Plan::new(actor.clone())?;
plan.set_pipeline(Some(pipeline_id));
plan.set_fwindow(Some((0, 3)));
// Add steps
let mut step = PlanStep::new("Refactor auth module");
step.set_iframes(vec![0, 1]); // consumed frames 0 and 1
plan.add_step(step);
// Create a revision
let plan_v2 = plan.new_revision(actor)?;
assert_eq!(plan_v2.previous(), Some(plan.header().object_id()));A unit of work with constraints and acceptance criteria. The stable identity for a piece of work β persists across retries and replanning.
Draft βββΆ Running βββΆ Done
β β
ββββββββββββ΄βββΆ Failed
ββββββββββββββββΆ Cancelled
| Field | Type | Description |
|---|---|---|
header |
Header |
Common metadata |
title |
String |
Short summary (< 100 chars) |
description |
Option<String> |
Extended context |
goal |
Option<GoalType> |
Work classification (Feature, Bugfix, etc.) |
constraints |
Vec<String> |
Hard rules the solution must satisfy |
acceptance_criteria |
Vec<String> |
Testable success conditions |
requester |
Option<ActorRef> |
Who requested the work |
parent |
Option<Uuid> |
Parent Task (for sub-Tasks) |
intent |
Option<Uuid> |
Originating Intent |
runs |
Vec<Uuid> |
Chronological execution history |
dependencies |
Vec<Uuid> |
Tasks that must complete first |
status |
TaskStatus |
Current lifecycle status |
Feature, Bugfix, Refactor, Docs, Perf, Test, Chore, Build,
Ci, Style, Other(String).
The Task stays the same across Plan revisions. Each Run records which Plan version it executed:
Task (constant) Intent (constant, plan updated)
β ββ plan βββΆ Plan_v2 (latest)
ββ runs:
Runβ ββplanβββΆ Plan_v1 (snapshot: original plan)
Runβ ββplanβββΆ Plan_v2 (snapshot: revised plan)
use git_internal::internal::object::task::{Task, GoalType};
use git_internal::internal::object::types::ActorRef;
let actor = ActorRef::human("user")?;
let mut task = Task::new(actor, "Refactor Login", Some(GoalType::Refactor))?;
task.add_constraint("Must use JWT");
task.add_acceptance_criterion("All tests pass");
task.set_intent(Some(intent_id));
// After Run completes
task.add_run(run_id);
task.set_status(TaskStatus::Done);A single execution attempt of a Task. Captures the execution context (baseline commit, environment, Plan version) and accumulates artifacts (PatchSets, Evidence, ToolInvocations) during execution.
Created βββΆ Patching βββΆ Validating βββΆ Completed
β β
ββββββββββββββββ΄βββΆ Failed
- Created: Run initialized, environment captured.
- Patching: Agent is generating code changes / tool calls.
- Validating: Agent has produced a PatchSet and is running tests/lint.
- Completed: Decision has been created.
- Failed: Unrecoverable error.
Run.errorhas details.
| Field | Type | Description |
|---|---|---|
header |
Header |
Common metadata |
task |
Uuid |
Owning Task (mandatory) |
plan |
Option<Uuid> |
Plan version being executed (snapshot) |
commit |
IntegrityHash |
Baseline git commit |
status |
RunStatus |
Current lifecycle status |
snapshot |
Option<Uuid> |
ContextSnapshot at Run start |
patchsets |
Vec<Uuid> |
Candidate diffs (chronological) |
metrics |
Option<Value> |
Execution metrics (JSON) |
error |
Option<String> |
Error message if Failed |
environment |
Option<Environment> |
Host OS/arch/cwd |
| Object | Cardinality | Purpose |
|---|---|---|
| Provenance | 1:1 | LLM config + token usage |
| ToolInvocation | 1:N | Chronological action log |
| Evidence | 1:N | Validation results (test/lint/build) |
| Decision | 1:1 | Terminal verdict |
use git_internal::internal::object::run::Run;
use git_internal::internal::object::types::ActorRef;
let actor = ActorRef::agent("orchestrator")?;
let mut run = Run::new(actor, task_id, "abc123def...")?;
run.set_plan(Some(plan_id));
// Agent generates patches
run.set_status(RunStatus::Patching);
run.add_patchset(patchset_id);
// Validation phase
run.set_status(RunStatus::Validating);
// Completed
run.set_status(RunStatus::Completed);A candidate diff generated by the agent during a Run. The atomic unit of code modification β every change the agent wants to make is packaged as a PatchSet.
(created) βββΆ Proposed
β
ββββββββββββββββΌβββββββββββββββ
β β β
βΌ βΌ β
Applied Rejected β
β β
βΌ β
agent generates new PatchSet
appended to Run.patchsets
| Field | Type | Description |
|---|---|---|
header |
Header |
Common metadata |
run |
Uuid |
Owning Run |
commit |
IntegrityHash |
Baseline commit this diff applies to |
format |
DiffFormat |
Unified or GitDiff |
artifact |
Option<ArtifactRef> |
Reference to stored diff content |
touched |
Vec<TouchedFile> |
File-level change summary |
rationale |
Option<String> |
Agent's explanation of what/why changed |
apply_status |
ApplyStatus |
Proposed, Applied, or Rejected |
The rationale field bridges the gap between the Task/Plan (high-level intent)
and the raw diff (low-level changes). It is primarily written by the agent and
may be overridden by a human reviewer via set_rationale().
Output of a single validation step (test, lint, build) run against a PatchSet. One Evidence per tool invocation.
| Field | Type | Description |
|---|---|---|
header |
Header |
Common metadata |
run_id |
Uuid |
Owning Run |
patchset_id |
Option<Uuid> |
PatchSet being validated (None for run-level checks) |
kind |
EvidenceKind |
Test, Lint, Build, or Other(String) |
tool |
String |
Tool name (e.g. "cargo", "eslint", "pytest") |
command |
Option<String> |
Full command line for reproducibility |
exit_code |
Option<i32> |
Process exit code (0 = success) |
summary |
Option<String> |
Short result summary |
report_artifacts |
Vec<ArtifactRef> |
Full report files (logs, coverage, JUnit XML) |
The terminal verdict of a Run. Created once per Run at the end of execution.
| Type | Action | chosen_patchset_id |
result_commit_sha |
|---|---|---|---|
Commit |
Apply the chosen PatchSet | Set | Set after commit |
Checkpoint |
Save intermediate progress | β | β |
Abandon |
Give up on the Task | β | β |
Retry |
Create new Run for same Task | β | β |
Rollback |
Revert applied PatchSet | β | β |
| Field | Type | Description |
|---|---|---|
header |
Header |
Common metadata |
run_id |
Uuid |
The Run this Decision concludes |
decision_type |
DecisionType |
Verdict (Commit/Retry/Abandon/etc.) |
chosen_patchset_id |
Option<Uuid> |
Selected PatchSet (for Commit) |
result_commit_sha |
Option<IntegrityHash> |
Git commit hash after applying |
checkpoint_id |
Option<String> |
Saved state ID (for Checkpoint) |
rationale |
Option<String> |
Explanation of why this decision was made |
Records how a Run was executed: which LLM provider, model, and parameters were used, and how many tokens were consumed. Created once per Run (1:1).
| Field | Type | Description |
|---|---|---|
header |
Header |
Common metadata |
run_id |
Uuid |
The Run this Provenance describes |
provider |
String |
LLM provider ("openai", "anthropic", "local") |
model |
String |
Model ID ("gpt-4", "claude-opus-4-20250514") |
parameters |
Option<Value> |
Provider-specific raw parameters (JSON) |
temperature |
Option<f64> |
Sampling temperature (0.0 = deterministic) |
max_tokens |
Option<u64> |
Maximum generation length |
token_usage |
Option<TokenUsage> |
Token consumption and cost |
| Field | Type | Description |
|---|---|---|
input_tokens |
u64 |
Tokens in the prompt/input |
output_tokens |
u64 |
Tokens in the completion/output |
total_tokens |
u64 |
input_tokens + output_tokens |
cost_usd |
Option<f64> |
Estimated cost in USD |
The finest-grained record: one per tool call made by the agent during a Run.
| Field | Type | Description |
|---|---|---|
header |
Header |
Common metadata |
run_id |
Uuid |
Owning Run |
tool_name |
String |
Registered tool name ("read_file", "bash", etc.) |
io_footprint |
Option<IoFootprint> |
Files read/written |
args |
Value |
Arguments (JSON, tool-dependent schema) |
status |
ToolStatus |
Ok or Error |
result_summary |
Option<String> |
Short output summary |
artifacts |
Vec<ArtifactRef> |
Full output files |
| Field | Type | Description |
|---|---|---|
paths_read |
Vec<String> |
Files read (repo-relative) |
paths_written |
Vec<String> |
Files written (repo-relative) |
A static capture of the codebase and external resources the agent observed when a Run began. Point-in-time β does not change after creation.
| Field | Type | Description |
|---|---|---|
header |
Header |
Common metadata |
selection_strategy |
SelectionStrategy |
Explicit (user-chosen) or Heuristic (auto-selected) |
items |
Vec<ContextItem> |
Context items (files, URLs, snippets, etc.) |
summary |
Option<String> |
Aggregated summary |
Each item has three layers:
pathβ human-readable locator (repo path, URL, command, label)blobβ Git blob hash pointing to full content at capture timepreviewβ truncated text for quick display
| Kind | path example |
blob content |
|---|---|---|
File |
src/main.rs |
Same blob in git tree (zero extra storage) |
Url |
https://docs.rs/... |
Fetched page content |
Snippet |
"design notes" |
Snippet text |
Command |
cargo test |
Command output |
Image |
screenshot.png |
Image binary |
Blobs referenced only by AI objects are not reachable in git's DAG and will be
pruned by git gc. For non-File items, applications must choose a retention
strategy:
| Strategy | Approach |
|---|---|
| Ref anchoring | refs/ai/blobs/<hex> |
| Orphan commit | refs/ai/uploads tree |
| Keep pack | .keep marker on pack file |
| Custom GC mark | Scan AI objects during GC |
A dynamic context container that accumulates incremental ContextFrames
throughout the workflow. Solves the context-forgetting problem in long-running
tasks.
Intent (Active) β content analyzed
β
βΌ
ContextPipeline created β seeded with IntentAnalysis frame
β
βΌ
Plan created β Plan.pipeline β Pipeline, Plan.fwindow = range
β steps execute
βΌ
Frames accumulate β StepSummary, CodeChange, ToolCall, ...
β
βΌ
Replan? β new Plan with updated fwindow
| Field | Type | Description |
|---|---|---|
header |
Header |
Common metadata |
frames |
Vec<ContextFrame> |
Chronologically ordered frames |
next_frame_id |
u64 |
Monotonic counter for stable frame IDs |
max_frames |
u32 |
Max frames before eviction (0 = unlimited) |
global_summary |
Option<String> |
Aggregated summary |
| Field | Type | Description |
|---|---|---|
frame_id |
u64 |
Stable monotonic ID (survives eviction) |
kind |
FrameKind |
IntentAnalysis, StepSummary, CodeChange, etc. |
summary |
String |
Compact human-readable summary |
data |
Option<Value> |
Structured payload (JSON) |
created_at |
DateTime<Utc> |
When this frame was created |
token_estimate |
Option<u64> |
Estimated tokens for budgeting |
| Kind | Protected | Description |
|---|---|---|
IntentAnalysis |
Yes | Seed frame from Intent analysis |
StepSummary |
No | Summary after a PlanStep completes |
CodeChange |
No | Code change digest (files, diff stats) |
SystemState |
No | System/environment state snapshot |
ErrorRecovery |
No | Error recovery context |
Checkpoint |
Yes | Explicit save-point |
ToolCall |
No | External tool invocation result |
Other(String) |
No | Application-defined |
Protected frames survive eviction when max_frames is exceeded.
When max_frames > 0 and the limit is exceeded, push_frame removes the
oldest non-protected frame. IntentAnalysis and Checkpoint frames are
never evicted.
Steps track their relationship to frames via stable frame IDs:
ContextPipeline.frames: [Fβ, Fβ, Fβ, Fβ, Fβ, Fβ
]
β β β²
β°βββββ― β
iframes=[0,1] oframes=[4]
β°ββ Stepβ βββ―
iframesβ stableframe_ids of frames the step consumed as contextoframesβ stableframe_ids of frames the step produced
Frame IDs are monotonic integers assigned by push_frame. Unlike Vec
indices, IDs survive eviction β a step's iframes remain valid even
after older frames are removed. Look up frames via frame_by_id.
All association is owned by the step; ContextFrame has no back-references.
use git_internal::internal::object::pipeline::{ContextPipeline, FrameKind};
use git_internal::internal::object::types::ActorRef;
let actor = ActorRef::agent("orchestrator")?;
// 1. Create pipeline after Intent content is analyzed
let mut pipeline = ContextPipeline::new(actor)?;
// 2. Seed with the Intent's analyzed content β returns stable frame_id
let seed_id = pipeline.push_frame(
FrameKind::IntentAnalysis,
"Add offset/limit pagination to GET /users with default page size 20",
);
// 3. Create a Plan referencing this pipeline
// plan.set_pipeline(Some(pipeline.header().object_id()));
// plan.set_fwindow(Some((0, pipeline.frames().len() as u32)));
// 4. As steps complete, push incremental frames
let step_id = pipeline.push_frame(FrameKind::StepSummary, "Refactored auth module");
// 5. Track on PlanStep side using stable frame IDs:
// step.set_iframes(vec![seed_id]);
// step.set_oframes(vec![step_id]);
// 6. Look up frame by ID (survives eviction)
// pipeline.frame_by_id(seed_id);All AI objects share a common Header (flattened into the JSON via #[serde(flatten)]):
| Field | Type | Description |
|---|---|---|
object_id |
Uuid |
Globally unique ID (UUID v7, time-ordered) |
object_type |
ObjectType |
Discriminator (Intent, Plan, Task, etc.) |
header_version |
u32 |
Format version of the Header struct |
schema_version |
u32 |
Per-object-type schema version |
created_at |
DateTime<Utc> |
Creation timestamp |
updated_at |
DateTime<Utc> |
Last modification timestamp |
created_by |
ActorRef |
Who created this object |
visibility |
Visibility |
Private or Public |
tags |
HashMap<String, String> |
Free-form search tags |
external_ids |
HashMap<String, String> |
External ID mapping |
checksum |
Option<IntegrityHash> |
Content checksum (set by seal()) |
Identifies who created or triggered an action:
| Kind | Factory | Example |
|---|---|---|
Human |
ActorRef::human("alice") |
A user |
Agent |
ActorRef::agent("coder") |
An AI agent |
System |
ActorRef::system("scheduler") |
Infrastructure |
McpClient |
ActorRef::mcp_client("vscode") |
MCP client |
Reference to external content stored outside the AI object:
| Field | Type | Description |
|---|---|---|
store |
String |
Storage backend ("local", "s3") |
key |
String |
Storage key or path |
content_type |
Option<String> |
MIME type |
size_bytes |
Option<u64> |
Size in bytes |
hash |
Option<IntegrityHash> |
Content hash (SHA-256) |
expires_at |
Option<DateTime<Utc>> |
Expiration time |
Supports integrity verification via verify_integrity(content) and
content deduplication via content_eq(other).
All AI objects implement ObjectTrait with JSON serialization:
from_bytes(data, hash)β deserialize from JSON bytesto_data()β serialize to JSON bytesget_type()β returns theObjectTypediscriminatorget_size()β returns the serialized sizeobject_hash()β computes the content-addressable hash
AI object types are registered in ObjectType with numeric IDs 8β18 and
string identifiers for serialization (e.g. "intent", "plan", "task").
Pack encoding: AI objects cannot be encoded in standard Git pack headers (which only support 3-bit type values 1β7). They must use the AI-specific storage layer.
ObjectType::to_pack_type_u8()returns an error for AI types.
-
Append-only history: Status changes (Intent, PlanStep) and execution records (Task.runs, Run.patchsets) are append-only β entries are never removed or mutated.
-
Snapshot references: Run.plan records the Plan version at execution time. Intent.plan always points to the latest revision. This allows replanning without invalidating in-progress Runs.
-
Bidirectional references: Key relationships are bidirectional for efficient traversal (Task β Run, Run β PatchSet). Other relationships are unidirectional with reverse lookup by scanning (Evidence β Run, Decision β Run).
-
Serde conventions: Optional fields use
#[serde(default, skip_serializing_if)]. Empty Vecs useskip_serializing_if = "Vec::is_empty". Field renames use#[serde(alias = "old_name")]for backward compatibility. -
Context separation: Static context (ContextSnapshot) vs. dynamic context (ContextPipeline) serve complementary needs β reproducibility vs. continuity.