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

`1747e42`→[main](/content/gh/entireio/cli/commits/main/index.html)·

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

Address review findings on imported-checkpoint history:

- Call strategy.EnsureRedactionConfigured() before import writes so
  imported transcripts honor opt-in PII, custom_redactions, and redactor
  packs, not just always-on secret scanning. Covered by a custom-redaction
  test.
- Store only the transcript basename (transcript_file) in provenance
  instead of the absolute path, which leaked local usernames/repo layout
  onto the shared v1 branch.
- Hash the redacted turn slice instead of raw bytes, closing a
  confirmation attack against guessed raw prompt/output content.
- Gate the explain "--generate" hint on summary.Imported, since imported
  history is read-only and --generate is refused.

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

## Sessions

3e645399c3f3View transcript

## Changes

7

- api/checkpoint

- Mmetadata.go+2/-2
  - Mmetadata_test.go+1/-1

- cmd/entire/cli

- agentimport

- Magentimport.go+30/-6
    - Magentimport_test.go+52
    - Mclaude.go+8/-14

- Mexplain.go+5
  - Mimport_cmd.go+7

```go
// 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

```go
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)
}`
```
