Skip to content
zit Artifact [93b312d69c]
Login

Artifact [93b312d69c]

Artifact 93b312d69c8aff2d756af5dd0e1679edf42a8cc36d400f894bb0e2b174068aea:


const std = @import("std");
const repostate = @import("repostate.zig");

const Rid = repostate.Rid;
const Mtime = repostate.Mtime;
const TagType = repostate.TagType;
const MemStore = repostate.MemStore;
const RepoState = repostate.RepoState;

const t = std.testing;

const h_a = "a" ** 64;
const h_b = "b" ** 64;
const h_c = "c" ** 64;
const h_d = "d" ** 64;
const h_e = "e" ** 64;
const h_g = "0" ** 64;

fn countLeaves(rs: *const RepoState) usize {
    var it = rs.leaves();
    var n: usize = 0;
    while (it.next()) |_| n += 1;
    return n;
}

test "repostate §2: intern assigns one stable rid per hash" {
    var ms = MemStore.init(t.allocator);
    defer ms.deinit();
    const s = ms.store();
    const ra = try s.intern(h_a);
    const rb = try s.intern(h_b);
    try t.expect(ra != rb);
    try t.expectEqual(ra, try s.intern(h_a));
    try t.expectEqualStrings(h_a, s.hashOf(ra));
    try t.expectEqualStrings(h_b, s.hashOf(rb));
}

test "repostate §4.2: an interned but absent artifact is a phantom with no content" {
    var ms = MemStore.init(t.allocator);
    defer ms.deinit();
    const s = ms.store();
    const rid = try s.intern(h_a);
    try t.expectEqual(@as(?[]const u8, null), s.content(rid));
    // The referenced artifact arrives: the phantom upgrades in place.
    try t.expectEqual(rid, try s.put(h_a, "payload"));
    try t.expectEqualStrings("payload", s.content(rid).?);
    // A repeat put of the same hash is a no-op.
    try t.expectEqual(rid, try s.put(h_a, "payload"));
    try t.expectEqualStrings("payload", s.content(rid).?);
}

test "repostate §5.1: tag types encode cancel=0, singleton=1, propagate=2" {
    try t.expectEqual(0, @intFromEnum(TagType.cancel));
    try t.expectEqual(1, @intFromEnum(TagType.singleton));
    try t.expectEqual(2, @intFromEnum(TagType.propagate));
}

test "repostate §3.1: a repeated parent reference collapses to one linkage row" {
    var ms = MemStore.init(t.allocator);
    defer ms.deinit();
    var rs = RepoState.init(t.allocator, ms.store());
    defer rs.deinit();
    const ra = try rs.crosslink(.{ .hash = h_a, .mtime = 1 });
    const rb = try rs.crosslink(.{ .hash = h_b, .mtime = 2, .parents = &.{ h_a, h_a } });
    try t.expectEqual(@as(usize, 1), rs.parentCount(rb));
    try t.expectEqual(@as(?Rid, ra), rs.primaryParentOf(rb));
    try t.expect(!rs.isLeaf(ra));
    try t.expect(rs.isLeaf(rb));
}

test "repostate §3.2: the first listed parent is the primary parent" {
    var ms = MemStore.init(t.allocator);
    defer ms.deinit();
    var rs = RepoState.init(t.allocator, ms.store());
    defer rs.deinit();
    const ra = try rs.crosslink(.{ .hash = h_a, .mtime = 1 });
    _ = try rs.crosslink(.{ .hash = h_b, .mtime = 2 });
    const rc = try rs.crosslink(.{ .hash = h_c, .mtime = 3, .parents = &.{ h_a, h_b } });
    try t.expectEqual(@as(usize, 2), rs.parentCount(rc));
    try t.expectEqual(@as(?Rid, ra), rs.primaryParentOf(rc));
}

test "repostate §3.2: a sole parent is the primary parent" {
    var ms = MemStore.init(t.allocator);
    defer ms.deinit();
    var rs = RepoState.init(t.allocator, ms.store());
    defer rs.deinit();
    const ra = try rs.crosslink(.{ .hash = h_a, .mtime = 1 });
    const rb = try rs.crosslink(.{ .hash = h_b, .mtime = 2, .parents = &.{h_a} });
    try t.expectEqual(@as(?Rid, ra), rs.primaryParentOf(rb));
}

test "repostate §4.2: re-crosslinking a linked artifact is a linkage no-op" {
    var ms = MemStore.init(t.allocator);
    defer ms.deinit();
    var rs = RepoState.init(t.allocator, ms.store());
    defer rs.deinit();
    _ = try rs.crosslink(.{ .hash = h_a, .mtime = 1 });
    const rb = try rs.crosslink(.{ .hash = h_b, .mtime = 2, .parents = &.{h_a} });
    const rb_again = try rs.crosslink(.{ .hash = h_b, .mtime = 2, .parents = &.{h_a} });
    try t.expectEqual(rb, rb_again);
    try t.expectEqual(@as(usize, 1), rs.parentCount(rb));
    try t.expectEqual(@as(usize, 1), countLeaves(&rs));
    try t.expect(rs.isLeaf(rb));
}

test "repostate §4.2: an absent parent hash yields a phantom, resolved when it arrives" {
    var ms = MemStore.init(t.allocator);
    defer ms.deinit();
    var rs = RepoState.init(t.allocator, ms.store());
    defer rs.deinit();
    // The child arrives first; its parent hash resolves to a phantom
    // placeholder rather than a fatal error.
    const rb = try rs.crosslink(.{ .hash = h_b, .mtime = 2, .parents = &.{h_a} });
    const ra = rs.primaryParentOf(rb).?;
    try t.expectEqual(@as(?Mtime, null), rs.mtimeOf(ra));
    // A phantom is not a check-in: it never enters the leaf table.
    try t.expect(!rs.isLeaf(ra));
    try t.expect(rs.isLeaf(rb));
    try t.expectEqual(@as(usize, 1), countLeaves(&rs));
    // The referenced artifact arrives and is crosslinked: the recorded
    // linkage becomes visible in the derived views.
    try t.expectEqual(ra, try rs.crosslink(.{ .hash = h_a, .mtime = 1 }));
    try t.expectEqual(@as(?Mtime, 1), rs.mtimeOf(ra));
    try t.expect(!rs.isLeaf(ra));
    try t.expect(rs.isLeaf(rb));
    try t.expectEqual(@as(usize, 1), countLeaves(&rs));
}

test "repostate §7.2: a child reached only via a merge edge disqualifies the leaf" {
    var ms = MemStore.init(t.allocator);
    defer ms.deinit();
    var rs = RepoState.init(t.allocator, ms.store());
    defer rs.deinit();
    _ = try rs.crosslink(.{ .hash = h_a, .mtime = 1 });
    const rb = try rs.crosslink(.{ .hash = h_b, .mtime = 2, .parents = &.{h_a} });
    const rc = try rs.crosslink(.{ .hash = h_c, .mtime = 3, .parents = &.{h_a} });
    const rd = try rs.crosslink(.{ .hash = h_d, .mtime = 4, .parents = &.{ h_b, h_c } });
    // rc's only child rd is reached via a non-primary (merge) edge; the
    // canonical test applies no primary/merge filter.
    try t.expect(!rs.isLeaf(rb));
    try t.expect(!rs.isLeaf(rc));
    try t.expect(rs.isLeaf(rd));
    try t.expectEqual(@as(usize, 1), countLeaves(&rs));
}

test "repostate §7.3: a root check-in with no children is a leaf" {
    var ms = MemStore.init(t.allocator);
    defer ms.deinit();
    var rs = RepoState.init(t.allocator, ms.store());
    defer rs.deinit();
    const ra = try rs.crosslink(.{ .hash = h_a, .mtime = 1 });
    try t.expect(rs.isLeaf(ra));
    var it = rs.leaves();
    try t.expectEqual(@as(?Rid, ra), it.next());
    try t.expectEqual(@as(?Rid, null), it.next());
}

test "repostate §7.3: a new child retroactively removes its parent's leaf status" {
    var ms = MemStore.init(t.allocator);
    defer ms.deinit();
    var rs = RepoState.init(t.allocator, ms.store());
    defer rs.deinit();
    const ra = try rs.crosslink(.{ .hash = h_a, .mtime = 1 });
    try t.expect(rs.isLeaf(ra));
    const rb = try rs.crosslink(.{ .hash = h_b, .mtime = 2, .parents = &.{h_a} });
    try t.expect(!rs.isLeaf(ra));
    try t.expect(rs.isLeaf(rb));
}

test "repostate §7.3: full rebuild reproduces the incrementally maintained table" {
    var ms = MemStore.init(t.allocator);
    defer ms.deinit();
    var rs = RepoState.init(t.allocator, ms.store());
    defer rs.deinit();
    _ = try rs.crosslink(.{ .hash = h_a, .mtime = 1 });
    const rb = try rs.crosslink(.{ .hash = h_b, .mtime = 2, .parents = &.{h_a} });
    const rc = try rs.crosslink(.{ .hash = h_c, .mtime = 3, .parents = &.{h_a} });
    _ = try rs.crosslink(.{ .hash = h_d, .mtime = 4, .parents = &.{ h_b, h_c } });
    // An independent root with no children: a candidate the rebuild MUST
    // include even though it appears in no parent-link.
    const re = try rs.crosslink(.{ .hash = h_e, .mtime = 5 });
    var before: [8]Rid = undefined;
    const n_before = collectLeaves(&rs, &before);
    try rs.rebuildLeaves();
    var after: [8]Rid = undefined;
    const n_after = collectLeaves(&rs, &after);
    try t.expectEqual(n_before, n_after);
    std.mem.sort(Rid, before[0..n_before], {}, std.sort.asc(Rid));
    std.mem.sort(Rid, after[0..n_after], {}, std.sort.asc(Rid));
    try t.expectEqualSlices(Rid, before[0..n_before], after[0..n_after]);
    try t.expect(rs.isLeaf(re));
    try t.expect(!rs.isLeaf(rb));
    try t.expect(!rs.isLeaf(rc));
}

test "repostate §7.2/§6.1: a fork onto a new branch leaves the parent a leaf of its own branch" {
    var ms = MemStore.init(t.allocator);
    defer ms.deinit();
    var rs = RepoState.init(t.allocator, ms.store());
    defer rs.deinit();
    const ra = try rs.crosslink(.{ .hash = h_a, .mtime = 1 });
    const rb = try rs.crosslink(.{ .hash = h_b, .mtime = 2, .parents = &.{h_a}, .tags = &.{
        .{ .type = .propagate, .name = "branch", .target = null, .value = "feature" },
    } });
    try t.expect(rs.isLeaf(ra));
    try t.expect(rs.isLeaf(rb));
}

test "repostate §7.6: directly applying a branch tag re-evaluates leaf status" {
    var ms = MemStore.init(t.allocator);
    defer ms.deinit();
    var rs = RepoState.init(t.allocator, ms.store());
    defer rs.deinit();
    const ra = try rs.crosslink(.{ .hash = h_a, .mtime = 1 });
    _ = try rs.crosslink(.{ .hash = h_b, .mtime = 2, .parents = &.{h_a} });
    try t.expect(!rs.isLeaf(ra)); // same branch, has a child
    _ = try rs.crosslink(.{ .hash = h_c, .mtime = 3, .tags = &.{
        .{ .type = .propagate, .name = "branch", .target = h_b, .value = "feature" },
    } });
    try t.expect(rs.isLeaf(ra));
}

test "repostate §7.6: branch propagation re-evaluates a descendant's leaf status" {
    var ms = MemStore.init(t.allocator);
    defer ms.deinit();
    var rs = RepoState.init(t.allocator, ms.store());
    defer rs.deinit();
    const ra = try rs.crosslink(.{ .hash = h_a, .mtime = 1 });
    const rb = try rs.crosslink(.{ .hash = h_b, .mtime = 2, .parents = &.{h_a} });
    // C carries its own direct branch tag, so it blocks any further
    // propagation past it (Section 5.5: a nonzero-srcid row is never
    // overwritten by an inherited one).
    const rc = try rs.crosslink(.{ .hash = h_c, .mtime = 3, .parents = &.{h_b}, .tags = &.{
        .{ .type = .propagate, .name = "branch", .target = null, .value = "trunk" },
    } });
    try t.expect(!rs.isLeaf(ra));
    try t.expect(!rs.isLeaf(rb)); // same branch (trunk) as its child C
    try t.expect(rs.isLeaf(rc));
    // A, a pre-existing check-in with a child already in place, now gets a
    // branch tag. The walk reaches B — B carries no direct branch row of
    // its own, so it inherits "feature" — while C's direct "trunk" row
    // blocks the walk from reaching any further. B did not itself get
    // crosslinked by this call, so nothing but the propagation step re-
    // evaluates it. Derivation Section 7.6 second bullet: "when a branch
    // tag propagates to descendants, each descendant that gains or loses
    // the propagating tag MUST have its leaf status re-evaluated."
    _ = try rs.crosslink(.{ .hash = h_a, .mtime = 10, .tags = &.{
        .{ .type = .propagate, .name = "branch", .target = null, .value = "feature" },
    } });
    try t.expect(!rs.isLeaf(ra)); // A still shares a branch with child B
    try t.expect(rs.isLeaf(rb)); // B's only child C stayed on trunk: B gained leaf status
    try t.expect(rs.isLeaf(rc));
}

test "repostate §7.4: a leaf carrying an active closed marker is not an open leaf" {
    var ms = MemStore.init(t.allocator);
    defer ms.deinit();
    var rs = RepoState.init(t.allocator, ms.store());
    defer rs.deinit();
    const ra = try rs.crosslink(.{ .hash = h_a, .mtime = 1, .tags = &.{
        .{ .type = .singleton, .name = "closed", .target = null, .value = null },
    } });
    try t.expect(rs.isLeaf(ra));
    try t.expect(!rs.isOpenLeaf(ra));
}

test "repostate §7.4: cancelling the closed marker reopens the leaf" {
    var ms = MemStore.init(t.allocator);
    defer ms.deinit();
    var rs = RepoState.init(t.allocator, ms.store());
    defer rs.deinit();
    const ra = try rs.crosslink(.{ .hash = h_a, .mtime = 5, .tags = &.{
        .{ .type = .singleton, .name = "closed", .target = null, .value = null },
    } });
    try t.expect(!rs.isOpenLeaf(ra));
    _ = try rs.crosslink(.{ .hash = h_b, .mtime = 6, .tags = &.{
        .{ .type = .cancel, .name = "closed", .target = h_a, .value = null },
    } });
    try t.expect(rs.isOpenLeaf(ra));
}

test "repostate §6.1: branchOf returns the active branch-tag value" {
    var ms = MemStore.init(t.allocator);
    defer ms.deinit();
    var rs = RepoState.init(t.allocator, ms.store());
    defer rs.deinit();
    const ra = try rs.crosslink(.{ .hash = h_a, .mtime = 1, .tags = &.{
        .{ .type = .propagate, .name = "branch", .target = null, .value = "trunk" },
    } });
    try t.expectEqualStrings("trunk", rs.branchOf(ra, "main"));
}

test "repostate §6.1/§6.4: a check-in with no branch tag falls back to the configured main-branch" {
    var ms = MemStore.init(t.allocator);
    defer ms.deinit();
    var rs = RepoState.init(t.allocator, ms.store());
    defer rs.deinit();
    const ra = try rs.crosslink(.{ .hash = h_a, .mtime = 1 });
    // "main", not the literal "trunk" — proves the configured value is
    // read, not a hardcoded default (§6.1 "MUST NOT assume a fixed string").
    try t.expectEqualStrings("main", rs.branchOf(ra, "main"));
}

test "repostate §6.4: a cancelled branch tag is not active and falls back" {
    var ms = MemStore.init(t.allocator);
    defer ms.deinit();
    var rs = RepoState.init(t.allocator, ms.store());
    defer rs.deinit();
    const ra = try rs.crosslink(.{ .hash = h_a, .mtime = 1, .tags = &.{
        .{ .type = .cancel, .name = "branch", .target = null, .value = null },
    } });
    try t.expectEqualStrings("main", rs.branchOf(ra, "main"));
}

test "repostate §8.2: resolves to the active sym-name carrier with the greatest event.mtime" {
    var ms = MemStore.init(t.allocator);
    defer ms.deinit();
    var rs = RepoState.init(t.allocator, ms.store());
    defer rs.deinit();
    _ = try rs.crosslink(.{ .hash = h_a, .mtime = 1, .tags = &.{
        .{ .type = .propagate, .name = "sym-trunk", .target = null, .value = "trunk" },
    } });
    const rb = try rs.crosslink(.{ .hash = h_b, .mtime = 5, .parents = &.{h_a} });
    // B inherits the propagating sym-trunk tag; resolution keys on each
    // carrier's own event.mtime (Section 8.2), not the tag row's stored
    // mtime, so B's later commit time outranks the tag's origin A.
    try t.expectEqual(rb, try rs.resolveName("trunk"));
}

test "repostate §8.2: closed-ness does not filter resolution" {
    var ms = MemStore.init(t.allocator);
    defer ms.deinit();
    var rs = RepoState.init(t.allocator, ms.store());
    defer rs.deinit();
    _ = try rs.crosslink(.{ .hash = h_a, .mtime = 1, .tags = &.{
        .{ .type = .propagate, .name = "sym-trunk", .target = null, .value = "trunk" },
    } });
    const rb = try rs.crosslink(.{ .hash = h_b, .mtime = 5, .parents = &.{h_a}, .tags = &.{
        .{ .type = .singleton, .name = "closed", .target = null, .value = null },
    } });
    try t.expect(rs.isLeaf(rb));
    try t.expect(!rs.isOpenLeaf(rb));
    try t.expectEqual(rb, try rs.resolveName("trunk"));
}

test "repostate §8.2: leaf-ness does not filter resolution" {
    var ms = MemStore.init(t.allocator);
    defer ms.deinit();
    var rs = RepoState.init(t.allocator, ms.store());
    defer rs.deinit();
    // Singleton keeps the tag off B, isolating "a non-leaf still resolves"
    // from Section 5.4 inheritance mechanics.
    const ra = try rs.crosslink(.{ .hash = h_a, .mtime = 10, .tags = &.{
        .{ .type = .singleton, .name = "sym-trunk", .target = null, .value = "trunk" },
    } });
    _ = try rs.crosslink(.{ .hash = h_b, .mtime = 5, .parents = &.{h_a} });
    try t.expect(!rs.isLeaf(ra));
    try t.expectEqual(ra, try rs.resolveName("trunk"));
}

test "repostate §8.2: an unknown symbolic name is rejected as NotFound" {
    var ms = MemStore.init(t.allocator);
    defer ms.deinit();
    var rs = RepoState.init(t.allocator, ms.store());
    defer rs.deinit();
    _ = try rs.crosslink(.{ .hash = h_a, .mtime = 1 });
    try t.expectError(error.NotFound, rs.resolveName("nope"));
}

test "repostate §8.2: a name whose only sym-tag row is cancelled is NotFound" {
    var ms = MemStore.init(t.allocator);
    defer ms.deinit();
    var rs = RepoState.init(t.allocator, ms.store());
    defer rs.deinit();
    _ = try rs.crosslink(.{ .hash = h_a, .mtime = 1, .tags = &.{
        .{ .type = .cancel, .name = "sym-trunk", .target = null, .value = null },
    } });
    try t.expectError(error.NotFound, rs.resolveName("trunk"));
}

test "repostate §9/§2: current-trunk resolves the configured default branch name" {
    var ms = MemStore.init(t.allocator);
    defer ms.deinit();
    var rs = RepoState.init(t.allocator, ms.store());
    defer rs.deinit();
    const ra = try rs.crosslink(.{ .hash = h_a, .mtime = 1, .tags = &.{
        .{ .type = .propagate, .name = "sym-trunk", .target = null, .value = "trunk" },
    } });
    try t.expectEqual(ra, try rs.currentTrunk("trunk"));
    try t.expectEqual(try rs.resolveName("trunk"), try rs.currentTrunk("trunk"));
}

test "repostate §9: current-trunk of an unknown default branch is NotFound" {
    var ms = MemStore.init(t.allocator);
    defer ms.deinit();
    var rs = RepoState.init(t.allocator, ms.store());
    defer rs.deinit();
    _ = try rs.crosslink(.{ .hash = h_a, .mtime = 1 });
    try t.expectError(error.NotFound, rs.currentTrunk("nope"));
}

test "repostate §9: the open leaf carries an active branch tag, is a leaf, and is not closed" {
    var ms = MemStore.init(t.allocator);
    defer ms.deinit();
    var rs = RepoState.init(t.allocator, ms.store());
    defer rs.deinit();
    const ra = try rs.crosslink(.{ .hash = h_a, .mtime = 1, .tags = &.{
        .{ .type = .propagate, .name = "branch", .target = null, .value = "trunk" },
    } });
    try t.expectEqual(@as(?Rid, ra), rs.openLeafOf("trunk"));
}

test "repostate §9/§6.1: a tagless leaf is not the open leaf of the default branch" {
    var ms = MemStore.init(t.allocator);
    defer ms.deinit();
    var rs = RepoState.init(t.allocator, ms.store());
    defer rs.deinit();
    _ = try rs.crosslink(.{ .hash = h_a, .mtime = 1 });
    // Attributed to "trunk" only through the default-branch fallback: not
    // the open leaf of trunk (§6.1's fallback caveat).
    try t.expectEqual(@as(?Rid, null), rs.openLeafOf("trunk"));
}

test "repostate §9: a non-leaf on the branch is not an open leaf" {
    var ms = MemStore.init(t.allocator);
    defer ms.deinit();
    var rs = RepoState.init(t.allocator, ms.store());
    defer rs.deinit();
    const ra = try rs.crosslink(.{ .hash = h_a, .mtime = 1, .tags = &.{
        .{ .type = .propagate, .name = "branch", .target = null, .value = "trunk" },
    } });
    // B, a same-branch child, disqualifies A under condition (b) even
    // though A meets (a) and (c); B — the actual leaf — wins instead.
    const rb = try rs.crosslink(.{ .hash = h_b, .mtime = 2, .parents = &.{h_a} });
    try t.expect(!rs.isLeaf(ra));
    try t.expectEqual(@as(?Rid, rb), rs.openLeafOf("trunk"));
}

test "repostate §9: a closed leaf is not an open leaf" {
    var ms = MemStore.init(t.allocator);
    defer ms.deinit();
    var rs = RepoState.init(t.allocator, ms.store());
    defer rs.deinit();
    const ra = try rs.crosslink(.{ .hash = h_a, .mtime = 1, .tags = &.{
        .{ .type = .propagate, .name = "branch", .target = null, .value = "trunk" },
        .{ .type = .singleton, .name = "closed", .target = null, .value = null },
    } });
    try t.expect(rs.isLeaf(ra));
    try t.expectEqual(@as(?Rid, null), rs.openLeafOf("trunk"));
}

test "repostate §9: with several open leaves the greatest mtime is selected" {
    var ms = MemStore.init(t.allocator);
    defer ms.deinit();
    var rs = RepoState.init(t.allocator, ms.store());
    defer rs.deinit();
    _ = try rs.crosslink(.{ .hash = h_a, .mtime = 1, .tags = &.{
        .{ .type = .propagate, .name = "branch", .target = null, .value = "trunk" },
    } });
    const rb = try rs.crosslink(.{ .hash = h_b, .mtime = 2, .parents = &.{h_a} });
    const rc = try rs.crosslink(.{ .hash = h_c, .mtime = 3, .parents = &.{h_a} });
    try t.expect(rs.isLeaf(rb));
    try t.expect(rs.isLeaf(rc));
    try t.expectEqual(@as(?Rid, rc), rs.openLeafOf("trunk"));
}

test "repostate §9: a branch with no qualifying check-in has no open leaf" {
    var ms = MemStore.init(t.allocator);
    defer ms.deinit();
    var rs = RepoState.init(t.allocator, ms.store());
    defer rs.deinit();
    _ = try rs.crosslink(.{ .hash = h_a, .mtime = 1, .tags = &.{
        .{ .type = .propagate, .name = "branch", .target = null, .value = "trunk" },
        .{ .type = .singleton, .name = "closed", .target = null, .value = null },
    } });
    try t.expectEqual(@as(?Rid, null), rs.openLeafOf("trunk"));
}

test "repostate §10.1: fork at trunk with one closed leaf — resolution and open-leaf agree" {
    var ms = MemStore.init(t.allocator);
    defer ms.deinit();
    var rs = RepoState.init(t.allocator, ms.store());
    defer rs.deinit();
    _ = try rs.crosslink(.{ .hash = h_a, .mtime = 1, .tags = &.{
        .{ .type = .propagate, .name = "branch", .target = null, .value = "trunk" },
        .{ .type = .propagate, .name = "sym-trunk", .target = null, .value = "trunk" },
    } });
    const open_leaf = try rs.crosslink(.{ .hash = h_b, .mtime = 3, .parents = &.{h_a} });
    const closed_leaf = try rs.crosslink(.{ .hash = h_c, .mtime = 2, .parents = &.{h_a}, .tags = &.{
        .{ .type = .singleton, .name = "closed", .target = null, .value = null },
    } });
    _ = try rs.crosslink(.{ .hash = h_d, .mtime = 4, .parents = &.{h_a}, .tags = &.{
        .{ .type = .cancel, .name = "sym-trunk", .target = null, .value = null },
        .{ .type = .propagate, .name = "branch", .target = null, .value = "feature" },
        .{ .type = .propagate, .name = "sym-feature", .target = null, .value = "feature" },
    } });
    try t.expect(rs.isLeaf(open_leaf));
    try t.expect(rs.isOpenLeaf(open_leaf));
    try t.expect(rs.isLeaf(closed_leaf));
    try t.expect(!rs.isOpenLeaf(closed_leaf));
    // §10.1: leaves lists the open trunk leaf while omitting the closed
    // sibling; whatis trunk agrees with that same open leaf here.
    try t.expectEqual(open_leaf, try rs.resolveName("trunk"));
    try t.expectEqual(@as(?Rid, open_leaf), rs.openLeafOf("trunk"));
}

test "repostate §10.2: a closed leaf newer than the open leaf — resolution and open-leaf diverge" {
    var ms = MemStore.init(t.allocator);
    defer ms.deinit();
    var rs = RepoState.init(t.allocator, ms.store());
    defer rs.deinit();
    _ = try rs.crosslink(.{ .hash = h_a, .mtime = 1, .tags = &.{
        .{ .type = .propagate, .name = "branch", .target = null, .value = "trunk" },
        .{ .type = .propagate, .name = "sym-trunk", .target = null, .value = "trunk" },
    } });
    const open_leaf = try rs.crosslink(.{ .hash = h_b, .mtime = 2, .parents = &.{h_a} });
    const closed_leaf = try rs.crosslink(.{ .hash = h_c, .mtime = 3, .parents = &.{h_a}, .tags = &.{
        .{ .type = .singleton, .name = "closed", .target = null, .value = null },
    } });
    try t.expect(rs.isOpenLeaf(open_leaf));
    try t.expect(rs.isLeaf(closed_leaf));
    try t.expect(!rs.isOpenLeaf(closed_leaf));
    // §10.2: whatis trunk reports the closed, newer check-in even though
    // the default open-leaf view omits it — resolution and open-leaf
    // enumeration diverge on this graph.
    try t.expectEqual(closed_leaf, try rs.resolveName("trunk"));
    try t.expectEqual(@as(?Rid, open_leaf), rs.openLeafOf("trunk"));
    try t.expect((try rs.resolveName("trunk")) != rs.openLeafOf("trunk").?);
}

fn collectLeaves(rs: *const RepoState, out: []Rid) usize {
    var it = rs.leaves();
    var n: usize = 0;
    while (it.next()) |rid| : (n += 1) out[n] = rid;
    return n;
}

fn crosslinkNoLeak(gpa: std.mem.Allocator) !void {
    var ms = MemStore.init(gpa);
    defer ms.deinit();
    var rs = RepoState.init(gpa, ms.store());
    defer rs.deinit();
    // Self-branch root plus a tag on a not-yet-stored (phantom) target, so
    // internTag/applyDirect/propagate all allocate.
    _ = try rs.crosslink(.{ .hash = h_a, .mtime = 1, .tags = &.{
        .{ .type = .propagate, .name = "branch", .target = null, .value = "feature" },
        .{ .type = .propagate, .name = "t1", .target = h_g, .value = "v" },
    } });
    _ = try rs.crosslink(.{ .hash = h_b, .mtime = 2, .parents = &.{h_a} });
    // Phantom parent plus a merge, so every allocation site is exercised.
    _ = try rs.crosslink(.{ .hash = h_d, .mtime = 4, .parents = &.{ h_b, h_c } });
    // A cancel and a singleton (+closed), exercising the value-clear and
    // downgrade-at-repropagation paths.
    _ = try rs.crosslink(.{ .hash = h_c, .mtime = 3, .parents = &.{h_a}, .tags = &.{
        .{ .type = .cancel, .name = "t1", .target = null, .value = null },
        .{ .type = .singleton, .name = "closed", .target = null, .value = null },
    } });
    _ = try rs.crosslink(.{ .hash = h_g, .mtime = 5 }); // the phantom target arrives
    try rs.rebuildLeaves();
}

test "repostate §4.2: crosslink survives allocation failure without leaking" {
    try t.checkAllAllocationFailures(t.allocator, crosslinkNoLeak, .{});
}

/// Fork topology reused across the root:/start:/merge-in:/branch-type
/// tests: A (self-tagged trunk) <- B (plain, inherits trunk) <- C
/// (self-tagged feature, diverging from trunk at B) <- D (plain, inherits
/// feature).
const TopoT = struct { a: Rid, b: Rid, c: Rid, d: Rid };

fn buildTopoT(rs: *RepoState) !TopoT {
    const a = try rs.crosslink(.{ .hash = h_a, .mtime = 1, .tags = &.{
        .{ .type = .propagate, .name = "sym-trunk", .target = null, .value = "trunk" },
        .{ .type = .propagate, .name = "branch", .target = null, .value = "trunk" },
    } });
    const b = try rs.crosslink(.{ .hash = h_b, .mtime = 2, .parents = &.{h_a} });
    const c = try rs.crosslink(.{ .hash = h_c, .mtime = 3, .parents = &.{h_b}, .tags = &.{
        .{ .type = .propagate, .name = "sym-feature", .target = null, .value = "feature" },
        .{ .type = .propagate, .name = "branch", .target = null, .value = "feature" },
    } });
    const d = try rs.crosslink(.{ .hash = h_d, .mtime = 4, .parents = &.{h_c} });
    return .{ .a = a, .b = b, .c = c, .d = d };
}

test "repostate §8.3.1: rid: resolves a numeric RID to a known check-in" {
    var ms = MemStore.init(t.allocator);
    defer ms.deinit();
    var rs = RepoState.init(t.allocator, ms.store());
    defer rs.deinit();
    const topo = try buildTopoT(&rs);
    var buf: [32]u8 = undefined;
    const name = try std.fmt.bufPrint(&buf, "rid:{d}", .{topo.b});
    try t.expectEqual(topo.b, try rs.resolve(name, .checkin));
}

test "repostate §8.3.1: rid: an unknown or non-numeric RID is NotFound" {
    var ms = MemStore.init(t.allocator);
    defer ms.deinit();
    var rs = RepoState.init(t.allocator, ms.store());
    defer rs.deinit();
    _ = try rs.crosslink(.{ .hash = h_a, .mtime = 1 });
    const cases = [_][]const u8{ "rid:999", "rid:xyz" };
    for (cases) |name| {
        try t.expectError(error.NotFound, rs.resolve(name, .checkin));
    }
}

test "repostate §8.3.2: tag: forces literal tag resolution over hash interpretation" {
    var ms = MemStore.init(t.allocator);
    defer ms.deinit();
    var rs = RepoState.init(t.allocator, ms.store());
    defer rs.deinit();
    const h_hash_match = "dead" ++ "2" ** 60;
    const rx = try rs.crosslink(.{ .hash = h_hash_match, .mtime = 1 });
    const ry = try rs.crosslink(.{ .hash = h_c, .mtime = 2, .tags = &.{
        .{ .type = .propagate, .name = "sym-dead", .target = null, .value = "dead" },
    } });
    try t.expectEqual(rx, try rs.resolve("dead", .checkin));
    try t.expectEqual(ry, try rs.resolve("tag:dead", .checkin));
    try t.expect((try rs.resolve("dead", .checkin)) != (try rs.resolve("tag:dead", .checkin)));
}

test "repostate §8.3.3: tip resolves the global maximum event.mtime check-in" {
    var ms = MemStore.init(t.allocator);
    defer ms.deinit();
    var rs = RepoState.init(t.allocator, ms.store());
    defer rs.deinit();
    const topo = try buildTopoT(&rs);
    try t.expectEqual(topo.d, try rs.resolve("tip", .checkin));
}

test "repostate §8.3.4/§8.4: root: rewrites to the first ancestor off the branch" {
    var ms = MemStore.init(t.allocator);
    defer ms.deinit();
    var rs = RepoState.init(t.allocator, ms.store());
    defer rs.deinit();
    const topo = try buildTopoT(&rs);
    // Walk D -> C[feature] -> B[trunk]: B is the first ancestor off feature.
    try t.expectEqual(topo.b, try rs.resolve("root:feature", .checkin));
}

test "repostate §8.3.5/§8.4: start: rewrites to the earliest check-in on the branch" {
    var ms = MemStore.init(t.allocator);
    defer ms.deinit();
    var rs = RepoState.init(t.allocator, ms.store());
    defer rs.deinit();
    const topo = try buildTopoT(&rs);
    try t.expectEqual(topo.c, try rs.resolve("start:feature", .checkin));
}

test "repostate §8.3.6: merge-in: rewrites to the youngest parent-branch ancestor" {
    var ms = MemStore.init(t.allocator);
    defer ms.deinit();
    var rs = RepoState.init(t.allocator, ms.store());
    defer rs.deinit();
    const topo = try buildTopoT(&rs);
    // Section 8.4's two-variant walk gives merge-in: the same node as
    // root: here: the youngest ancestor on the parent branch is exactly
    // the first ancestor found off the diverging branch.
    try t.expectEqual(topo.b, try rs.resolve("merge-in:feature", .checkin));
}

test "repostate §8.3: branch-type request post-processes to the branch root" {
    var ms = MemStore.init(t.allocator);
    defer ms.deinit();
    var rs = RepoState.init(t.allocator, ms.store());
    defer rs.deinit();
    const topo = try buildTopoT(&rs);
    try t.expectEqual(topo.c, try rs.resolve("feature", .branch));
}

test "repostate §8.3.8: an ambiguous hash prefix is rejected" {
    var ms = MemStore.init(t.allocator);
    defer ms.deinit();
    var rs = RepoState.init(t.allocator, ms.store());
    defer rs.deinit();
    const h1 = "abcd" ++ "1" ** 60;
    const h2 = "abcd" ++ "2" ** 60;
    _ = try rs.crosslink(.{ .hash = h1, .mtime = 1 });
    _ = try rs.crosslink(.{ .hash = h2, .mtime = 2 });
    try t.expectError(error.Ambiguous, rs.resolve("abcd", .checkin));
}

test "repostate §8.3.8: a unique hash prefix resolves to its single check-in" {
    var ms = MemStore.init(t.allocator);
    defer ms.deinit();
    var rs = RepoState.init(t.allocator, ms.store());
    defer rs.deinit();
    const ra = try rs.crosslink(.{ .hash = h_a, .mtime = 1 });
    try t.expectEqual(ra, try rs.resolve("aaaa", .checkin));
}

test "repostate §8.3: a trailing punctuation character is stripped and retried" {
    var ms = MemStore.init(t.allocator);
    defer ms.deinit();
    var rs = RepoState.init(t.allocator, ms.store());
    defer rs.deinit();
    _ = try rs.crosslink(.{ .hash = h_a, .mtime = 1, .tags = &.{
        .{ .type = .propagate, .name = "sym-trunk", .target = null, .value = "trunk" },
    } });
    try t.expectEqual(try rs.resolve("trunk", .checkin), try rs.resolve("trunk.", .checkin));
}

test "repostate §8.3: a name with no matching form is NotFound" {
    var ms = MemStore.init(t.allocator);
    defer ms.deinit();
    var rs = RepoState.init(t.allocator, ms.store());
    defer rs.deinit();
    _ = try rs.crosslink(.{ .hash = h_a, .mtime = 1 });
    // "zzz": no form matches and no punctuation to strip.
    try t.expectError(error.NotFound, rs.resolve("zzz", .checkin));
    // "..": the punct-retry exhausts down to an empty slice.
    try t.expectError(error.NotFound, rs.resolve("..", .checkin));
}

test "repostate §8.3.9: plain name falls through to the §8.2 Rule" {
    var ms = MemStore.init(t.allocator);
    defer ms.deinit();
    var rs = RepoState.init(t.allocator, ms.store());
    defer rs.deinit();
    _ = try rs.crosslink(.{ .hash = h_a, .mtime = 1, .tags = &.{
        .{ .type = .propagate, .name = "sym-trunk", .target = null, .value = "trunk" },
    } });
    try t.expectEqual(try rs.resolveName("trunk"), try rs.resolve("trunk", .checkin));
}

test "repostate §8.4: branch-walk is bounded by the primary-parent chain" {
    var ms = MemStore.init(t.allocator);
    defer ms.deinit();
    var rs = RepoState.init(t.allocator, ms.store());
    defer rs.deinit();
    const ra = try rs.crosslink(.{ .hash = h_a, .mtime = 1, .tags = &.{
        .{ .type = .propagate, .name = "sym-trunk", .target = null, .value = "trunk" },
        .{ .type = .propagate, .name = "branch", .target = null, .value = "trunk" },
    } });
    // A root-only branch: root:trunk never finds an ancestor off-branch, so
    // the walk falls back to the branch root itself.
    try t.expectEqual(ra, try rs.resolve("root:trunk", .checkin));
}

test "repostate §6.3: enumerates one entry per distinct active branch value" {
    var ms = MemStore.init(t.allocator);
    defer ms.deinit();
    var rs = RepoState.init(t.allocator, ms.store());
    defer rs.deinit();
    _ = try buildTopoT(&rs);
    const entries = try rs.branches(t.allocator);
    defer t.allocator.free(entries);
    try t.expectEqual(@as(usize, 2), entries.len);
    try t.expectEqualStrings("feature", entries[0].name);
    try t.expectEqualStrings("trunk", entries[1].name);
}

test "repostate §6.3: a branch entry's representative is its greatest-mtime member" {
    var ms = MemStore.init(t.allocator);
    defer ms.deinit();
    var rs = RepoState.init(t.allocator, ms.store());
    defer rs.deinit();
    const topo = try buildTopoT(&rs);
    const entries = try rs.branches(t.allocator);
    defer t.allocator.free(entries);
    var trunk: ?RepoState.BranchEntry = null;
    for (entries) |e| {
        if (std.mem.eql(u8, e.name, "trunk")) trunk = e;
    }
    try t.expectEqual(topo.b, trunk.?.tip);
    try t.expectEqual(@as(usize, 2), trunk.?.count);
    try t.expectEqual(@as(Mtime, 2), trunk.?.mtime);
}

test "repostate §6.3: listing-closed reflects the representative row, diverging from §9 open-leaf" {
    var ms = MemStore.init(t.allocator);
    defer ms.deinit();
    var rs = RepoState.init(t.allocator, ms.store());
    defer rs.deinit();
    _ = try rs.crosslink(.{ .hash = h_a, .mtime = 1, .tags = &.{
        .{ .type = .propagate, .name = "branch", .target = null, .value = "trunk" },
    } });
    const older_leaf = try rs.crosslink(.{ .hash = h_b, .mtime = 2, .parents = &.{h_a} });
    const newer_leaf = try rs.crosslink(.{ .hash = h_c, .mtime = 3, .parents = &.{h_a} });
    _ = try rs.crosslink(.{ .hash = h_d, .mtime = 5, .tags = &.{
        .{ .type = .singleton, .name = "closed", .target = h_c, .value = null },
    } });
    const entries = try rs.branches(t.allocator);
    defer t.allocator.free(entries);
    try t.expectEqual(@as(usize, 1), entries.len);
    try t.expectEqual(newer_leaf, entries[0].tip);
    try t.expect(entries[0].closed);
    try t.expectEqual(@as(?Rid, older_leaf), rs.openLeafOf("trunk"));
}

test "repostate §6.3: branches survives allocation failure without leaking" {
    try t.checkAllAllocationFailures(t.allocator, branchesNoLeak, .{});
}

fn branchesNoLeak(gpa: std.mem.Allocator) !void {
    var ms = MemStore.init(gpa);
    defer ms.deinit();
    var rs = RepoState.init(gpa, ms.store());
    defer rs.deinit();
    _ = try buildTopoT(&rs);
    const entries = try rs.branches(gpa);
    gpa.free(entries);
}

test "repostate §7.5: a non-leaf is never an ambiguous leaf" {
    var ms = MemStore.init(t.allocator);
    defer ms.deinit();
    var rs = RepoState.init(t.allocator, ms.store());
    defer rs.deinit();
    _ = try rs.crosslink(.{ .hash = h_a, .mtime = 1 });
    const rb = try rs.crosslink(.{ .hash = h_b, .mtime = 2, .parents = &.{h_a} });
    _ = try rs.crosslink(.{ .hash = h_c, .mtime = 3, .parents = &.{h_b} });
    try t.expect(!rs.isAmbiguousLeaf(rb));
}

test "repostate §7.5: a lone open leaf is not ambiguous" {
    var ms = MemStore.init(t.allocator);
    defer ms.deinit();
    var rs = RepoState.init(t.allocator, ms.store());
    defer rs.deinit();
    const ra = try rs.crosslink(.{ .hash = h_a, .mtime = 1 });
    try t.expect(!rs.isAmbiguousLeaf(ra));
}

test "repostate §7.5: two open leaves on a branch are each ambiguous" {
    var ms = MemStore.init(t.allocator);
    defer ms.deinit();
    var rs = RepoState.init(t.allocator, ms.store());
    defer rs.deinit();
    _ = try rs.crosslink(.{ .hash = h_a, .mtime = 1 });
    const rb1 = try rs.crosslink(.{ .hash = h_b, .mtime = 2, .parents = &.{h_a} });
    const rb2 = try rs.crosslink(.{ .hash = h_c, .mtime = 3, .parents = &.{h_a} });
    try t.expect(rs.isAmbiguousLeaf(rb1));
    try t.expect(rs.isAmbiguousLeaf(rb2));
}

test "repostate §7.5: a closed sibling does not make a leaf ambiguous" {
    var ms = MemStore.init(t.allocator);
    defer ms.deinit();
    var rs = RepoState.init(t.allocator, ms.store());
    defer rs.deinit();
    _ = try rs.crosslink(.{ .hash = h_a, .mtime = 1 });
    const rb1 = try rs.crosslink(.{ .hash = h_b, .mtime = 2, .parents = &.{h_a} });
    _ = try rs.crosslink(.{ .hash = h_c, .mtime = 3, .parents = &.{h_a}, .tags = &.{
        .{ .type = .singleton, .name = "closed", .target = null, .value = null },
    } });
    try t.expect(!rs.isAmbiguousLeaf(rb1));
}

test "repostate §7.5: open-leaf listing is mtime-descending" {
    var ms = MemStore.init(t.allocator);
    defer ms.deinit();
    var rs = RepoState.init(t.allocator, ms.store());
    defer rs.deinit();
    _ = try rs.crosslink(.{ .hash = h_a, .mtime = 1, .tags = &.{
        .{ .type = .propagate, .name = "branch", .target = null, .value = "trunk" },
    } });
    const rb1 = try rs.crosslink(.{ .hash = h_b, .mtime = 2, .parents = &.{h_a} });
    const rb2 = try rs.crosslink(.{ .hash = h_c, .mtime = 3, .parents = &.{h_a} });
    const leaves_out = try rs.openLeavesOf(t.allocator, "trunk");
    defer t.allocator.free(leaves_out);
    try t.expectEqualSlices(Rid, &.{ rb2, rb1 }, leaves_out);
}

test "repostate §7.5: openLeavesOf survives allocation failure without leaking" {
    try t.checkAllAllocationFailures(t.allocator, openLeavesOfNoLeak, .{});
}

fn openLeavesOfNoLeak(gpa: std.mem.Allocator) !void {
    var ms = MemStore.init(gpa);
    defer ms.deinit();
    var rs = RepoState.init(gpa, ms.store());
    defer rs.deinit();
    _ = try rs.crosslink(.{ .hash = h_a, .mtime = 1, .tags = &.{
        .{ .type = .propagate, .name = "branch", .target = null, .value = "trunk" },
    } });
    _ = try rs.crosslink(.{ .hash = h_b, .mtime = 2, .parents = &.{h_a} });
    _ = try rs.crosslink(.{ .hash = h_c, .mtime = 3, .parents = &.{h_a} });
    const leaves_out = try rs.openLeavesOf(gpa, "trunk");
    gpa.free(leaves_out);
}

test "repostate §8.3 fuzz: dispatch never crashes on adversarial name bytes" {
    var ms = MemStore.init(t.allocator);
    defer ms.deinit();
    var rs = RepoState.init(t.allocator, ms.store());
    defer rs.deinit();
    _ = try buildTopoT(&rs);
    var prng = std.Random.DefaultPrng.init(0xF055_D47E);
    const random = prng.random();
    // Every internal parser this loop can reach (rid:, hash-prefix,
    // expandTimestamp, punct-retry) sees adversarial bytes, NUL included
    // per the wire-data contract; `resolve`'s own postcondition assert
    // doubles as the invariant check — any bad candidate rid traps under
    // Debug/ReleaseSafe rather than the assertion being separately
    // reasserted here.
    const alphabet = "abcdef0123456789-: .ABCXYZ_/\\\x00";
    var buf: [40]u8 = undefined;
    var i: usize = 0;
    const iterations = 2000;
    while (i < iterations) : (i += 1) {
        const len = random.uintLessThan(usize, buf.len) + 1;
        for (buf[0..len]) |*c| c.* = alphabet[random.uintLessThan(usize, alphabet.len)];
        _ = rs.resolve(buf[0..len], .checkin) catch |err| switch (err) {
            error.NotFound, error.Ambiguous => {},
        };
    }
}