fix(trace): stop crediting finished sessions on unrelated commits

Merged
JI
1mo ago
+911-27

The Chainloop-Trace-Sessions trailer was chosen by intersecting the staged files with every ai-lines ledger on disk, with no scoping in time, branch or session liveness. Those ledgers never expire — WipeTraceDir preserves them and GCOrphans only drops one when no surviving commit references it — so a session pinned itself into the repository after its first commit and kept being stamped onto every later commit that touched any file it had ever edited.

The consequences compounded downstream: a session's evidence file set comes from the diffs of the commits credited to it, so a foreign commit contributed its whole diff, and every file the session had no ranges for was reported as human-written. A single session accumulated commits from unrelated branches and surfaced on pull requests it never touched.

What changed

Attribution is now consumed when it lands:

  • Each ai-lines entry carries a recorded_at, and the post-commit hook appends a consumed_by marker for the files the commit contained.
  • Matching runs against the pending set instead of the full range history. Ranges are retained for push-time enrichment, and per-file consumption keeps credit intact for a session committing its work in chunks.
  • Entries written before recorded_at existed are never pending, so ledgers already on disk stop matching immediately.

Two supporting changes:

  • session-end persists active: false and session-start revives it on resume, which lets a push with no commits to go on skip sessions that have ended. active was previously written once and never read.
  • GCOrphans retires sessions older than a week once everything they produced has been attested, since reachability alone never expires them.

active remains unsuitable as an attribution signal — session-end is not installed for trace run and never fires on SIGKILL or a closed terminal.