Skip to main content

Architecture

This page describes AGIT's internal architecture and design decisions.

Overview

AGIT creates a "Neural Graph" that runs parallel to Git's commit graph:

Git Graph               Neural Graph
───────── ────────────
commit e8d4f1a ←───→ neural a3f7b2c
│ │
commit c2f1a9b ←───→ neural b5d8e3a
│ │
commit ... ←───→ neural ...

Components

Storage Layer (V2 Git-Native)

AGIT uses a hybrid storage approach that leverages Git infrastructure:

Git-managed (shared via push/pull):

.git/
├── refs/agit/ # Custom ref namespace for neural commits
│ └── heads/ # Neural branch pointers
│ ├── main # Points to latest neural commit on main
│ └── feature # Points to latest neural commit on feature
└── objects/ # Neural commit objects stored in Git ODB

Local state (not shared):

.agit/
├── HEAD # Current branch reference
├── index # Staging area (JSONL)
├── staged-index # Frozen staging (before commit)
├── stash/ # Per-branch index stash
│ ├── main/index
│ └── feature/index
└── search_index/ # Full-text search index (Tantivy)

Custom Git Refs

Neural commit refs are stored under .git/refs/agit/heads/ using Git native ref system. This enables:

  • Push/Pull support: Refs sync automatically with git push and git fetch
  • Branch tracking: Each Git branch has a corresponding neural branch
  • Team collaboration: Neural history is shared like regular Git refs

Object Store

Neural commit objects are stored in Git object database:

.git/objects/
├── a3/
│ └── f7b2c... # Neural commit object
├── b5/
│ └── d8e3a... # Trace blob

Content is JSON, stored using Git content-addressable system.

Index (Staging Area)

The index is a JSONL file (one JSON object per line):

{"role":"user","category":"intent","content":"Fix bug","timestamp":"2024-01-15T10:30:00Z","file":"/src/auth.rs","line":42}
{"role":"ai","category":"reasoning","content":"Add null check","timestamp":"2024-01-15T10:31:00Z"}

Each entry includes:

  • role - "user" or "ai"
  • category - "intent", "reasoning", or "error"
  • content - The thought content
  • timestamp - ISO 8601 timestamp
  • file - Optional: source file path where thought originated
  • line - Optional: line number in source file

This format is:

  • Append-only during a session
  • Human-readable
  • Easy to parse

Per-Branch Index Stashing

When switching branches, the current index is stashed:

.agit/stash/
├── main/index # Stashed thoughts for 'main'
├── feature-x/index # Stashed thoughts for 'feature-x'
└── bugfix/index # Stashed thoughts for 'bugfix'

This preserves pending thoughts per branch, preventing context bleeding when switching between work.

Search Index

AGIT uses Tantivy for full-text search:

.agit/search_index/
├── meta.json
└── segments/
└── ...

The search index:

  • Is stored locally (not synced between machines)
  • Auto-updates on commit
  • Incrementally updates after pull
  • Can be manually rebuilt with agit search rebuild

MCP Server

The MCP server implements JSON-RPC 2.0 over stdio:

AI Editor ──── stdin/stdout ──── agit server
(JSON-RPC 2.0)

Available tools:

  • agit_log_step - Record a thought (supports repo_path for cross-repo validation)
  • agit_get_status - Get current status
  • agit_get_file_history - Get history for a file
  • agit_get_relevant_context - Search past reasoning
  • agit_get_recent_summaries - Get recent commit summaries
  • agit_read_roadmap - Read project roadmap

Synthesizer

The synthesizer creates summaries from staging entries:

pub fn synthesize(entries: &[IndexEntry]) -> String {
let intent = find_last_intent(entries);
let reasoning = find_last_reasoning(entries);

match (intent, reasoning) {
(Some(i), Some(r)) => format!("Intent: {}. Plan: {}.", i, r),
(Some(i), None) => format!("Intent: {}.", i),
(None, Some(r)) => format!("Plan: {}.", r),
(None, None) => "Manual update.".to_string(),
}
}

This is deterministic - no LLM calls. The AI editor does the "thinking", AGIT just synthesizes.

Commit Pipeline

The commit pipeline:

  1. Read entries from index
  2. Check for semantic conflicts (ghost commits)
  3. Synthesize summary
  4. Create trace blob (full JSONL)
  5. Create commit object
  6. Update search index
  7. Update refs
  8. Clear index
pub fn execute(&mut self, message: &str, summary: &str) -> Result<CommitResult> {
// 1. Read staging
let entries = self.index_store.read_all()?;

// 2. Check conflicts (unless --force)
if !force {
check_for_conflicts(&entries)?;
}

// 3. Create trace blob
let trace = format_trace(&entries);
let trace_hash = self.object_store.write(&trace)?;

// 4. Create commit
let commit = NeuralCommit {
message: message.to_string(),
summary: summary.to_string(),
git_commit: self.git_repo.head_commit_hash()?,
parent: self.head_store.read()?,
trace_hash,
timestamp: Utc::now(),
};

// 5. Write commit object
let hash = self.object_store.write(&commit.to_json())?;

// 6. Index for search
search::index_entries(&agit_dir, &entries)?;

// 7. Update refs
self.ref_store.write("main", &hash)?;
self.head_store.write(&hash)?;

// 8. Clear staging
self.index_store.clear()?;

Ok(CommitResult { neural_hash: hash, git_hash: commit.git_commit })
}

Git Workflow Resilience

AGIT handles complex Git workflows automatically:

Amend Detection

When git commit --amend changes a commit hash, AGIT detects this by comparing:

  • Author email
  • First line of commit message
  • Timestamp (within 60 seconds)

If detected, the neural commit is migrated to the new hash.

Rewind Recovery

When git reset --hard rewinds history, AGIT:

  1. Detects the linked commit is no longer reachable
  2. Walks backwards through neural history
  3. Finds the nearest valid ancestor
  4. Updates the branch ref

Orphaned commits are preserved (not deleted) for potential recovery.

Merge/Rebase Guard

During merge or rebase, AGIT enters read-only mode:

  • Detects .git/MERGE_HEAD or .git/rebase-merge/
  • Blocks record, add, commit commands
  • Shows warnings in status

Design Principles

1. Git-Native

AGIT integrates deeply with Git:

  • Uses .git/refs/agit/* for branch refs
  • Stores objects in Git object database
  • Syncs via standard git push/git pull
  • Local state in .agit/ (not synced)

2. Deterministic

No LLM calls in AGIT itself:

  • AI editors do the reasoning
  • AGIT just stores and synthesizes
  • Reproducible behavior

3. Simple Storage

Content-addressable store with JSON:

  • Easy to debug
  • Human-readable
  • No complex database

4. Atomic Operations

File operations are atomic:

  • Write to temp file first
  • Rename to final location
  • Safe concurrent access

Data Flow

┌─────────────┐         ┌─────────────┐         ┌─────────────┐
│ AI Editor │──MCP───▶│ AGIT Server │──write─▶│ .agit/ │
│ (Cursor) │ │ │ │ index │
└─────────────┘ └─────────────┘ └─────────────┘


┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ User │──CLI───▶│agit commit │──read──▶│ .git/refs/ │
│ │ │ │ │ agit/heads/ │
└─────────────┘ └─────────────┘ └─────────────┘

Security

  • No shell command execution with user input
  • Input validation on all parameters
  • File locking for concurrent access
  • Atomic writes prevent corruption

See Also