attribution: collapse duplicate candidate/context structs into one type · Entire

attribution: collapse duplicate candidate/context structs into one type

0326b95→main

Soph·1mo ago·1 file·+10 added/-21 removed

attributionCandidate and attributionCheckpointContext declared identical field lists with identical JSON tags, kept in sync only by a candidateFromContext() cast. Adding a field meant editing both. Alias attributionCandidate to attributionCheckpointContext so there is one definition, and drop the now-noop conversion helper.

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

Sessions

91b11a441843View transcript

[?
Attribution Bugs and Refactoring in CLIClaude Code·Opus 4.8[1m]·1 step](/content/gh/entireio/cli/session/d7c3cef2-8708-4e89-a62c-82d2933a3246#timeline-91b11a441843/index.html)

Changes

1

69 unmodified lines

70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
73
74
75
76
77
78
79
80
81
6 unmodified lines

88
89
90
91
92
93
94
95
263 unmodified lines

359
360
361
366
362
363
364
365
109 unmodified lines

475
476
477
482
478
479
480
481
559 unmodified lines

1041
1042
1043
1048
1049
1050
1051
1052
1053
1054
1044
1045
1046

69 unmodified lines

Candidates      []attributionCandidate `json:"candidates,omitempty"`
}

type attributionCandidate struct {
    CheckpointID    string   `json:"checkpoint_id"`
    SessionID       string   `json:"session_id,omitempty"`
    Agent           string   `json:"agent,omitempty"`
    Model           string   `json:"model,omitempty"`
    Prompt          string   `json:"prompt,omitempty"`
    Intent          string   `json:"intent,omitempty"`
    FilesTouched    []string `json:"files_touched,omitempty"`
    MetadataMissing bool     `json:"metadata_missing,omitempty"`
    Mixed           bool     `json:"mixed,omitempty"`
}

// attributionCheckpointContext is the resolved metadata for one checkpoint as
// it applies to a file: the agent/session that produced the file's lines plus
// the prompt and intent behind them. The same shape is used two ways — as a
// per-line candidate (one line may map to several checkpoints) and as the
// deduplicated per-file checkpoint map — so attributionCandidate aliases it
// rather than duplicating the fields.
type attributionCheckpointContext struct {
    CheckpointID    string   `json:"checkpoint_id"`
    SessionID       string   `json:"session_id,omitempty"`
6 unmodified lines

Mixed           bool     `json:"mixed,omitempty"`
}

type attributionCandidate = attributionCheckpointContext

type fileAttributionResult struct {
    File        string                                  `json:"file"`
    Lines       []attributionLine                       `json:"lines"`
263 unmodified lines

var candidates []attributionCandidate
    for _, cpID := range cpIDs {
        candidates = append(candidates, candidateFromContext(r.checkpointContext(cpID, file)))
        candidates = append(candidates, r.checkpointContext(cpID, file))
    }

preferred := preferredAttributionCandidate(candidates, file)
109 unmodified lines

}
    cpCtx := resolver.checkpointContext(cpID, file)
    checkpoints[cpCtx.CheckpointID] = cpCtx
    candidates = append(candidates, candidateFromContext(cpCtx))
    candidates = append(candidates, cpCtx)
}
    preferred := preferredAttributionCandidate(candidates, file)
    applyPreferredToLine(line, preferred)
559 unmodified lines

}
}

// candidateFromContext projects the resolved checkpoint context onto a
// per-line candidate. The two structs carry the same fields, so this is a
// direct conversion — if they ever diverge, this stops compiling.
func candidateFromContext(ctx attributionCheckpointContext) attributionCandidate {
    return attributionCandidate(ctx)
}

// applyPreferredToLine copies the preferred candidate's metadata onto the line.
// It does not touch line.Authorship; callers decide how Mixed maps to authorship.
func applyPreferredToLine(line *attributionLine, preferred *attributionCandidate) {

Mcmd/entire/cli/attribution.go+10/-21