Add hidden `doctor migrate-to-ulid` validation tooling (branch+hex → refs+ULID) · Entire

Add hidden doctor migrate-to-ulid validation tooling (branch+hex → refs+ULID)

2dc8888· Soph·1w ago·5 files·+689 added/-0 removed

Test tooling to convert a THROWAWAY COPY of a git-branch + hex-id repo into one that reads as if it had used git-refs + ULIDs from the start, so we can stage a realistic validation repo and exercise commands + the UI against it.

What it does (dry-run preview by default; --yes to apply):

  1. Re-identifies every checkpoint on entire/checkpoints/v1 as a fresh ULID (minted from the checkpoint's original CreatedAt, so ULIDs sort by real time), stored under refs/entire/checkpoints//. The embedded checkpoint_id in the root + per-session metadata.json is re-stamped to the ULID; everything else (transcripts, summaries, attribution) is carried over byte-for-byte. No commit SHAs are embedded in checkpoint content, so the later history rewrite doesn't stale it.
  2. Rewrites the Entire-Checkpoint commit trailers hex → ULID across all local branches except entire/* (git filter-branch --msg-filter; rewrites history).
  3. Deletes the entire/checkpoints/v1 branch and the shadow branches so only the ULID refs remain — refs-native.

The migrated checkpoints resolve through the normal read path (kind routing → refs) and are enqueued for push like any git-refs write.

Kept hidden and, per intent, on this branch (not for merge to main) — it rewrites history irreversibly apart from git's refs/original backup.

Tests: core re-ID/re-stamp/dry-run/no-branch + ULID-timestamp minting, and an end-to-end command test (real commit trailers → ULID, v1 deleted, checkpoint resolves via Open().Persistent).

Sessions

904b45068824View transcript

Changes

5

package checkpoint

import (
    "context"
    "crypto/rand"
    "encoding/json"
    "errors"
    "fmt"
    "log/slog"
    "strconv"
    "time"

git "github.com/go-git/go-git/v6"
    "github.com/go-git/go-git/v6/plumbing"
    "github.com/go-git/go-git/v6/plumbing/filemode"
    "github.com/go-git/go-git/v6/plumbing/object"
    ulidpkg "github.com/oklog/ulid/v2"

"github.com/entireio/cli/cmd/entire/cli/checkpoint/id"
    "github.com/entireio/cli/cmd/entire/cli/logging"
    "github.com/entireio/cli/cmd/entire/cli/paths"
)

// ULIDMapping records one legacy-hex checkpoint re-identified as a ULID.
type ULIDMapping struct {
    OldID id.CheckpointID // legacy 12-hex id (on the v1 branch)
    NewID id.CheckpointID // freshly minted ULID (now a per-checkpoint ref)
}

// ULIDMigrateResult summarizes a hex → ULID checkpoint migration.
type ULIDMigrateResult struct {
    // Total is the number of checkpoints found on the v1 branch.
    Total int
    // Mapping lists each re-identified checkpoint, in walk order. Callers use it
    // to rewrite the Entire-Checkpoint commit trailers from hex to ULID.
    Mapping []ULIDMapping
    // Skipped counts checkpoints left as-is because their id is already a ULID.
    Skipped int
}

// MigrateBranchHexToULIDRefs re-identifies every legacy-hex checkpoint on the
// git-branch v1 branch (entire/checkpoints/v1) as a fresh ULID stored under a
// per-checkpoint ref (refs/entire/checkpoints/<shard>/<ulid>), ...

func MigrateBranchHexToULIDRefs(ctx context.Context, repo *git.Repository, dryRun bool) (ULIDMigrateResult, error) {
    var result ULIDMigrateResult

branch := NewGitStore(repo, DefaultV1Refs())
    tree, err := branch.getSessionsBranchTree()
    // ... omitted for brevity
}

// mintULIDAt mints a canonical ULID checkpoint id whose timestamp is t (falling
// back to now when t is zero), ...
func mintULIDAt(t time.Time) (id.CheckpointID, error) {
    // ... omitted for brevity
}

// restampCheckpointID rewrites the embedded checkpoint_id (hex → newID) in the
// checkpoint's metadata.json files ...
func restampCheckpointID(repo *git.Repository, cpTreeHash plumbing.Hash, newID id.CheckpointID, sessionCount int) (plumbing.Hash, error) {
    // ... omitted for brevity
}

Here is where more information about implementation details would go.