Skip to content

Latest commit

Β 

History

History
860 lines (667 loc) Β· 29.8 KB

ai.md

File metadata and controls

860 lines (667 loc) Β· 29.8 KB

AI Object Model Reference

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/.

Overview

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

End-to-End Flow

 β‘   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

Steps

  1. User input β€” the user provides a natural-language request.

  2. Intent β€” captures the raw prompt and the AI's structured interpretation. Status transitions from Draft (prompt only) to Active (analysis complete). Supports conversational refinement via parent chain.

  3. Plan β€” a sequence of PlanSteps derived from the Intent. References a ContextPipeline and 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 via previous.

  4. 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).

  5. Run β€” a single execution attempt of a Task. Records the baseline commit, the Plan version being executed (snapshot reference), and the host environment. A Provenance (1:1) captures the LLM configuration and token usage.

  6. 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.

  7. PatchSet β€” a candidate diff generated by the agent. Contains the diff artifact, file-level touched summary, and rationale. Starts as Proposed; transitions to Applied or Rejected. Ordering is by position in Run.patchsets.

  8. Evidence β€” output of a validation tool (test, lint, build) run against a PatchSet. One per tool invocation. Carries exit_code, summary, and report_artifacts. Feeds into the Decision.

  9. 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). Records rationale and result_commit_sha.

  10. Intent completed β€” the orchestrator records the final git commit in Intent.commit and transitions status to Completed.

Object Relationship Summary

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

Object Details

Intent (intent.rs)

The entry point of every AI-assisted workflow. Captures the verbatim user request (prompt) and the AI's structured interpretation (content).

Status Transitions

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. commit contains the result git commit hash.
  • Cancelled: Abandoned before completion (user interrupt, timeout, budget).

Fields

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

Conversational Refinement

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.

Usage

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");

Plan (plan.rs)

A sequence of PlanSteps derived from an Intent's analyzed content. Defines what to do (strategy and decomposition); Run handles how to execute.

Revision Chain

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.

Context Range

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)

Fields

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

PlanStep

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

Recursive Decomposition

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)

Usage

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()));

Task (task.rs)

A unit of work with constraints and acceptance criteria. The stable identity for a piece of work β€” persists across retries and replanning.

Status Transitions

Draft ──▢ Running ──▢ Done
  β”‚          β”‚
  β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β–Ά Failed
  └──────────────▢ Cancelled

Fields

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

GoalType

Feature, Bugfix, Refactor, Docs, Perf, Test, Chore, Build, Ci, Style, Other(String).

Replanning

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)

Usage

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);

Run (run.rs)

A single execution attempt of a Task. Captures the execution context (baseline commit, environment, Plan version) and accumulates artifacts (PatchSets, Evidence, ToolInvocations) during execution.

Status Transitions

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.error has details.

Fields

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

Associated Objects (by run_id)

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

Usage

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);

PatchSet (patchset.rs)

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.

Lifecycle

  (created) ──▢ Proposed
                    β”‚
     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
     β”‚              β”‚              β”‚
     β–Ό              β–Ό              β”‚
  Applied       Rejected           β”‚
                    β”‚              β”‚
                    β–Ό              β”‚
       agent generates new PatchSet
       appended to Run.patchsets

Fields

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

Rationale

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().


Evidence (evidence.rs)

Output of a single validation step (test, lint, build) run against a PatchSet. One Evidence per tool invocation.

Fields

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)

Decision (decision.rs)

The terminal verdict of a Run. Created once per Run at the end of execution.

Decision Types

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 β€” β€”

Fields

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

Provenance (provenance.rs)

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).

Fields

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

TokenUsage

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

ToolInvocation (tool.rs)

The finest-grained record: one per tool call made by the agent during a Run.

Fields

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

IoFootprint

Field Type Description
paths_read Vec<String> Files read (repo-relative)
paths_written Vec<String> Files written (repo-relative)

ContextSnapshot (context.rs)

A static capture of the codebase and external resources the agent observed when a Run began. Point-in-time β€” does not change after creation.

Fields

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

ContextItem

Each item has three layers:

  • path β€” human-readable locator (repo path, URL, command, label)
  • blob β€” Git blob hash pointing to full content at capture time
  • preview β€” 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

Blob Retention

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

ContextPipeline (pipeline.rs)

A dynamic context container that accumulates incremental ContextFrames throughout the workflow. Solves the context-forgetting problem in long-running tasks.

Lifecycle

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

Fields

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

ContextFrame

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

FrameKind

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.

Eviction

When max_frames > 0 and the limit is exceeded, push_frame removes the oldest non-protected frame. IntentAnalysis and Checkpoint frames are never evicted.

Step-Frame Association

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 β€” stable frame_ids of frames the step consumed as context
  • oframes β€” stable frame_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.

Usage

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);

Common Header

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())

ActorRef

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

ArtifactRef

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).

Serialization

All AI objects implement ObjectTrait with JSON serialization:

  • from_bytes(data, hash) β€” deserialize from JSON bytes
  • to_data() β€” serialize to JSON bytes
  • get_type() β€” returns the ObjectType discriminator
  • get_size() β€” returns the serialized size
  • object_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.

Design Principles

  1. Append-only history: Status changes (Intent, PlanStep) and execution records (Task.runs, Run.patchsets) are append-only β€” entries are never removed or mutated.

  2. 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.

  3. 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).

  4. Serde conventions: Optional fields use #[serde(default, skip_serializing_if)]. Empty Vecs use skip_serializing_if = "Vec::is_empty". Field renames use #[serde(alias = "old_name")] for backward compatibility.

  5. Context separation: Static context (ContextSnapshot) vs. dynamic context (ContextPipeline) serve complementary needs β€” reproducibility vs. continuity.