attribution: scope Mixed to the touching session and unify authorship rule · Entire

Attribution: Scope Mixed to the Touching Session and Unify Authorship Rule

a17eb09 → main

Soph · 1mo ago · 2 files · +71 added / -17 removed

Two related correctness bugs in line attribution:

  1. entire blame and entire why disagreed on the tag. resolveLine marks a line [MX] only when its preferred candidate is mixed, but the why-time enrichment path (enrichAttributionLineWithFetch) marked it [MX] if any candidate was mixed. A line with a non-mixed preferred candidate and a mixed secondary one rendered [AI] in blame and [MX] in why. Extract a single authorshipForPreferred() rule used by both paths.

  2. Mixed was checkpoint-scoped, not session-scoped. readCheckpointContext OR-combined the checkpoint-wide CombinedAttribution with every session's attribution, so a line from an agent-only file got tagged [MX] whenever the checkpoint also touched a hand-edited file. Scope Mixed to the session whose work actually touched this file; fall back to the checkpoint-wide attribution only when no session metadata resolves.

Adds a regression test where the file-touching session is pure-AI but the checkpoint's combined attribution is mixed — the line must stay [AI].

Co-Authored-By: Claude Opus 4.8 (1M context) noreply@anthropic.com

Sessions

ef513bf77157 View transcript

Attribution Bugs and Refactoring in CLI

Changes

""" 360 unmodified lines

"""

// authorshipForPreferred maps the preferred candidate to a line's authorship. // A committed line that carries a checkpoint trailer is [AI]; it is [MX] only // when the candidate that actually produced it (the session whose work touched // this file) reflects mixed AI+human work. func authorshipForPreferred(preferred *attributionCandidate) attributionAuthorship { if preferred != nil && preferred.Mixed { return attributionMixed } return attributionAI }

func preferredAttributionCandidate(candidates []attributionCandidate, file string) *attributionCandidate { if len(candidates) == 0 { return nil }