fix(import): redaction config + provenance privacy for imported checkpoints · Entire

fix(import): redaction config + provenance privacy for imported checkpoints

1747e42→main·

computermode·3w ago·7 files·+105 added/-23 removed

Address review findings on imported-checkpoint history:

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

Sessions

3e645399c3f3View transcript

Changes

7

// Only set for imported (Kind == "imported") checkpoints.
type Provenance struct {
    Source         string `json:`"source"`                // e.g. "claude-code"
    TranscriptPath string `json:`"transcript_path"`       // absolute path at import time
    TranscriptFile string `json:`"transcript_file"`       // basename of the source transcript (no absolute path: avoid leaking local usernames/repo layout)
    SessionID      string `json:`"session_id"`            // agent session id
    TurnUUID       string `json:`"turn_uuid"`             // uuid of the user-prompt line that starts this turn
    ParentUUID     string `json:`"parent_uuid,omitempty"` // parent uuid of that line
    LineStart      int    `json:`"line_start"`            // 0-indexed start line of this turn in the source
    LineEnd        int    `json:`"line_end"`              // exclusive end line (next turn start or EOF)
    ContentHash    string `json:`"content_hash"`          // hash of the turn slice
    ContentHash    string `json:`"content_hash"`          // hash of the redacted turn slice (never raw content)
    ImportVersion  int    `json:`"import_version"`        // importer schema version
}

Test cases

package agentimport

import (
    "crypto/sha256"
    "encoding/hex"
    "encoding/json"
    "fmt"
    "os"
)

func TestDeriveCheckpointID_StableAndDistinct(t *testing.T) {

}

func TestRun_AppliesConfiguredCustomRedaction(t *testing.T) {
    // A benign marker word that always-on secret scanning would never flag, so
    // redacting it can only be the configured custom rule's doing.
    const secret = "bananaphone-marker-word"
    redact.ConfigureCustomRules(redact.CustomRulesConfig{
        Inline: map[string]string{"acme-token": secret},
    })
    t.Cleanup(func() { redact.ConfigureCustomRules(redact.CustomRulesConfig{}) })

repo, repoDir := initRepoWithCommit(t)
    claudeDir := t.TempDir()
    content := strings.Join([]string{
        `{"type":"user","uuid":"u1","timestamp":"2026-06-20T00:00:00Z","message":{"role":"user","content":"use ` + secret + ` please"}}`,
        `{"type":"assistant","uuid":"a1","message":{"id":"m1","model":"claude-x","content":[{"type":"text","text":"ok"}],"usage":{"output_tokens":5}}}`,
    }, "\n") + "\n"
    if err := os.WriteFile(filepath.Join(claudeDir, "sess1.jsonl"), []byte(content), 0o644); err != nil {
        t.Fatal(err)
    }

res, err := Run(context.Background(), repo, claudeImporter{}, Options{
        RepoRoot: repoDir, OverridePath: claudeDir,
        Now: time.Date(2026, 6, 25, 0, 0, 0, 0, time.UTC),
    })
    if err != nil {
        t.Fatal(err)
    }
    if res.TurnsImported != 1 {
        t.Fatalf("want 1 imported, got %+v", res)
    }

stores, err := cp.Open(context.Background(), repo, cp.OpenOptions{})
    if err != nil {
        t.Fatal(err)
    }
    cid := DeriveCheckpointID("sess1", "u1")
    sc, err := stores.Persistent.ReadSessionContent(context.Background(), cid, 0)
    if err != nil {
        t.Fatal(err)
    }
    if strings.Contains(string(sc.Transcript), secret) {
        t.Fatalf("custom-configured secret was not redacted from imported transcript")
    }
    if !strings.Contains(string(sc.Transcript), redact.RedactedPlaceholder) {
        t.Fatalf("expected %q in redacted transcript, got: %s", redact.RedactedPlaceholder, sc.Transcript)
    }
}

func TestRun_DryRunWritesNothing(t *testing.T) {
    t.Parallel()
    repo, repoDir := initRepoWithCommit(t)
}`