migrate: pin metadata rebasing to the native git-refs layout · Entire
migrate: pin metadata rebasing to the native git-refs layout
a902b1a→main·
Soph·1w ago·1 file·+72 added/-5 removed
normalizeMigratedMetadata rebases session paths by string-stripping the
"/
Add a regression test that writes the same checkpoint content two ways — natively via the git-refs store and migrated from the git-branch store — and asserts their root-metadata session paths are identical. Any future divergence between the writer and the migration now fails loudly.
Co-Authored-By: Claude Opus 4.8 (1M context) noreply@anthropic.com
Sessions
01KWY7PAG1KG98TP2RHZX9RP6KView transcript
[?
Fix Git Migration Ref and Metadata HandlingClaude Code·Opus 4.8[1m]·2 steps](/content/gh/entireio/cli/session/27223555-55fc-47d9-892f-dbb48c2ab21a#timeline-01KWY7PAG1KG98TP2RHZX9RP6K/index.html) [?
Investigate CLI Enable Backend PingClaude Code·4 steps](/content/gh/entireio/cli/session/eb27315b-0e8e-4ec7-95a7-76f556039e46#timeline-01KWY7PAG1KG98TP2RHZX9RP6K/index.html)
Changes
1
cmd/entire/cli/checkpoint
Mmigrate_test.go+72/-5
2 unmodified lines
3
4
5
6
7
8
9
10
11
12
8 unmodified lines
21
22
23
21
22
23
24
24
25
26
27
28
29
30
31
1 unmodified line
33
34
35
32
36
37
38
39
40
41
42
43
44
45
151 unmodified lines
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
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
2 unmodified lines
import (
"context"
"encoding/json"
"fmt"
"os"
"sort"
"strings"
"testing"
git "github.com/go-git/go-git/v6"
8 unmodified lines
"github.com/entireio/cli/redact"
)
// seedBranchCheckpoint writes one checkpoint to the git-branch v1 store.
func seedBranchCheckpoint(t *testing.T, store *GitStore, cid id.CheckpointID, sessionID string) {
t.Helper()
require.NoError(t, store.Write(context.Background(), Session{
// sampleSession builds a checkpoint write request with deterministic content,
// shared so a checkpoint written to the git-branch and git-refs stores has
// byte-identical session contents.
func sampleSession(cid id.CheckpointID, sessionID string) Session {
return Session{
CheckpointID: cid,
SessionID: sessionID,
Strategy: "manual-commit",
1 unmodified line
Prompts: []string{"do the thing"},
AuthorName: "Test",
AuthorEmail: "test@test.com",
}))
}
}
// seedBranchCheckpoint writes one checkpoint to the git-branch v1 store.
func seedBranchCheckpoint(t *testing.T, store *GitStore, cid id.CheckpointID, sessionID string) {
t.Helper()
require.NoError(t, store.Write(context.Background(), sampleSession(cid, sessionID)))
}
// mutateBranchCheckpointMetadata rewrites a checkpoint's root metadata.json on
151 unmodified lines
assert.Equal(t, before[cid2.String()], refHash(t, repo, cid2))
}
// TestMigrateBranchToRefs_MetadataMatchesNativeRefsLayout pins the migration's
// metadata rebasing to the git-refs writer it must mirror. The migration rebases
// session paths by string surgery rather than round-tripping the metadata model,
// so if the native layout ever drifts (a renamed session dir, a new path field,
// a non-prefix-relative field) this fails loudly instead of silently shipping
// checkpoints whose paths a native reader can't resolve.
func TestMigrateBranchToRefs_MetadataMatchesNativeRefsLayout(t *testing.T) {
t.Parallel()
repo, _ := setupBranchTestRepo(t)
ctx := context.Background()
// The same checkpoint content written two ways: natively by the git-refs
// store, and migrated from the git-branch store.
nativeCID := id.MustCheckpointID("aaaaaaaaaaaa")
migratedCID := id.MustCheckpointID("bbbbbbbbbbbb")
refsStore := newGitRefsStore(repo)
require.NoError(t, refsStore.Write(ctx, sampleSession(nativeCID, "s1")))
branch := NewGitStore(repo, DefaultV1Refs())
seedBranchCheckpoint(t, branch, migratedCID, "s1")
_, err := MigrateBranchToRefs(ctx, repo, false)
require.NoError(t, err)
nativeCommit, err := repo.CommitObject(refHash(t, repo, nativeCID))
require.NoError(t, err)
migratedCommit, err := repo.CommitObject(refHash(t, repo, migratedCID))
require.NoError(t, err)
native := sessionPathFields(t, migratedMetadataDoc(t, repo, nativeCommit.TreeHash))
migrated := sessionPathFields(t, migratedMetadataDoc(t, repo, migratedCommit.TreeHash))
require.NotEmpty(t, migrated, "sanity: migrated metadata carries session paths")
assert.Equal(t, native, migrated,
"migrated session paths must match the native git-refs layout")
}
// sessionPathFields returns the sorted "field=value" pairs of every path-shaped
// (leading "/") string value under sessions[] — the layout the migration must
// keep in lockstep with the writer.
func sessionPathFields(t *testing.T, doc map[string]any) []string {
t.Helper()
set, ok := doc["sessions"].([]any)
require.True(t, ok, "sessions must be an array")
var out []string
for i, entry := range set {
session, ok := entry.(map[string]any)
require.True(t, ok)
for field, v := range session {
if s, ok := v.(string); ok && strings.HasPrefix(s, "/") {
out = append(out, fmt.Sprintf("%d.%s=%s", i, field, s))
}
}
}
sort.Strings(out)
return out
}
func TestMigrateBranchToRefs_AdvancesOnBranchChange(t *testing.T) {
t.Parallel()
repo, _ := setupBranchTestRepo(t)