checkpoint: refresh compact_transcript pointer on finalize · Entire

checkpoint: refresh compact_transcript pointer on finalize

1cba79a→main·

computermode·3w ago·2 files·+79 added/-0 removed

backfillTranscript regenerates transcript.jsonl via replaceTranscript but did not rewrite the root metadata.json, so a compact transcript created during finalize (when the initial write skipped compaction) left sessions[].compact_transcript omitted. Re-derive the pointer from the tree entry after replaceTranscript and rewrite the root summary when it changed.

Co-Authored-By: Claude Opus 4.8 noreply@anthropic.com

Sessions

b0e1c7d96944View transcript

[?
Add Compact Transcript Metadata FieldClaude Code·Opus 4.8·2 steps](/content/gh/entireio/cli/session/785891a2-654d-4a24-a12d-38aa83dd327d#timeline-b0e1c7d96944/index.html)

Changes

2

1579 unmodified lines

1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611

1579 unmodified lines

if err := s.replaceTranscript(ctx, opts.Transcript, agentType, startLine, opts.PrecomputedBlobs, sessionPath, entries); err != nil {
        return fmt.Errorf("failed to replace transcript: %w", err)
    }

// Keep the root metadata.json compact_transcript pointer consistent with
    // the finalized tree. replaceTranscript may have written transcript.jsonl
    // that the initial write lacked (e.g. compaction was skipped then and
    // succeeds now), so re-derive the pointer from the tree entry and rewrite
    // the root summary when it changed.
    compactPath := ""
    if _, ok := entries[sessionPath+paths.CompactTranscriptFileName]; ok {
        compactPath = "/" + sessionPath + paths.CompactTranscriptFileName
    }
    if checkpointSummary.Sessions[sessionIndex].CompactTranscript != compactPath {
        checkpointSummary.Sessions[sessionIndex].CompactTranscript = compactPath
        summaryJSON, err := jsonutil.MarshalIndentWithNewline(checkpointSummary, "", "  ")
        if err != nil {
            return fmt.Errorf("failed to marshal checkpoint summary: %w", err)
        }
        summaryHash, err := CreateBlobFromContent(s.repo, summaryJSON)
        if err != nil {
            return fmt.Errorf("failed to create checkpoint summary blob: %w", err)
        }
        entries[rootMetadataPath] = object.TreeEntry{
            Name: rootMetadataPath,
            Mode: filemode.Regular,
            Hash: summaryHash,
        }
    }
}

// Replace prompts with 7-layer-redacted content.

Mcmd/entire/cli/checkpoint/persistent.go+26

165 unmodified lines

166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224

165 unmodified lines

}
}

// TestUpdateCommitted_RefreshesCompactTranscriptPointer guards against the
// finalize path writing transcript.jsonl without updating the root
// metadata.json. When the initial write produced no compact transcript but a
// later backfill does, sessions[].compact_transcript must be refreshed to point
// at it rather than staying omitted.
func TestUpdateCommitted_RefreshesCompactTranscriptPointer(t *testing.T) {

t.Parallel()
repo, _ := setupTestRepo(t)
store := NewGitStore(repo, DefaultV1Refs())
cpID := id.MustCheckpointID("f6a1b2c3d4e5")

// Initial write with a non-compactable transcript: full.jsonl is written but
    // no transcript.jsonl, so compact_transcript is omitted.
    err := store.Write(context.Background(), Session{
        CheckpointID: cpID,
        SessionID:    "session-001",
        Strategy:     "manual-commit",
        Transcript:   redact.AlreadyRedacted([]byte("not json at all\nstill not json\n")),
        Agent:        agent.AgentTypeClaudeCode,
        AuthorName:   "Test",
        AuthorEmail:  "test@test.com",
    })
    if err != nil {
        t.Fatalf("WriteCommitted() error = %v", err)
    }
    summary := readSummaryFromBranch(t, repo, cpID)
    if summary.Sessions[0].CompactTranscript != "" {
        t.Fatalf("precondition: compact_transcript = %q, want empty", summary.Sessions[0].CompactTranscript)
    }

// Finalize with a compactable transcript: transcript.jsonl is now written and
    // the root summary's compact_transcript must be refreshed to match.
    err = store.Write(context.Background(), SessionTranscript{
        CheckpointID: cpID,
        SessionID:    "session-001",
        Transcript:   redact.AlreadyRedacted(claudeStyleTranscript()),
        Agent:        agent.AgentTypeClaudeCode,
    })
    if err != nil {
        t.Fatalf("UpdateCommitted() error = %v", err)
    }

sessionPath := cpID.Path() + "/0/"
    if _, ok := readBranchFile(t, store, sessionPath+paths.CompactTranscriptFileName); !ok {
        t.Fatal("transcript.jsonl missing after finalize")
    }
    summary = readSummaryFromBranch(t, repo, cpID)
    wantCompact := "/" + sessionPath + paths.CompactTranscriptFileName
    if summary.Sessions[0].CompactTranscript != wantCompact {
        t.Errorf("sessions[0].compact_transcript = %q, want %q", summary.Sessions[0].CompactTranscript, wantCompact)
    }
}

// codexTranscriptWithCompactionBeforeStart returns a Codex-format JSONL
// transcript whose line 1 is a `compaction` entry that
// codex.SanitizePortableTranscript drops. With a checkpoint start of line 2,