AGENTS.md · git-ai-project/git-ai
AGENTS.md from git-ai-project/git-ai: 249 lines covering Non-Negotiable Rules, Build & Test Commands, PR Workflow, Architecture and more.
Template by git-ai-project
Install
Works with Codex, Cursor, GitHub Copilot, Gemini CLI, OpenCode, Claude Code. Read the repository before you give any tool access to your data.
curl -fsSL https://raw.githubusercontent.com/git-ai-project/git-ai/main/AGENTS.md -o AGENTS.md
Read it and adapt it to your project before use.
Get the weekly diff
The weekly diff: one email a week with new agent tools, notable updates and grade changes, plus security alerts for tools you follow. Double opt-in, one-click unsubscribe.
About
Use cases: version-control, code-generation
Reviews
Write a reviewNo reviews yet. Installed it? Reviews come from verified installers only. How reviews work
What's in it
An AGENTS.md file: instructions coding agents (Codex, Cursor, Copilot, Gemini CLI and others) read before working in a repo.
- For
- Codex, Cursor, GitHub Copilot, Gemini CLI, OpenCode, Claude Code
- Length
- 249 lines
- File
AGENTS.md
Sections
- Non-Negotiable Rules
- Build & Test Commands
- PR Workflow
- Architecture
- Test Infrastructure
- Key Conventions
- Optimize for Human Review
- Gotchas
First lines (shown under the repo's Apache-2.0 license)
## Non-Negotiable Rules
These are hard constraints. Violating any of them will get a PR rejected outright.
1. **All Git integration/processing is trace2-driven -- we do NOT wrap git.** All git command processing is based on trace2, so EVERYTHING we do must be fully async. As a result, we cannot rely on repo/git state at processing time being reflective of the state whenever the given operation actually occurred. We have a highly latency-sensitive trace2 ingestion flow that gathers/estimates the minimal possible state and orders events for later async processing.
2. **No new work on the critical ingestion path.** You CANNOT add git spawns, git object lookups, ref checks, etc. in the critical ingestion path of the daemon. It is EXTREMELY latency-sensitive -- even additional file reads have meaningful overhead on this path, which is sensitive to sub-millisecond latency increases.
3. **No non-constant-time git work anywhere.** You CANNOT add git spawns, git object lookups, ref checks, etc. that are not constant-time (e.g., must be O(2)/O(3), not O(n)). You cannot have any operation that calls git per commit, per file, per object, or per ref -- that is not constant time. You can still call git outside of latency-sensitive paths, but instead of calling it once per item, find a constant-time alternative: a plumbing command, a command that accepts batched inputs, etc., so you avoid the massive overhead of N process spawns. Rule of thumb: if you're calling git or looking up objects/refs "for each `<thing>`," you're doing it wrong.
4. **Unbounded git spawns are an automatic rejection.** Changes/PRs with unbounded git spawns will NOT be accepted.
5. **Reuse existing code.** This is a large codebase. For nearly any operation you need, there is almost certainly a helper function already available and tested. Reuse code as much as practical -- it also keeps diffs small. PRs that fail to reuse existing code where applicable will be rejected immediately.
6. **Follow STRICT TDD.** PRs that are not clearly TDD-driven with high-quality `TestRepo`-based tests will be rejected immediately.
## Build & Test Commands
```bash
# Install a git-ai debug build for local dev on the system so that all git commands will route through it.
# Installs to the same location as real release builds, so it overrides system-wide. It also runs `git-ai install`
# and restarts the daemon to ensure all latest code changes are fully installed and propagated system-wide.
# Use this for trying out changes locally -- do not use any other approaches for runing git-ai locally. They will
# not work, interfere, and break things.
task dev
# Build (only use this for checking that your changes compile)
task build
# Test (use these commands to run the test suite -- these calls are optimized for your system; all flags/args can be combined)
task test # Run the full test suite
task test TEST_FILTER=foo # run specific test
task test NO_CAPTURE=true # Run with Cargo's --no-capture flag
task test EXTRA_TEST_BINARY_ARGS="--ignored" # ignored / exact / other flags
task test CARGO_TEST_ARGS="--lib" # cargo-level flags (rare)
# Lint & Format
task lint
task fmt
# Snapshot management (insta crate)
cargo insta review # interactively review snapshot changes
cargo insta accept # accept all pending snapshots
```
## PR Workflow
Before opening a PR, make sure to run `task lint` and `task fmt` and resolve any formatting/lint issues as they will fail in CI.
When opening a PR, make sure to monitor the ubuntu-based CI jobs first. They are the fastest (roughly 15mins) and if they fail, you should quickly iterate based on those failures and update the PR -- iterating there until those jobs are all green. Additionally, while you're checking on the ubuntu-based jobs, our automated PR review bot, Devin, should have had time to leave feedback. Make sure to read all of Devin's PR review feedback commits and address them. Address them means review, understand, evaluate, and fix if necessary or comment with your thoughts if you don't the feedback is a real issue. Once the lint, fmt, and Ubuntu-based tests have passed and you have addressed all Devin PR review feedback, you can stop monitoring CI for the Mac (~35mins) and Windows (up to 3.5 hours) checks unless the user has explicitly asked for you to wait for those or you're working on a specific OS-based bug.
## Architecture
### Binary dispatch (src/main.rs)
A single binary serves two roles based on `argv[0]`:
- **`argv[0] == "git"`** --> `commands::git_handlers::handle_git()` -- proxies to real git with pre/post hooks per subcommand
- **`argv[0] == "git-ai"`** --> `commands::git_ai_handlers::handle_git_ai()` -- direct subcommands (checkpoint, blame, diff, status, search, etc.)
- **Debug-only shortcut**: When `cfg!(debug_assertions)` and `GIT_AI=git` env var is set, forces git proxy mode regardless of binary name. Most integration tests no longer rely on this: they run the real git binary with trace2 wired to a per-test daemon (production-like), using the proxy env only in a few special cases.
### Core data flow: checkpoint --> working log --> authorship note
1. **Checkpoint**: An AI coding agent calls `git-ai checkpoint <agent>` with hook input (typically JSON via stdin) before AND after it edits a file. The corresponding agent preset (`src/commands/checkpoint_agent/agent_presets.rs`) extracts edited file paths, transcript, and model info. The checkpoint processor diffs the file against HEAD's version or the last-checkpointed value of that file and compute character-level attributions. The combination of pre and post file edit checkpoints is what allows us to know exactly what the AI changed (since we can compare the before and after). There are 3 main types of checkpoints in git-ai:
* Plain or legacy `human`: only due to legacy, it's still called `human` as it used to mean "human" edited files, but since we migrated to an explicit Human checkpoint (now called `known_human`), this checkpoint represents 'untracked' changes. This is the checkpoint that AI agent presets invoke to take the before edit snapshots. Changes caught by these checkpoints do get explicit attestations in the final authorship notes (they are basically holes in the data) and stats recognize them as untracked. For testing, invoke by calling `git-ai checkpoint human` (for unscoped) or `git-ai checkpoint human /path/to/file` (for scoped).
* Known human (`known_human`) checkpoints: this is the 'real' Human checkpoint. These are never called by the AI agent presets and are only invoked by our IDE/editor extensions that recognize when a change has actually been made by the human by typing, etc. For testing, invoke via `git-ai checkpoint mock_known_human` (for unscoped) or `git-ai checkpoint mock_known_human /path/to/file` (for scoped).
* AI checkpoint (`ai_agent`) checkpoints: this is the AI checkpoint that explicitly associates the captured changes with the particular AI agent and session. This is the checkpoint taht AI agent presets invoke to take the after edit snapshots. For testing, invoke via `git-ai checkpoint mock_ai` (for unscoped) or `git-ai checkpoint mock_ai /path/to/file` (for scoped).
2. **Working log**: Checkpoint data is written to `.git/ai/working_logs/<base_commit>/` as JSON files. Each working log entry records per-file line attributions (which ranges are AI vs known human vs untracked (legacy human)) and session metadata.
3. **Post-commit authorship**: After `git commit`, the daemon reads working logs, generates an `AuthorshipLog` (schema version `authorship/3.0.0`), and stores it as a Git Note under `refs/notes/ai`. The authorship log contains attestation entries (hash --> line ranges) and a metadata section with prompt records.
4. **Rewrite tracking**: The daemon ingests git trace2 event streams to learn which git commands ran, establishes exact ref transitions via a reflog cursor model (`src/daemon/ref_cursor.rs`), and migrates authorship notes/working logs through `src/authorship/rewrite.rs` (`RewriteEvent` + `handle_rewrite_event`) plus the per-operation modules (`rewrite_reset.rs`, `rewrite_stash.rs`, `rewrite_revert.rs`, `rewrite_cherry_pick.rs`). See `docs/rewrite-ops-spec.md` and `docs/daemon-trace2-ingestion-spec.md`.
### Daemon trace2 ingestion (src/daemon.rs, src/daemon/)
The git proxy is a thin passthrough (`src/commands/git_handlers.rs`); all attribution side effects run in the shared daemon, driven by trace2:
- Socket listener receives trace2 JSON frames; definitely-read-only roots are filtered out
- `TraceNormalizer` (src/daemon/trace_normalizer.rs) groups frames by root sid into a `NormalizedCommand`
- A per-repo-family actor (src/daemon/family_actor.rs) sequences commands and checkpoints in order
- `RefCursor::enrich_command` (src/daemon/ref_cursor.rs) consumes cursor-bounded reflog entries to fill exact `ref_changes`; commands without a cursor or immutable argv OIDs fail closed for attribution
- Analyzers (src/daemon/analyzers/history.rs) classify enriched commands into semantic events that drive post-commit authorship and rewrite-note migration
Signal forwarding: On Unix, the git proxy installs signal handlers (SIGTERM, SIGINT, SIGHUP, SIGQUIT) that forward to the child git process group.
### Config singleton
`Config` is a global `OnceLock` singleton accessed via `Config::get()`. It reads from `~/.git-ai/config.json`. In tests, `GIT_AI_TEST_CONFIG_PATCH` env var allows overriding specific config fields without a real config file. Feature flags follow precedence: environment vars (`GIT_AI_*` prefix via `envy`) > config file > defaults.
Feature flags have separate debug/release defaults defined via the `define_feature_flags!` macro in `src/feature_flags.rs`. Currently: `auth_keyring` (false/false), `transcript_streaming` (true/true), `transcript_sweep` (true/true), `checkpoint_debug_log` (false/false), `daemon_log_upload` (true/true), `token_usage_metrics` (true/true), `untraced_commit_fixup` (true/true), `untraced_fixup_ignore_temp_repos` (true/true).
### Error handling
`GitAiError` enum in `src/error.rs` -- not `thiserror`-based, uses manual `Display`/`From` impls. Variants: `GitCliError` (captures exit code + stderr + args), `IoError`, `JsonError`, `SqliteError`, `PresetError`, `Generic`, `GixError`.
## Test Infrastructure
### Integration test framework (tests/integration/repos/)
Tests create real git repositories and run against a shared test daemon pool (trace2-driven, like production). The test framework has three key files:
- **`tests/integration/repos/test_repo.rs`** -- `TestRepo` struct: creates temp git repos, runs git/git-ai commands as subprocesses wired to a per-test daemon (control + trace sockets), and provides explicit daemon sync before assertions. Uses `get_binary_path()` which auto-compiles the binary via a `OnceLock`.
- **`tests/integration/repos/test_file.rs`** -- `TestFile` fluent API for setting file contents with attribution expectations. The `lines!` macro + `.ai()` / `.human()` / `.unattributed_human()` trait methods create `ExpectedLine` vectors. `assert_lines_and_blame()` validates both content and AI/human attribution.
- **`tests/integration/repos/mod.rs`** -- `subdir_test_variants!` macro auto-generates two test variants: one from a subdirectory and one using `-C` flag, to verify repository discovery works from any CWD.
Simple test pattern (using all standard helpers):
```rust
#[test]
fn test_using_test_repo() {
let repo = TestRepo::new();
let mut file = repo.filename("test.txt");
file.set_contents(lines!["Line 1", "AI line".ai()]);
repo.stage_all_and_commit("Initial commit").unwrap();
file.assert_lines_and_blame(lines!["Line 1".human(), "AI line".ai()]);
}
```
For certain test cases, especially where you are focused on testing specific checkpoint or attribution behavior, do NOT use the `file.set_contents` helper as it has a very specific (and unrealistic) ai vs human checkpointing flow that first sets file content to all the human values with explicit placeholders for the lines that are AI, calls a known human checkpoint, and then replaces the AI lines with their real values and calls the AI checkpoint after. As you can imagine, if you really want to test nuances of checkpointing, this is problematic. In those cases, explicitly write the file using standard Rust file write utils and explicitly call the ai vs human checkpoints mocking the real pre/post checkpointing flow using `mock_known_human` for explicit/known human changes, `human` for untracked changes, and `mock_ai` for AI changes. Example with custom writes+checkpointing for when you really care about exact replication of issues or testing checkpointing/attribution internals or any time the exact flow, order, etc. of checkpoints is relevant:
```rust
#[test]
fn test_using_test_repo_with_custom_checkpoints() {
let repo = TestRepo::new();
let file_path = repo.path().join("example.md");
let initial = "\
Untracked line
";
fs::write(&file_path, initial).unwrap();
// Example of a completely untracked edit where we didn't fire a checkpoint call at all
repo.stage_all_and_commit("Initial commit").unwrap();
// Assert after every commit
let mut file = repo.filename("example.md");
// ALWAYS use the helper to assert the lines post-commit AND make sure to always assert line-level after EVERY commit for EVERY test you EVER right. This is CRUCIAL.
file.assert_committed_lines(lines![
"Untracked line".unattributed_human(), // 'untracked'
]);
let second_edit = "\
Untracked line
Human line
";
fs::write(&file_path, second_edit).unwrap();
repo.git_ai(&["checkpoint", "mock_known_human", "example.md"])
.unwrap();
// Explicit add call (very useful to test partial staging scenarios)
repo.git(&["add", "."]).unwrap();
// Explicit commit
repo.commit("Second commit").unwrap();
file.assert_committed_lines(lines![
"Untracked line".unattributed_human(), // still 'untracked'
"Human line".human(), // known human
]);
let third_edit = "\
Untracked lineSignals
- Stars
- 2,886
- Forks
- 326
- Open issues
- 90
- Last commit (this folder)
- Sep 8, 2026 (1 month ago)
- Last release
- Sep 9, 2026 (1 month ago)
From GitHub (fetched Oct 24, 2026).
Details
- License
- Apache-2.0
- Repository
- github.com/git-ai-project/git-ai
- First seen
- Oct 24, 2026
- Last checked
- Oct 24, 2026
- Source
- GitHubgithub-graphql
Cite this page
AGENTS.md · git-ai-project/git-ai. Agent Mart. https://agentmart.dev/templates/git-ai-project-git-ai-agents-md (last updated 2026-10-24)