narrow attribution checkpoint reader · Entire

narrow attribution checkpoint reader

772908f→main·

pfleidi·4w ago·2 files·+63 added/-4 removed

Keep attribution resolution dependent only on the committed summary and session metadata methods it uses.

Add a focused test with a reader that omits unused session-content methods so the local interface stays precise.

Sessions

136650505f9cView transcript

?\Refactor Checkpoint Storage Interfaces and Domain AdaptersCodex·GPT-5.5·2 steps

Changes

2

113 unmodified lines

114
115
116
117
118
117
118
119
120
121
122
123
124
125
125
126
127
128
282 unmodified lines

411
412
413
414
414
415
416
417
71 unmodified lines

489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508

113 unmodified lines

MixedPercentage  int `json:"mixed_percentage"`
}

type attributionSessionMetadataPromptsReader interface {
    checkpoint.CommittedReader

type attributionCheckpointReader interface {
    ReadCommitted(ctx context.Context, checkpointID id.CheckpointID) (*checkpoint.CheckpointSummary, error)
    ReadSessionMetadataAndPrompts(ctx context.Context, checkpointID id.CheckpointID, sessionIndex int) (*checkpoint.SessionContent, error)
}

type attributionResolver struct {
    ctx         context.Context
    repo        *git.Repository
    store       attributionSessionMetadataPromptsReader
    store       attributionCheckpointReader
    fetchOnMiss bool

commitCache     map[string]*object.Commit
282 unmodified lines

func (r *attributionResolver) readCheckpointContext(cpID id.CheckpointID, file string) attributionCheckpointContext {
    ctx := attributionCheckpointContext{CheckpointID: cpID.String()}
    summary, err := checkpoint.ReadCommittedCheckpoint(r.ctx, r.store, cpID)
summary, err := readAttributionCheckpointSummary(r.ctx, r.store, cpID)
    if err != nil && r.fetchOnMiss {
        if fetched, fetchErr := r.fetchCheckpointContext(cpID, file); fetchErr == nil {
            return fetched
        }
    }

return ctx
}

func readAttributionCheckpointSummary(ctx context.Context, reader attributionCheckpointReader, cpID id.CheckpointID) (*checkpoint.CheckpointSummary, error) {
    if err := ctx.Err(); err != nil {
        return nil, err //nolint:wrapcheck // Propagating context cancellation
    }
    summary, err := reader.ReadCommitted(ctx, cpID)
    if err != nil {
        return nil, fmt.Errorf("read committed checkpoint: %w", err)
    }
    if summary == nil {
        return nil, checkpoint.ErrCheckpointNotFound
    }
    return summary, nil
}

func enrichAttributionLineWithFetch(ctx context.Context, file string, line *attributionLine, checkpoints map[string]attributionCheckpointContext) error {
    if line == nil || len(line.Candidates) == 0 {
        return nil
}

Mcmd/entire/cli/attribution.go+18/-4

352 unmodified lines

353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403

352 unmodified lines

require.Equal(t, 1, payload.Summary.AILines)
}

func TestAttributionResolverUsesCheckpointReader(t *testing.T) {

t.Parallel()

cpID := checkpointid.MustCheckpointID("d9b2c3d4e5f6")
    reader := &attributionCheckpointReaderStub{
        summary: &checkpoint.CheckpointSummary{
            FilesTouched: []string{"auth.py"},
            Sessions:     []checkpoint.SessionFilePaths{{Metadata: "metadata.json"}},
        },
        content: &checkpoint.SessionContent{
            Metadata: checkpoint.CommittedMetadata{
                SessionID:    "session-ai",
                FilesTouched: []string{"auth.py"},
                Agent:        agent.AgentTypeClaudeCode,
                Model:        "claude-test",
            },
            Prompts: "Explain the authentication change.",
        },
    }
    resolver := &attributionResolver{
        ctx:             context.Background(),
        store:           reader,
        checkpointCache: make(map[string]attributionCheckpointContext),
    }

ctx := resolver.readCheckpointContext(cpID, "auth.py")
    require.Equal(t, "session-ai", ctx.SessionID)
    require.Equal(t, "Claude Code", ctx.Agent)
    require.Equal(t, "claude-test", ctx.Model)
    require.Equal(t, "Explain the authentication change.", ctx.Prompt)
}

type attributionCheckpointReaderStub struct {
    summary *checkpoint.CheckpointSummary
    content *checkpoint.SessionContent
}

func (s *attributionCheckpointReaderStub) ReadCommitted(context.Context, checkpointid.CheckpointID) (*checkpoint.CheckpointSummary, error) {
    return s.summary, nil
}

func (s *attributionCheckpointReaderStub) ReadSessionMetadataAndPrompts(context.Context, checkpointid.CheckpointID, int) (*checkpoint.SessionContent, error) {
    return s.content, nil
}

func TestAttributionBlameScopesMixedToSessionNotCheckpoint(t *testing.T) {
    repoRoot := newAttributionRepo(t)
    writeAttributionCheckpoint(t, repoRoot, "a9b2c3d4e5f6", checkpoint.WriteCommittedOptions{