feat(trace): attribute lines to their last writer by content

Merged
JI
4h ago
+2286-147

Implements milestone 1 of spec docs/specs/issue-3639-trace-line-attribution.md (#3639): R-001, R-002, R-005, R-006 and R-011.

What changes

  • Each agent edit records a fingerprint for every line it adds and removes. The fingerprint ignores whitespace, so a re-indent or a moved line keeps its owner (D-008). The ledger also keeps the added and removed counts of every edit, including edits that never reach a commit (R-011).
  • Before the next agent edit of a file, and at commit time, the session's uncommitted AI lines that are gone from the file close. A human change then counts as human, also when a later agent edit keeps it (R-001, R-002).
  • A commit closes only the AI lines that it holds. The other AI lines of the file stay pending, and the next commit with them is credited to the session (R-006).
  • At push, an added line is AI when it matches a line that the session wrote. A removed line is AI only when an agent edit of the session removed it (R-005). Matching by content keeps the split after a cherry-pick or a rebase.
  • Each file entry of the evidence gets ai_lines_added, ai_lines_removed, human_lines_added and human_lines_removed (D-010). The session totals are their sums. The file-level attribution marker stays for older platforms (R-009). The JSON schema has the new fields.
  • Ledgers written by older CLIs keep the range-based rule, file by file.
  • The Trace README feature table has the new behavior.

Implementation choices not in the spec

  • A commit that names two sessions: a line that matches the other session counts for neither side in this session's evidence.
  • A shell command has no content snapshot from before the command. The file at HEAD stands in for it, so uncommitted human lines of a file that the command changes count as AI.
  • The commit-msg hook still credits sessions file by file. A file now stays pending only while it has open AI lines.
  • Each agent file edit now reads the session's ledger to find the replaced lines.

Not in this PR

  • Net contribution per session, sending a session again after a later replacement, and human commits that join a session (R-003, R-004, R-010): milestone 2. Until then, commits of a session are still added up.
  • Renames (R-007): milestone 3.
  • Platform use of the per-file split (R-008): follow-up platform ticket.

AI disclosure: this change was written with the assistance of Claude Code.

šŸ¤– Generated with Claude Code