checkpoint resume: simplify picker and prefix gate · Entire

checkpoint resume: simplify picker and prefix gate

c6659c5→main·

Rely on store.List's documented newest-first ordering instead of re-sorting, move the checkpoint-prefix shape gate into the id package next to the patterns it must not drift from, reuse the existing picker cancel sentinel, and share the repeated test repo setup.

Sessions

01KX48VMKTYXYNTAGAK3B2BCQNView transcript

Changes

4

43 unmodified lines

// a loose shape, not authoritative validation).
const CheckpointPattern = `(?:` + Pattern + `|` + ulidPattern + `)`

// prefixShapeRegex matches strings shaped like a checkpoint ID or a prefix of
// one: 1-12 lowercase hex characters (legacy) or 1-26 Crockford base32
// characters (ULID). Kept next to Pattern/ulidPattern so the alphabet and
// width bounds cannot drift from the full-ID patterns above.
var prefixShapeRegex = regexp.MustCompile(`^(?:[0-9a-f]{1,12}|[0-9ABCDEFGHJKMNPQRSTVWXYZ]{1,26})$`)

// CouldBePrefix reports whether s is shaped like a checkpoint ID or a prefix
// of one. It is a cheap gate for callers deciding whether a free-form target
// could name a checkpoint before paying for a store lookup; it is not
// validation (see Validate).
func CouldBePrefix(s string) bool {
    return prefixShapeRegex.MatchString(s)
}

// ShortIDLength is the standard length for truncating IDs for display purposes.
// Used for tool use IDs, session IDs, and commit hashes in logs and messages.
const ShortIDLength = 12
func TestCouldBePrefix(t *testing.T) {
    t.Parallel()
    tests := []struct {
        input string
        want  bool
    }{
        {"abc123", true},
        {"abc123def456", true},
        {"01HZXW5J8KQ2M3N4P5Q6R7S8T9", true},
        {"01HZXW", true},
        {"HEAD", true},
        {"", false},
        {"abc123def4567", false},
        {"feature/foo", false},
        {"abcdefI", false},
        {"01hzxw5j8kq2m3n4p5q6r7s8t9x", false},
    }
    for _, tt := range tests {
        if got := CouldBePrefix(tt.input); got != tt.want {
            t.Errorf("CouldBePrefix(%q) = %v, want %v", tt.input, got, tt.want)
        }
    }
}
func TestRecentCheckpoints_SortsNewestFirstAndCaps(t *testing.T) {
    t.Parallel()
    base := time.Date(2025, 1, 1, 0, 0, 0, 0, time.UTC)
    var infos []checkpoint.CheckpointInfo
    for i := range 25 {
        infos = append(infos, checkpoint.CheckpointInfo{
            CheckpointID: id.MustCheckpointID(fmt.Sprintf("%012x", i)),
            CreatedAt:    base.Add(time.Duration(i) * time.Hour),
        })
    }
    got := recentCheckpoints(infos, 20)
    if len(got) != 20 {
        t.Fatalf("len = %d, want 20", len(got))
    }
    if !got[0].CreatedAt.After(got[19].CreatedAt) {
        t.Errorf("not sorted newest first: got[0]=%v got[19]=%v", got[0].CreatedAt, got[19].CreatedAt)
    }
    if got[0].CreatedAt != base.Add(24*time.Hour) {
        t.Errorf("newest = %v, want %v", got[0].CreatedAt, base.Add(24*time.Hour))
    }
}

Jaeger's test repo setup for various test cases involving checkpoints and their management is also studied thoroughly.