Skip to content

Commit 3aa0032

Browse files
jwiegleyclaude
andcommitted
Add attribution test writing guidelines to AGENTS.md
Document patterns and conventions for writing integration tests that exercise the attribution system, including test harness setup, daemon interaction, and assertion helpers. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent ed02bb9 commit 3aa0032

1 file changed

Lines changed: 78 additions & 0 deletions

File tree

AGENTS.md

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,3 +149,81 @@ Uses `insta` crate. Snapshots live in `tests/snapshots/` and `tests/repos/snapsh
149149
- **SQLite WAL files**: Test DB paths are placed as siblings to the repo directory (not inside `.git/`) to prevent WAL/SHM files from interfering with git operations.
150150

151151
- **`smol` async runtime**: The project uses `smol` (not tokio) for async operations with `futures` combinators. The async surface area is small -- mostly HTTP operations and background flushes.
152+
153+
## Writing Attribution Tests
154+
155+
When writing tests that verify AI vs human attribution:
156+
157+
### Required: Use Line-Level Blame Verification
158+
159+
**NEVER** just check that an authorship note exists or that `attestations.is_empty()` is false. This only proves the system created a note -- it does NOT verify correct line-level attribution.
160+
161+
**ALWAYS** use `assert_lines_and_blame()` to verify attribution at specific lines:
162+
163+
```rust
164+
use crate::repos::test_file::{ExpectedLineExt, TestFile};
165+
166+
// After committing, verify each line's attribution:
167+
let file = TestFile::from_existing_file(repo.path().join("code.rs"), &repo);
168+
file.assert_lines_and_blame(lines![
169+
"fn main() {".human(),
170+
" println!(\"AI wrote this\");".ai(),
171+
"}".human(),
172+
]);
173+
```
174+
175+
### Required: Direct File Writes + Explicit Checkpoints
176+
177+
**Do NOT** use `set_contents()` for attribution tests. It creates an unrealistic two-phase write (stub content -> human checkpoint -> real content -> AI checkpoint) that can mask bugs in the checkpoint-to-commit pipeline.
178+
179+
**Instead**, write file contents directly and run explicit checkpoints:
180+
181+
```rust
182+
let path = repo.path().join("code.rs");
183+
184+
// 1. Write human content
185+
fs::write(&path, "fn main() {\n // human code\n}\n").unwrap();
186+
repo.git_ai(&["checkpoint", "--", "code.rs"]).unwrap();
187+
188+
// 2. AI modifies the file
189+
fs::write(&path, "fn main() {\n // human code\n println!(\"AI\");\n}\n").unwrap();
190+
repo.git_ai(&["checkpoint", "mock_ai", "--", "code.rs"]).unwrap();
191+
192+
// 3. Commit and verify
193+
repo.stage_all_and_commit("add feature").unwrap();
194+
let file = TestFile::from_existing_file(path, &repo);
195+
file.assert_lines_and_blame(lines![
196+
"fn main() {".human(),
197+
" // human code".human(),
198+
" println!(\"AI\");".ai(),
199+
"}".human(),
200+
]);
201+
```
202+
203+
### Whitespace Attribution Rule
204+
205+
- Human whitespace changes **preserve** existing attribution
206+
- AI whitespace changes **are attributed to AI**
207+
208+
Account for this in tests involving formatting or whitespace-only edits.
209+
210+
### Test Intermediate States
211+
212+
Always verify attribution at intermediate commits, not just the final state. Bugs like the intermediate commit corruption in rebase logic can only be caught by checking each step:
213+
214+
```rust
215+
let commit1 = repo.stage_all_and_commit("step 1").unwrap();
216+
// verify blame after commit 1
217+
let commit2 = repo.stage_all_and_commit("step 2").unwrap();
218+
// verify blame after commit 2 -- don't skip this
219+
```
220+
221+
### Key Test Helpers Reference
222+
223+
| Helper | Location | Purpose |
224+
|--------|----------|---------|
225+
| `assert_lines_and_blame()` | `tests/integration/repos/test_file.rs` | Core line-level blame assertion |
226+
| `assert_committed_lines()` | same | Filters uncommitted lines first |
227+
| `.ai()` / `.human()` | `ExpectedLineExt` trait | Mark expected authorship |
228+
| `lines![]` macro | `tests/integration/repos/test_file.rs` | Create `Vec<ExpectedLine>` |
229+
| `TestFile::from_existing_file()` | same | Reconstruct expectations from blame |

0 commit comments

Comments
 (0)