Skip to content
zit Artifact [33d395df04]
Login

Artifact [33d395df04]

Artifact 33d395df0443d898d5a046a135bb2070b6e555d13d5eb8fde997e1749dff9974:


//! Fossil interop smoke check — the one sanctioned use of the real `fossil`
//! binary. `fossil` is invoked nowhere else in this tree.
//!
//! Policy, enforced by this file's shape, not by convention:
//!   - Every byte handed to fossil is zit's OWN output (`artifact.create` /
//!     `delta.create` over an RFC fixture) -- never the raw RFC transcription,
//!     never anything fossil produced.
//!   - fossil may only CONFIRM an RFC-cited zit expectation. A scenario that
//!     would require comparing zit bytes to fossil-AUTHORED bytes (e.g.
//!     `fossil test-delta-create`) does not belong here.
//!   - On any mismatch, print the diff and the RFC citation and exit 1. This
//!     file never adjusts zit to match fossil -- a mismatch is an operator
//!     decision (RFC misread vs RFC wrong), recorded in the untracked
//!     ambiguities ledger, not "fixed" in place.
//!
//! Milestone-manual: this executable lives solely behind `zig build smoke`.
//! `zig build test` compiles it (catching type errors) but never runs its
//! scenarios; it is not imported by `root.zig`'s test aggregation.

const std = @import("std");
const builtin = @import("builtin");
const zit = @import("zit");
const artifact = zit.artifact;
const rfc_vectors = zit.rfc_vectors;
const delta = zit.delta;
const build_options = @import("build_options");
const net = std.Io.net;

/// The compiled `zit` binary, threaded in by build.zig (a `LazyPath`, always
/// rebuilt first). `build_options.zit_exe` is relative to the build root, which
/// is this process's cwd; S5 resolves it to an absolute path before spawning it
/// as a subprocess (`zit init/add/commit/pack/serve`). Confined to S5 — the
/// other scenarios drive only the `zit` LIBRARY, never the binary.
const zit_exe: []const u8 = build_options.zit_exe;

// ---------------------------------------------------------------------------
// RFC fixtures -- the same vectors src/artifact.zig's own tests cite.
// ---------------------------------------------------------------------------

const a1_1_manifest = rfc_vectors.artifact_a1_1_sha1_manifest;
const a1_2_technote = rfc_vectors.artifact_a1_2_sha3_technote;
const a3_manifest = rfc_vectors.artifact_a3_rcard_manifest;

/// §5/Appendix B.1 delta worked-example pair (102/151-byte sizes), reused
/// verbatim from delta.zig's own "create() then apply() round-trips the
/// target" test so S4 exercises the same RFC-cited content.
const delta_source = "The quick brown fox jumps over the lazy dog. A Fossil delta encodes byte-level differences compactly.\n";
const delta_target = "The quick brown fox jumps over the sleeping fox. A Fossil delta encodes byte-level differences compactly.\n An example delta target with appended tail.\n";

const ManifestScenario = struct {
    name: []const u8,
    citation: []const u8,
    fixture: []const u8,
    algo: artifact.HashAlgo,
};

const manifest_scenarios = [_]ManifestScenario{
    .{ .name = "S1", .citation = "§A.1.1/§A.2", .fixture = a1_1_manifest, .algo = .sha1 },
    .{ .name = "S2", .citation = "§A.1.2", .fixture = a1_2_technote, .algo = .sha3_256 },
    .{ .name = "S3", .citation = "§A.3", .fixture = a3_manifest, .algo = .sha3_256 },
};

pub fn main(init: std.process.Init) !void {
    const gpa = init.gpa;
    const io = init.io;

    var stdout_buffer: [4096]u8 = undefined;
    var stdout_writer: std.Io.File.Writer = .init(.stdout(), io, &stdout_buffer);
    const out = &stdout_writer.interface;

    // Record the exact fossil the scenarios confirm against, verbatim, at the
    // head of the smoke log: the version line and the PASS/FAIL results then sit
    // in one block the release notes cite (readiness item S7). A null return is
    // the milestone-manual skip — fossil absent, nothing to confirm against.
    if (try fossilVersion(gpa, io)) |ver| {
        defer gpa.free(ver);
        try out.print("fossil version: {s}\n", .{std.mem.trim(u8, ver, " \t\r\n")});
    } else {
        try out.writeAll("fossil not found — smoke is milestone-manual\n");
        try out.flush();
        return;
    }

    var arena_state = std.heap.ArenaAllocator.init(gpa);
    defer arena_state.deinit();
    const arena = arena_state.allocator();

    const tmp_root = try tempRoot(arena, init.environ_map.*);
    std.Io.Dir.cwd().deleteTree(io, tmp_root) catch {};
    try std.Io.Dir.cwd().createDirPath(io, tmp_root);
    defer std.Io.Dir.cwd().deleteTree(io, tmp_root) catch {};

    var env = try init.environ_map.clone(gpa);
    defer env.deinit();
    const home_dir = try std.fs.path.join(arena, &.{ tmp_root, "home" });
    try std.Io.Dir.cwd().createDirPath(io, home_dir);
    try env.put("FOSSIL_HOME", home_dir);

    var all_pass = true;

    for (manifest_scenarios) |sc| {
        const pass = runManifestScenario(arena, gpa, io, tmp_root, &env, out, sc) catch |err| blk: {
            try out.print("FAIL {s} ({s}): unexpected error running scenario: {s}\n", .{ sc.name, sc.citation, @errorName(err) });
            break :blk false;
        };
        all_pass = all_pass and pass;
    }

    const s4_pass = runDeltaScenario(arena, gpa, io, tmp_root, &env, out) catch |err| blk: {
        try out.print("FAIL S4 (§5/§Appendix B.1): unexpected error running scenario: {s}\n", .{@errorName(err)});
        break :blk false;
    };
    all_pass = all_pass and s4_pass;

    const s5_pass = runCloneFromRepackedScenario(arena, gpa, io, tmp_root, &env, out) catch |err| blk: {
        try out.print("FAIL S5 (§7.5/§3): unexpected error running scenario: {s}\n", .{@errorName(err)});
        break :blk false;
    };
    all_pass = all_pass and s5_pass;

    const s6_pass = runOpenFromFossilServerScenario(arena, gpa, io, tmp_root, &env, out) catch |err| blk: {
        try out.print("FAIL S6 (§3.7/§4): unexpected error running scenario: {s}\n", .{@errorName(err)});
        break :blk false;
    };
    all_pass = all_pass and s6_pass;

    const s7_pass = runPushToFossilServerScenario(arena, gpa, io, tmp_root, &env, out) catch |err| blk: {
        try out.print("FAIL S7 (§3.3/§3.7): unexpected error running scenario: {s}\n", .{@errorName(err)});
        break :blk false;
    };
    all_pass = all_pass and s7_pass;

    const s8_pass = runPullFossilExtensionScenario(arena, gpa, io, tmp_root, &env, out) catch |err| blk: {
        try out.print("FAIL S8 (§3.2/§3.7): unexpected error running scenario: {s}\n", .{@errorName(err)});
        break :blk false;
    };
    all_pass = all_pass and s8_pass;

    const s9_pass = runCloneOfShunnedScenario(arena, gpa, io, tmp_root, &env, out) catch |err| blk: {
        try out.print("FAIL S9 (§9.5/§8.2): unexpected error running scenario: {s}\n", .{@errorName(err)});
        break :blk false;
    };
    all_pass = all_pass and s9_pass;

    const s10_pass = runPushOfShunnedScenario(arena, gpa, io, tmp_root, &env, out) catch |err| blk: {
        try out.print("FAIL S10 (§7.7): unexpected error running scenario: {s}\n", .{@errorName(err)});
        break :blk false;
    };
    all_pass = all_pass and s10_pass;

    const s11_pass = runPushPolicyRefusalScenario(arena, gpa, io, tmp_root, &env, out) catch |err| blk: {
        try out.print("FAIL S11 (#88): unexpected error running scenario: {s}\n", .{@errorName(err)});
        break :blk false;
    };
    all_pass = all_pass and s11_pass;

    const s12_pass = runUserConfigPullScenario(arena, gpa, io, tmp_root, &env, out) catch |err| blk: {
        try out.print("FAIL S12 (zit#125): unexpected error running scenario: {s}\n", .{@errorName(err)});
        break :blk false;
    };
    all_pass = all_pass and s12_pass;

    const s13_pass = runEncodingWarningScenario(arena, gpa, io, tmp_root, &env, out) catch |err| blk: {
        try out.print("FAIL S13 (zit#174 item 6): unexpected error running scenario: {s}\n", .{@errorName(err)});
        break :blk false;
    };
    all_pass = all_pass and s13_pass;

    const s14_pass = runSettingsFixtureScenario(arena, gpa, io, tmp_root, &env, out) catch |err| blk: {
        try out.print("FAIL S14 (zit#174 item 12): unexpected error running scenario: {s}\n", .{@errorName(err)});
        break :blk false;
    };
    all_pass = all_pass and s14_pass;

    const s15_pass = runAttachmentInteropScenario(arena, gpa, io, tmp_root, &env, out) catch |err| blk: {
        try out.print("FAIL S15 (zit#186): unexpected error running scenario: {s}\n", .{@errorName(err)});
        break :blk false;
    };
    all_pass = all_pass and s15_pass;

    try out.flush();
    if (!all_pass) std.process.exit(1);
}

/// S12 (zit#125) — the user-table configuration area, against a REAL fossil
/// server: `zit config pull user` adopts the peer's logins and capabilities,
/// and an ANONYMOUS attempt gets nothing and changes nothing.
///
/// This is the one lane where the admin gate can be proved rather than
/// assumed. Fossil strips `CONFIGSET_USER` from a non-administrator's group
/// mask (`configure.c`, `xfer.c`), so the boundary is fossil's, not zit's —
/// and a unit test against zit's own server could not observe it at all,
/// because zit's server has no capability model and therefore never serves
/// this area. The server is spawned WITHOUT `--localauth`, which would
/// auto-authenticate a loopback client as the default administrator and
/// quietly turn the anonymous half of this test into a second credentialed
/// one.
fn runUserConfigPullScenario(
    arena: std.mem.Allocator,
    gpa: std.mem.Allocator,
    io: std.Io,
    tmp_root: []const u8,
    env: *const std.process.Environ.Map,
    out: *std.Io.Writer,
) !bool {
    const citation = "zit#125";
    const abs_exe = try zitAbsPath(arena, io);
    const base = try std.fs.path.join(arena, &.{ tmp_root, "S12" });
    try std.Io.Dir.cwd().createDirPath(io, base);
    const peer_repo = try std.fs.path.join(arena, &.{ base, "peer.fossil" });
    const mirror = try std.fs.path.join(arena, &.{ base, "mirror" });

    {
        const res = try runFossil(gpa, io, &.{ "fossil", "init", peer_repo }, base, env);
        defer gpa.free(res.stdout);
        defer gpa.free(res.stderr);
        if (!okExit(res.term)) {
            try out.print("FAIL S12 ({s}): fossil init failed\n  stderr: {s}\n", .{ citation, res.stderr });
            return false;
        }
    }
    // A named administrator whose capabilities this pull must reproduce.
    {
        const res = try runFossil(gpa, io, &.{ "fossil", "user", "new", "smokeadmin", "Smoke Admin", "sekret", "-R", peer_repo }, base, env);
        defer gpa.free(res.stdout);
        defer gpa.free(res.stderr);
        if (!okExit(res.term)) {
            try out.print("FAIL S12 ({s}): fossil user new failed\n  stderr: {s}\n", .{ citation, res.stderr });
            return false;
        }
    }
    {
        const res = try runFossil(gpa, io, &.{ "fossil", "user", "capabilities", "smokeadmin", "s", "-R", peer_repo }, base, env);
        defer gpa.free(res.stdout);
        defer gpa.free(res.stderr);
        if (!okExit(res.term)) {
            try out.print("FAIL S12 ({s}): fossil user capabilities failed\n  stderr: {s}\n", .{ citation, res.stderr });
            return false;
        }
    }

    const port = try freshLoopbackPort(io);
    const port_str = try std.fmt.allocPrint(arena, "{d}", .{port});
    var server = spawnFossilServe(gpa, io, out, peer_repo, base, env, port_str) catch |err| {
        try out.print("FAIL S12 ({s}): could not spawn fossil server: {s}\n", .{ citation, @errorName(err) });
        return false;
    };
    defer stopFossilServe(io, &server);
    waitForListen(io, port) catch {
        try out.print("FAIL S12 ({s}): fossil server never bound 127.0.0.1:{d}\n", .{ citation, port });
        return false;
    };
    const url = try std.fmt.allocPrint(arena, "http://127.0.0.1:{d}/", .{port});

    // A mirror of the peer: the project code must match or fossil refuses the
    // session outright (§9.1), so the clone is part of the fixture.
    {
        const res = try std.process.run(gpa, io, .{ .argv = &.{ abs_exe, "clone", url, mirror }, .cwd = .{ .path = base }, .environ_map = env });
        defer gpa.free(res.stdout);
        defer gpa.free(res.stderr);
        if (!okExit(res.term)) {
            try out.print("FAIL S12 ({s}): zit clone of the fossil peer failed\n  stderr: {s}\n", .{ citation, res.stderr });
            return false;
        }
    }

    // ANONYMOUS: fossil serves no user rows, zit says so, and nothing lands.
    {
        const res = try std.process.run(gpa, io, .{ .argv = &.{ abs_exe, "config", "pull", "user" }, .cwd = .{ .path = mirror }, .environ_map = env });
        defer gpa.free(res.stdout);
        defer gpa.free(res.stderr);
        if (okExit(res.term)) {
            try out.print("FAIL S12 ({s}): an anonymous user pull SUCCEEDED; fossil's admin gate is not being observed\n", .{citation});
            try printMismatchFooter(out, citation);
            return false;
        }
        const listed = try std.process.run(gpa, io, .{ .argv = &.{ abs_exe, "users" }, .cwd = .{ .path = mirror }, .environ_map = env });
        defer gpa.free(listed.stdout);
        defer gpa.free(listed.stderr);
        if (listed.stdout.len != 0) {
            try out.print("FAIL S12 ({s}): a refused pull still wrote rows: {s}\n", .{ citation, listed.stdout });
            return false;
        }
    }

    // CREDENTIALED: the rows arrive, with fossil's capability string verbatim.
    {
        const res = try std.process.run(gpa, io, .{
            .argv = &.{ abs_exe, "config", "pull", "user", "--user=smokeadmin", "--password=sekret" },
            .cwd = .{ .path = mirror },
            .environ_map = env,
        });
        defer gpa.free(res.stdout);
        defer gpa.free(res.stderr);
        if (!okExit(res.term)) {
            try out.print("FAIL S12 ({s}): a credentialed user pull failed\n  stdout: {s}\n  stderr: {s}\n", .{ citation, res.stdout, res.stderr });
            try printMismatchFooter(out, citation);
            return false;
        }
    }
    {
        const listed = try std.process.run(gpa, io, .{ .argv = &.{ abs_exe, "users" }, .cwd = .{ .path = mirror }, .environ_map = env });
        defer gpa.free(listed.stdout);
        defer gpa.free(listed.stderr);
        if (std.mem.indexOf(u8, listed.stdout, "smokeadmin\ts\t") == null) {
            try out.print("FAIL S12 ({s}): the pulled table does not carry smokeadmin with fossil's caps\n  got: {s}\n", .{ citation, listed.stdout });
            try printMismatchFooter(out, citation);
            return false;
        }
        // The hash is withheld from the default listing even though the row
        // carries one — the same rule the unit lane pins, proved here over a
        // password fossil itself hashed.
        if (std.mem.count(u8, listed.stdout, "\t") > 3 * std.mem.count(u8, listed.stdout, "\n")) {
            try out.print("FAIL S12 ({s}): the default listing has too many columns; is it printing the hash?\n  got: {s}\n", .{ citation, listed.stdout });
            return false;
        }
    }

    // An authenticated push lands on the peer, and a saved login then
    // authenticates a push that names no user. fossil's anonymous user cannot
    // write, so a check-in reaching the peer's timeline is proof the login card
    // went out and was accepted. This is the end-to-end guard for the omitted-
    // URL credential drop and for the stored-login reuse: `config pull` stores
    // nothing, so the first authenticated push is what stores the secret.
    {
        const a_path = try std.fs.path.join(arena, &.{ mirror, "a191.txt" });
        try std.Io.Dir.cwd().writeFile(io, .{ .sub_path = a_path, .data = "first\n" });
        inline for (.{
            .{ &[_][]const u8{ "add", "a191.txt" }, "zit add" },
            .{ &[_][]const u8{ "commit", "-m", "auth-push", "--user", "u", "--no-sync" }, "zit commit" },
            .{ &[_][]const u8{ "push", "--user", "smokeadmin", "--password", "sekret" }, "zit push --user (no URL, uses saved remote)" },
        }) |step| {
            const r = (try zitStep(gpa, io, out, abs_exe, mirror, env, step[0], step[1])) orelse return false;
            gpa.free(r.stdout);
            gpa.free(r.stderr);
        }
        {
            const tl = try runFossil(gpa, io, &.{ "fossil", "timeline", "-n", "5", "-t", "ci", "-R", peer_repo }, base, env);
            defer gpa.free(tl.stdout);
            defer gpa.free(tl.stderr);
            if (std.mem.indexOf(u8, tl.stdout, "auth-push") == null) {
                try out.print("FAIL S12 ({s}): an authenticated push with no URL did not reach the fossil peer\n  timeline: {s}\n", .{ citation, tl.stdout });
                try printMismatchFooter(out, citation);
                return false;
            }
        }
        // The stored login now authenticates a push that names no user.
        try std.Io.Dir.cwd().writeFile(io, .{ .sub_path = a_path, .data = "second\n" });
        inline for (.{
            .{ &[_][]const u8{ "commit", "-m", "stored-login", "--user", "u", "--no-sync" }, "zit commit" },
            .{ &[_][]const u8{"push"}, "zit push (no --user, stored login)" },
        }) |step| {
            const r = (try zitStep(gpa, io, out, abs_exe, mirror, env, step[0], step[1])) orelse return false;
            gpa.free(r.stdout);
            gpa.free(r.stderr);
        }
        const tl = try runFossil(gpa, io, &.{ "fossil", "timeline", "-n", "5", "-t", "ci", "-R", peer_repo }, base, env);
        defer gpa.free(tl.stdout);
        defer gpa.free(tl.stderr);
        if (std.mem.indexOf(u8, tl.stdout, "stored-login") == null) {
            try out.print("FAIL S12 ({s}): a push with no user, relying on the stored login, did not reach the fossil peer\n  timeline: {s}\n", .{ citation, tl.stdout });
            try printMismatchFooter(out, citation);
            return false;
        }
    }

    try out.print("PASS S12 ({s}): a credentialed 'config pull user' reproduces a fossil peer's logins and capabilities; an anonymous one is refused and writes nothing; an authenticated push and a stored-login push both land on the peer\n", .{citation});
    return true;
}

// ---------------------------------------------------------------------------
// Preflight
// ---------------------------------------------------------------------------

/// The runner's `fossil version` output, captured verbatim, or null when fossil
/// is absent or errors. Doubles as the availability gate (null ⇒ skip) and the
/// version record printed at the head of the smoke log. Caller frees a non-null
/// return.
/// S15 (zit#186) — an attachment zit authored is an attachment fossil reads.
/// Pushed to a REAL fossil server, the A artifact crosslinks into fossil's
/// `attachment` table under the target and name zit recorded, with zit's blob
/// as its `src`, and the blob itself is in fossil's store. This is the interop
/// check the issue asks for: the format is written in one place and read by
/// the other implementation.
fn runAttachmentInteropScenario(
    arena: std.mem.Allocator,
    gpa: std.mem.Allocator,
    io: std.Io,
    tmp_root: []const u8,
    env: *const std.process.Environ.Map,
    out: *std.Io.Writer,
) !bool {
    const citation = "zit#186";
    const abs_exe = try zitAbsPath(arena, io);
    const base = try std.fs.path.join(arena, &.{ tmp_root, "S15" });
    try std.Io.Dir.cwd().createDirPath(io, base);
    const peer_repo = try std.fs.path.join(arena, &.{ base, "peer.fossil" });
    const mirror = try std.fs.path.join(arena, &.{ base, "mirror" });

    {
        const res = try runFossil(gpa, io, &.{ "fossil", "init", peer_repo }, base, env);
        defer gpa.free(res.stdout);
        defer gpa.free(res.stderr);
        if (!okExit(res.term)) {
            try out.print("FAIL S15 ({s}): fossil init failed\n  stderr: {s}\n", .{ citation, res.stderr });
            return false;
        }
    }
    // An anonymous push needs check-in capability on the `nobody` login.
    {
        const res = try runFossil(gpa, io, &.{ "fossil", "user", "capabilities", "nobody", "gjorzi", "-R", peer_repo }, base, env);
        defer gpa.free(res.stdout);
        defer gpa.free(res.stderr);
        if (!okExit(res.term)) {
            try out.print("FAIL S15 ({s}): fossil user capabilities failed\n  stderr: {s}\n", .{ citation, res.stderr });
            return false;
        }
    }
    const port = try freshLoopbackPort(io);
    const port_str = try std.fmt.allocPrint(arena, "{d}", .{port});
    var server = spawnFossilServe(gpa, io, out, peer_repo, base, env, port_str) catch |err| {
        try out.print("FAIL S15 ({s}): could not spawn fossil server: {s}\n", .{ citation, @errorName(err) });
        return false;
    };
    defer stopFossilServe(io, &server);
    waitForListen(io, port) catch {
        try out.print("FAIL S15 ({s}): fossil server never bound 127.0.0.1:{d}\n", .{ citation, port });
        return false;
    };
    const url = try std.fmt.allocPrint(arena, "http://127.0.0.1:{d}/", .{port});

    {
        const res = try std.process.run(gpa, io, .{ .argv = &.{ abs_exe, "clone", url, mirror }, .cwd = .{ .path = base }, .environ_map = env });
        defer gpa.free(res.stdout);
        defer gpa.free(res.stderr);
        if (!okExit(res.term)) {
            try out.print("FAIL S15 ({s}): zit clone of the fossil peer failed\n  stderr: {s}\n", .{ citation, res.stderr });
            return false;
        }
    }
    try std.Io.Dir.cwd().writeFile(io, .{
        .sub_path = try std.fs.path.join(arena, &.{ mirror, "note.txt" }),
        .data = "an attachment authored by zit\n",
    });
    var blob_hash: []const u8 = "";
    {
        const res = try std.process.run(gpa, io, .{ .argv = &.{ abs_exe, "attach", "add", "Home", "note.txt", "--user", "smoke", "--comment", "interop" }, .cwd = .{ .path = mirror }, .environ_map = env });
        defer gpa.free(res.stdout);
        defer gpa.free(res.stderr);
        if (!okExit(res.term)) {
            try out.print("FAIL S15 ({s}): zit attach add failed\n  stderr: {s}\n", .{ citation, res.stderr });
            return false;
        }
        var it = std.mem.splitScalar(u8, std.mem.trim(u8, res.stdout, "\n"), '\n');
        _ = it.next() orelse "";
        blob_hash = try arena.dupe(u8, it.next() orelse "");
        if (blob_hash.len != 64) {
            try out.print("FAIL S15 ({s}): attach add did not print the blob hash on its second line\n  stdout: {s}\n", .{ citation, res.stdout });
            return false;
        }
    }
    {
        const res = try std.process.run(gpa, io, .{ .argv = &.{ abs_exe, "push", url }, .cwd = .{ .path = mirror }, .environ_map = env });
        defer gpa.free(res.stdout);
        defer gpa.free(res.stderr);
        if (!okExit(res.term)) {
            try out.print("FAIL S15 ({s}): zit push to the fossil server failed\n  stderr: {s}\n", .{ citation, res.stderr });
            return false;
        }
    }
    stopFossilServe(io, &server);

    // Fossil crosslinked it: one live attachment row for (Home, note.txt)
    // whose src is the blob zit stored, and the blob is in fossil's store.
    const sql = try std.fmt.allocPrint(arena, "select count(*) from attachment where target='Home' and filename='note.txt' and src='{s}' and isLatest;", .{blob_hash});
    {
        const res = try runFossil(gpa, io, &.{ "fossil", "sqlite3", "--readonly", sql, "-R", peer_repo }, base, env);
        defer gpa.free(res.stdout);
        defer gpa.free(res.stderr);
        const got = std.mem.trim(u8, res.stdout, " \t\r\n");
        if (!okExit(res.term) or !std.mem.eql(u8, got, "1")) {
            try out.print("FAIL S15 ({s}): fossil holds no live attachment row for (Home, note.txt) with zit's blob as src\n  count: {s}\n  stderr: {s}\n", .{ citation, got, res.stderr });
            return false;
        }
    }
    const sql_blob = try std.fmt.allocPrint(arena, "select count(*) from blob where uuid='{s}';", .{blob_hash});
    {
        const res = try runFossil(gpa, io, &.{ "fossil", "sqlite3", "--readonly", sql_blob, "-R", peer_repo }, base, env);
        defer gpa.free(res.stdout);
        defer gpa.free(res.stderr);
        const got = std.mem.trim(u8, res.stdout, " \t\r\n");
        if (!okExit(res.term) or !std.mem.eql(u8, got, "1")) {
            try out.print("FAIL S15 ({s}): fossil does not hold the attachment blob {s}\n  stderr: {s}\n", .{ citation, blob_hash, res.stderr });
            return false;
        }
    }
    try out.print("PASS S15 ({s}): a zit-authored attachment pushed to a real fossil server crosslinks as a live attachment on its target, with zit's blob as its source\n", .{citation});
    return true;
}

fn fossilVersion(gpa: std.mem.Allocator, io: std.Io) !?[]u8 {
    const res = std.process.run(gpa, io, .{ .argv = &.{ "fossil", "version" } }) catch return null;
    defer gpa.free(res.stderr);
    if (!okExit(res.term)) {
        gpa.free(res.stdout);
        return null;
    }
    return res.stdout;
}

fn tempRoot(arena: std.mem.Allocator, env: std.process.Environ.Map) ![]const u8 {
    const base = env.get("TMPDIR") orelse "/tmp";
    const trimmed = std.mem.trimEnd(u8, base, "/");
    return std.fs.path.join(arena, &.{ trimmed, "zit-smoke" });
}

// ---------------------------------------------------------------------------
// fossil process helper
// ---------------------------------------------------------------------------

fn okExit(term: std.process.Child.Term) bool {
    return switch (term) {
        .exited => |code| code == 0,
        else => false,
    };
}

fn runFossil(
    gpa: std.mem.Allocator,
    io: std.Io,
    argv: []const []const u8,
    cwd_path: []const u8,
    env: *const std.process.Environ.Map,
) !std.process.RunResult {
    return std.process.run(gpa, io, .{
        .argv = argv,
        .cwd = .{ .path = cwd_path },
        .environ_map = env,
    });
}

/// First whitespace-delimited token of `text` (the hash column of
/// `fossil sha1sum`/`sha3sum` output: "<hash>  <path>\n").
fn firstToken(text: []const u8) []const u8 {
    const end = std.mem.indexOfAny(u8, text, " \t\r\n") orelse text.len;
    return text[0..end];
}

// ---------------------------------------------------------------------------
// Failure reporting -- never authored from fossil's side, only printed.
// ---------------------------------------------------------------------------

fn printBytesPreview(out: *std.Io.Writer, label: []const u8, bytes: []const u8) !void {
    try out.print("  {s} (len {d}): ", .{ label, bytes.len });
    const max = @min(bytes.len, 96);
    for (bytes[0..max]) |b| try out.print("{x:0>2}", .{b});
    if (bytes.len > max) try out.writeAll("...");
    try out.writeAll("\n");
}

fn firstDiffOffset(a: []const u8, b: []const u8) ?usize {
    const n = @min(a.len, b.len);
    var i: usize = 0;
    while (i < n) : (i += 1) {
        if (a[i] != b[i]) return i;
    }
    if (a.len != b.len) return n;
    return null;
}

fn printDiffWindow(out: *std.Io.Writer, zit_bytes: []const u8, fossil_bytes: []const u8) !void {
    const off = firstDiffOffset(zit_bytes, fossil_bytes) orelse return;
    const start = if (off > 16) off - 16 else 0;
    const zit_end = @min(zit_bytes.len, off + 16);
    const fossil_end = @min(fossil_bytes.len, off + 16);
    try out.print("  first diff at byte {d}\n", .{off});
    try out.writeAll("  zit    : ");
    for (zit_bytes[start..zit_end]) |b| try out.print("{x:0>2}", .{b});
    try out.writeAll("\n  fossil : ");
    for (fossil_bytes[start..fossil_end]) |b| try out.print("{x:0>2}", .{b});
    try out.writeAll("\n");
}

fn printMismatchFooter(out: *std.Io.Writer, citation: []const u8) !void {
    try out.print("  RFC citation: {s}\n", .{citation});
    try out.writeAll(
        \\  Fossil disagrees with zit's RFC-cited expectation. Operator decides
        \\  RFC-misread vs RFC-wrong; record in ambiguities.md. Never change zit
        \\  to match fossil.
        \\
    );
}

// ---------------------------------------------------------------------------
// S1/S2/S3 -- manifest scenarios (parse -> create -> confirm via fossil)
// ---------------------------------------------------------------------------

fn runManifestScenario(
    arena: std.mem.Allocator,
    gpa: std.mem.Allocator,
    io: std.Io,
    tmp_root: []const u8,
    env: *const std.process.Environ.Map,
    out: *std.Io.Writer,
    sc: ManifestScenario,
) !bool {
    // zit's own product: the artifact fed to fossil is `create()`'s output,
    // never the raw RFC transcription (§6.2 round-trip; the two happen to be
    // byte-identical for S1/S3 and a strict-lexical reorder for S2, but the
    // input to fossil is always what zit itself wrote).
    var m = try artifact.parse(gpa, sc.fixture);
    defer m.deinit();
    const zit_bytes = try artifact.create(&m, gpa);
    defer gpa.free(zit_bytes);

    var name_buf: [64]u8 = undefined;
    const zit_name = artifact.artifactName(sc.algo, zit_bytes, &name_buf);

    const base = try std.fs.path.join(arena, &.{ tmp_root, sc.name });
    const repo_dir = try std.fs.path.join(arena, &.{ base, "repo" });
    const checkout_dir = try std.fs.path.join(arena, &.{ base, "checkout" });
    try std.Io.Dir.cwd().createDirPath(io, repo_dir);
    try std.Io.Dir.cwd().createDirPath(io, checkout_dir);

    const manifest_path = try std.fs.path.join(arena, &.{ repo_dir, "manifest.txt" });
    try std.Io.Dir.cwd().writeFile(io, .{ .sub_path = manifest_path, .data = zit_bytes });

    // fossil test-parse-manifest: repo-free syntactic accept (§4/§5). Exit is
    // 0 either way -- the VERDICT TEXT is what confirms zit's parse.
    {
        const res = try runFossil(gpa, io, &.{ "fossil", "test-parse-manifest", manifest_path }, repo_dir, env);
        defer gpa.free(res.stdout);
        defer gpa.free(res.stderr);
        if (std.mem.indexOf(u8, res.stdout, "reports the input is ok") == null) {
            try out.print("FAIL {s} ({s}): fossil test-parse-manifest did not report the input well-formed\n", .{ sc.name, sc.citation });
            try printBytesPreview(out, "zit bytes", zit_bytes);
            try out.print("  fossil verdict: {s}\n", .{res.stdout});
            try printMismatchFooter(out, sc.citation);
            return false;
        }
    }

    // fossil {sha1,sha3}sum: confirms zit's own artifactName computation.
    {
        const sum_cmd = if (sc.algo == .sha1) "sha1sum" else "sha3sum";
        const res = try runFossil(gpa, io, &.{ "fossil", sum_cmd, manifest_path }, repo_dir, env);
        defer gpa.free(res.stdout);
        defer gpa.free(res.stderr);
        const fossil_name = firstToken(res.stdout);
        if (!std.mem.eql(u8, fossil_name, zit_name)) {
            try out.print("FAIL {s} ({s}): fossil {s} disagrees with zit's artifactName\n", .{ sc.name, sc.citation, sum_cmd });
            try out.print("  zit name:    {s}\n", .{zit_name});
            try out.print("  fossil name: {s}\n", .{fossil_name});
            try printMismatchFooter(out, sc.citation);
            return false;
        }
    }

    // init + open + put + retrieve: confirms zit's create() output
    // round-trips through fossil's own content store under the given name.
    const repo_fossil = try std.fs.path.join(arena, &.{ repo_dir, "r.fossil" });
    const init_argv: []const []const u8 = if (sc.algo == .sha1)
        &.{ "fossil", "init", "--sha1", repo_fossil }
    else
        &.{ "fossil", "init", repo_fossil };
    {
        const res = try runFossil(gpa, io, init_argv, repo_dir, env);
        defer gpa.free(res.stdout);
        defer gpa.free(res.stderr);
        if (!okExit(res.term)) {
            try out.print("FAIL {s} ({s}): fossil init failed\n  stderr: {s}\n", .{ sc.name, sc.citation, res.stderr });
            try printMismatchFooter(out, sc.citation);
            return false;
        }
    }
    {
        const res = try runFossil(gpa, io, &.{ "fossil", "open", repo_fossil }, checkout_dir, env);
        defer gpa.free(res.stdout);
        defer gpa.free(res.stderr);
        if (!okExit(res.term)) {
            try out.print("FAIL {s} ({s}): fossil open failed\n  stderr: {s}\n", .{ sc.name, sc.citation, res.stderr });
            try printMismatchFooter(out, sc.citation);
            return false;
        }
    }
    {
        const res = try runFossil(gpa, io, &.{ "fossil", "test-content-put", manifest_path }, checkout_dir, env);
        defer gpa.free(res.stdout);
        defer gpa.free(res.stderr);
        if (!okExit(res.term)) {
            try out.print("FAIL {s} ({s}): fossil test-content-put failed\n  stderr: {s}\n", .{ sc.name, sc.citation, res.stderr });
            try printMismatchFooter(out, sc.citation);
            return false;
        }
    }

    const artifact_out_path = try std.fs.path.join(arena, &.{ checkout_dir, "out.bin" });
    {
        const res = try runFossil(gpa, io, &.{ "fossil", "artifact", zit_name, "out.bin" }, checkout_dir, env);
        defer gpa.free(res.stdout);
        defer gpa.free(res.stderr);
        if (!okExit(res.term)) {
            try out.print("FAIL {s} ({s}): fossil artifact could not resolve zit's name {s}\n  stderr: {s}\n", .{ sc.name, sc.citation, zit_name, res.stderr });
            try printMismatchFooter(out, sc.citation);
            return false;
        }
    }

    const fossil_bytes = try std.Io.Dir.cwd().readFileAlloc(io, artifact_out_path, gpa, .limited(1 << 20));
    defer gpa.free(fossil_bytes);
    if (!std.mem.eql(u8, zit_bytes, fossil_bytes)) {
        try out.print("FAIL {s} ({s}): fossil artifact returned bytes different from zit's create() output\n", .{ sc.name, sc.citation });
        try printBytesPreview(out, "zit bytes", zit_bytes);
        try printBytesPreview(out, "fossil bytes", fossil_bytes);
        try printDiffWindow(out, zit_bytes, fossil_bytes);
        try printMismatchFooter(out, sc.citation);
        return false;
    }

    try out.print("PASS {s} ({s}): zit's create() output confirmed by fossil (name {s})\n", .{ sc.name, sc.citation, zit_name });
    return true;
}

// ---------------------------------------------------------------------------
// S4 -- delta round-trip (never compared against fossil test-delta-create;
// fossil only applies zit's own delta and confirms the reconstructed target)
// ---------------------------------------------------------------------------

fn runDeltaScenario(
    arena: std.mem.Allocator,
    gpa: std.mem.Allocator,
    io: std.Io,
    tmp_root: []const u8,
    env: *const std.process.Environ.Map,
    out: *std.Io.Writer,
) !bool {
    const citation = "§5/§Appendix B.1";

    const zit_delta = try delta.create(gpa, delta_source, delta_target);
    defer gpa.free(zit_delta);

    // zit's own apply() must already reconstruct the target -- fossil only
    // ever confirms this, never establishes it.
    const self_applied = try delta.apply(gpa, delta_source, zit_delta);
    defer gpa.free(self_applied);
    std.debug.assert(std.mem.eql(u8, self_applied, delta_target));

    const base = try std.fs.path.join(arena, &.{ tmp_root, "S4" });
    try std.Io.Dir.cwd().createDirPath(io, base);
    const src_path = try std.fs.path.join(arena, &.{ base, "src.bin" });
    const delta_path = try std.fs.path.join(arena, &.{ base, "zit.delta" });
    const out_path = try std.fs.path.join(arena, &.{ base, "out.bin" });

    try std.Io.Dir.cwd().writeFile(io, .{ .sub_path = src_path, .data = delta_source });
    try std.Io.Dir.cwd().writeFile(io, .{ .sub_path = delta_path, .data = zit_delta });

    {
        const res = try runFossil(gpa, io, &.{ "fossil", "test-delta-apply", src_path, delta_path, out_path }, base, env);
        defer gpa.free(res.stdout);
        defer gpa.free(res.stderr);
        if (!okExit(res.term)) {
            try out.print("FAIL S4 ({s}): fossil test-delta-apply rejected zit's delta.create() output\n  stderr: {s}\n", .{ citation, res.stderr });
            try printBytesPreview(out, "zit delta", zit_delta);
            try printMismatchFooter(out, citation);
            return false;
        }
    }

    const fossil_out = try std.Io.Dir.cwd().readFileAlloc(io, out_path, gpa, .limited(1 << 20));
    defer gpa.free(fossil_out);
    if (!std.mem.eql(u8, fossil_out, delta_target)) {
        try out.print("FAIL S4 ({s}): fossil test-delta-apply produced bytes different from zit's target\n", .{citation});
        try printBytesPreview(out, "zit target", delta_target);
        try printBytesPreview(out, "fossil bytes", fossil_out);
        try printDiffWindow(out, delta_target, fossil_out);
        try printMismatchFooter(out, citation);
        return false;
    }

    try out.print("PASS S4 ({s}): fossil test-delta-apply reconstructs zit's delta.create() target\n", .{citation});
    return true;
}

// ---------------------------------------------------------------------------
// S5 -- clone from a re-packed store (§7.5, covering the §3
// serve-regenerated-delta note). The one scenario that drives the real `zit`
// BINARY: build a repo, evolve a file so its blob is stored as a delta, run
// `zit pack` so that delta is REGENERATED against our size-minimizing base, then
// serve the repacked store and have the real `fossil` client CLONE it. fossil
// hash-verifies every artifact it reconstructs on receipt, so a clone that exits
// 0 proves the regenerated delta container zit serves reconstructs byte-for-byte
// over the wire; the checked-out file is compared to zit's own committed bytes as
// the final oracle. Every byte fossil sees is zit's own output — fossil only
// confirms, never authors (the file policy at the top of this file).
// ---------------------------------------------------------------------------

/// Allocates a fresh loopback port for a child server. The reservation listener
/// closes before the child binds it; that close→child-bind race is unavoidable
/// because child servers cannot inherit the listener.
fn freshLoopbackPort(io: std.Io) !u16 {
    const addr = try net.IpAddress.parseIp4("127.0.0.1", 0);
    var server = try addr.listen(io, .{});
    const port = server.socket.address.getPort();
    server.deinit(io);
    if (port == 0) return error.NoLoopbackPort;
    return port;
}

/// Build the S5 file blob: 200 similar lines so `zit pack`'s best-of-N base
/// selection produces a profitable delta. `changed` mutates one line and appends
/// a tail, so the second commit's blob deltifies well against the first.
fn buildBlob(arena: std.mem.Allocator, changed: bool) ![]u8 {
    var aw = std.Io.Writer.Allocating.init(arena); // arena-owned; no deinit
    var i: usize = 0;
    while (i < 200) : (i += 1) {
        if (changed and i == 50) {
            try aw.writer.print("line {d}: CHANGED CONTENT for the regenerated-delta smoke\n", .{i});
        } else {
            try aw.writer.print("line {d}: the quick brown fox jumps over the lazy dog number {d}\n", .{ i, i });
        }
    }
    if (changed) try aw.writer.writeAll("line 200: appended tail exercises the delta insert op\n");
    return aw.toOwnedSlice();
}

/// Absolute path to the compiled `zit` binary. `zit_exe` is relative to the
/// build root (this process's cwd); a spawned child chdirs before exec, so it
/// must be resolved first. Allocated on `arena`.
fn zitAbsPath(arena: std.mem.Allocator, io: std.Io) ![]const u8 {
    const cwd_abs = try std.process.currentPathAlloc(io, arena);
    return std.fs.path.resolve(arena, &.{ cwd_abs, zit_exe });
}

/// Blocking `zit <tail...>` with cwd `cwd_path` and the shared smoke env
/// (FOSSIL_HOME set). Caller owns `result.stdout`/`result.stderr`.
fn runZit(
    gpa: std.mem.Allocator,
    io: std.Io,
    abs_exe: []const u8,
    cwd_path: []const u8,
    env: *const std.process.Environ.Map,
    tail: []const []const u8,
) !std.process.RunResult {
    var argv_buf: [8][]const u8 = undefined;
    argv_buf[0] = abs_exe;
    std.debug.assert(tail.len <= argv_buf.len - 1);
    for (tail, argv_buf[1..][0..tail.len]) |src, *dst| dst.* = src;
    return std.process.run(gpa, io, .{
        .argv = argv_buf[0 .. 1 + tail.len],
        .cwd = .{ .path = cwd_path },
        .environ_map = env,
    });
}

/// Spawn `zit serve --port <port>` detached, cwd `cwd_path` (so `discoverRoot`
/// finds `cwd_path/.zit`). stdout/stderr discarded — driven purely over the
/// socket. Caller `kill`s the returned child.
fn spawnZitServe(
    io: std.Io,
    abs_exe: []const u8,
    cwd_path: []const u8,
    env: *const std.process.Environ.Map,
    port_str: []const u8,
) !std.process.Child {
    return std.process.spawn(io, .{
        .argv = &.{ abs_exe, "serve", "--port", port_str },
        .cwd = .{ .path = cwd_path },
        .environ_map = env,
        .stdout = .ignore,
        .stderr = .ignore,
    });
}

/// Bounded wait for a server to bind: retry-connect to the loopback port
/// until accepted or a wall-clock deadline elapses (never a fixed sleep). A
/// successful TCP connect proves the listener is up.
fn waitForListen(io: std.Io, port: u16) !void {
    const addr = net.IpAddress.parseIp4("127.0.0.1", port) catch return error.BadTestAddress;
    const deadline_ns: i96 = 15_000_000_000; // 15 s cap — orders of magnitude of slack
    const start = std.Io.Clock.now(.awake, io).nanoseconds;
    while (true) {
        if (addr.connect(io, .{ .mode = .stream })) |stream| {
            stream.close(io);
            return;
        } else |_| {}
        if (std.Io.Clock.now(.awake, io).nanoseconds - start > deadline_ns) return error.ServerNeverBound;
    }
}

/// The count parsed from `zit pack`'s "<N> re-deltaed" clause, or null if the
/// clause is absent. Used to assert S5's fixture still forces a REGENERATED
/// delta (so the scenario keeps testing serve-of-a-regenerated-delta, not just a
/// full-form clone).
fn reDeltaCount(text: []const u8) ?usize {
    const idx = std.mem.indexOf(u8, text, " re-deltaed") orelse return null;
    var start = idx;
    while (start > 0 and std.ascii.isDigit(text[start - 1])) start -= 1;
    if (start == idx) return null;
    return std.fmt.parseInt(usize, text[start..idx], 10) catch null;
}

/// Run `zit <tail...>` and fail the scenario (printing stderr) unless it exits 0.
/// Returns the captured result on success (caller frees), or null on failure
/// (already freed + reported).
fn zitStep(
    gpa: std.mem.Allocator,
    io: std.Io,
    out: *std.Io.Writer,
    abs_exe: []const u8,
    cwd_path: []const u8,
    env: *const std.process.Environ.Map,
    tail: []const []const u8,
    label: []const u8,
) !?std.process.RunResult {
    return zitStepIn(gpa, io, out, abs_exe, cwd_path, env, tail, label, "S5", "§7.5/§3");
}

/// `zitStep` for a named scenario: the same run-or-report-and-abort shape, with
/// the scenario name and citation supplied rather than fixed to S5's.
fn zitStepIn(
    gpa: std.mem.Allocator,
    io: std.Io,
    out: *std.Io.Writer,
    abs_exe: []const u8,
    cwd_path: []const u8,
    env: *const std.process.Environ.Map,
    tail: []const []const u8,
    label: []const u8,
    scenario: []const u8,
    citation: []const u8,
) !?std.process.RunResult {
    const res = try runZit(gpa, io, abs_exe, cwd_path, env, tail);
    if (!okExit(res.term)) {
        try out.print("FAIL {s} ({s}): {s} failed\n  stderr: {s}\n", .{ scenario, citation, label, res.stderr });
        try printMismatchFooter(out, citation);
        gpa.free(res.stdout);
        gpa.free(res.stderr);
        return null;
    }
    return res;
}

fn runCloneFromRepackedScenario(
    arena: std.mem.Allocator,
    gpa: std.mem.Allocator,
    io: std.Io,
    tmp_root: []const u8,
    env: *const std.process.Environ.Map,
    out: *std.Io.Writer,
) !bool {
    const citation = "§7.5/§3";
    const abs_exe = try zitAbsPath(arena, io);

    const base = try std.fs.path.join(arena, &.{ tmp_root, "S5" });
    const src_dir = try std.fs.path.join(arena, &.{ base, "src" });
    try std.Io.Dir.cwd().createDirPath(io, src_dir);

    // A source repo with two check-ins of an evolving file: the second blob is
    // stored as a delta, giving `pack` something to re-base.
    {
        const r = (try zitStep(gpa, io, out, abs_exe, src_dir, env, &.{"init"}, "zit init")) orelse return false;
        gpa.free(r.stdout);
        gpa.free(r.stderr);
    }
    const blob_path = try std.fs.path.join(arena, &.{ src_dir, "big.txt" });
    const seed = try buildBlob(arena, false);
    try std.Io.Dir.cwd().writeFile(io, .{ .sub_path = blob_path, .data = seed });
    inline for (.{
        .{ &[_][]const u8{ "add", "big.txt" }, "zit add (seed)" },
        .{ &[_][]const u8{ "commit", "-m", "seed", "--user", "u" }, "zit commit (seed)" },
    }) |step| {
        const r = (try zitStep(gpa, io, out, abs_exe, src_dir, env, step[0], step[1])) orelse return false;
        gpa.free(r.stdout);
        gpa.free(r.stderr);
    }
    const edited = try buildBlob(arena, true);
    try std.Io.Dir.cwd().writeFile(io, .{ .sub_path = blob_path, .data = edited });
    inline for (.{
        .{ &[_][]const u8{ "add", "big.txt" }, "zit add (grow)" },
        .{ &[_][]const u8{ "commit", "-m", "grow", "--user", "u" }, "zit commit (grow)" },
    }) |step| {
        const r = (try zitStep(gpa, io, out, abs_exe, src_dir, env, step[0], step[1])) orelse return false;
        gpa.free(r.stdout);
        gpa.free(r.stderr);
    }

    // Re-pack: every artifact is re-deltified against our chosen base, so a
    // served delta container is REGENERATED, not the one the write path stored.
    {
        const r = (try zitStep(gpa, io, out, abs_exe, src_dir, env, &.{"pack"}, "zit pack")) orelse return false;
        defer gpa.free(r.stdout);
        defer gpa.free(r.stderr);
        const n = reDeltaCount(r.stdout) orelse {
            try out.print("FAIL S5 ({s}): could not parse pack's re-delta count\n  pack stdout: {s}\n", .{ citation, r.stdout });
            return false;
        };
        if (n == 0) {
            try out.print("FAIL S5 ({s}): pack regenerated no delta — the fixture no longer exercises a served regenerated delta container\n  pack stdout: {s}\n", .{ citation, r.stdout });
            return false;
        }
    }

    // Serve the repacked store; the real fossil client clones it over the wire.
    const s5_serve_port = try freshLoopbackPort(io);
    const port_str = try std.fmt.allocPrint(arena, "{d}", .{s5_serve_port});
    var server = try spawnZitServe(io, abs_exe, src_dir, env, port_str);
    defer server.kill(io);
    waitForListen(io, s5_serve_port) catch {
        try out.print("FAIL S5 ({s}): zit serve never bound 127.0.0.1:{d}\n", .{ citation, s5_serve_port });
        return false;
    };

    // A bare-root URL: a conformant `fossil clone` POSTs to "/" (sync §4). This
    // is the fossil-client-from-root case e2e explicitly defers to smoke.
    const url = try std.fmt.allocPrint(arena, "http://127.0.0.1:{d}/", .{s5_serve_port});
    const clone_repo = try std.fs.path.join(arena, &.{ base, "verify.fossil" });
    {
        const res = try runFossil(gpa, io, &.{ "fossil", "clone", url, clone_repo }, base, env);
        defer gpa.free(res.stdout);
        defer gpa.free(res.stderr);
        if (!okExit(res.term)) {
            try out.print("FAIL S5 ({s}): fossil clone from the repacked zit store failed\n  stderr: {s}\n", .{ citation, res.stderr });
            try printMismatchFooter(out, citation);
            return false;
        }
    }

    // Check out the clone (autosync off — the server is about to be killed) so
    // fossil reconstructs the file from the artifacts it received.
    const co_dir = try std.fs.path.join(arena, &.{ base, "co" });
    try std.Io.Dir.cwd().createDirPath(io, co_dir);
    {
        const res = try runFossil(gpa, io, &.{ "fossil", "open", "--nosync", clone_repo }, co_dir, env);
        defer gpa.free(res.stdout);
        defer gpa.free(res.stderr);
        if (!okExit(res.term)) {
            try out.print("FAIL S5 ({s}): fossil open of the clone failed\n  stderr: {s}\n", .{ citation, res.stderr });
            try printMismatchFooter(out, citation);
            return false;
        }
    }

    // THE PROOF: fossil's reconstructed file == zit's own committed bytes. A
    // regenerated delta that dropped or corrupted a byte fails here.
    const co_blob = try std.fs.path.join(arena, &.{ co_dir, "big.txt" });
    const fossil_bytes = try std.Io.Dir.cwd().readFileAlloc(io, co_blob, gpa, .limited(1 << 20));
    defer gpa.free(fossil_bytes);
    if (!std.mem.eql(u8, edited, fossil_bytes)) {
        try out.print("FAIL S5 ({s}): fossil clone from the repacked store did not reconstruct zit's committed bytes\n", .{citation});
        try printBytesPreview(out, "zit committed", edited);
        try printBytesPreview(out, "fossil clone", fossil_bytes);
        try printDiffWindow(out, edited, fossil_bytes);
        try printMismatchFooter(out, citation);
        return false;
    }

    try out.print("PASS S5 ({s}): fossil clone from a repacked zit store reconstructs zit's committed bytes byte-for-byte\n", .{citation});
    return true;
}

// ---------------------------------------------------------------------------
// S6 -- clone → open against a real fossil SERVER (sync §3.7/§4, P3-F3). The
// mirror of S5: there zit served and fossil cloned; here fossil serves and the
// real `zit` binary clones AND opens, so the working tree zit materializes is
// the thing under test. This is the half of the clone story digest-set equality
// cannot see — a repository can converge perfectly on the wire and still be
// unusable if nothing turns it into files on disk.
//
// The file policy at the top of this file holds: the compared bytes are OUR OWN
// (we author `s6_payload`, hand it to `fossil add`/`fossil commit`, and compare
// it to what zit materializes). fossil is the container and the transport in
// between; it never authors a byte of the oracle. A mismatch means zit's
// artifact reconstruction or its working-tree materialization dropped or
// corrupted content that fossil carried faithfully.
// ---------------------------------------------------------------------------

/// The bytes S6 round-trips. Authored here, never by fossil: multi-line with a
/// trailing newline, so a materialization that mangled line endings or dropped
/// the tail is caught.
const s6_payload =
    \\line one of the fossil-served source tree
    \\line two, so a truncation is visible
    \\line three
    \\
;

/// Run `fossil <argv...>` and fail the scenario (printing stderr) unless it
/// exits 0. Returns true on success; the caller aborts the scenario on false.
fn fossilStep(
    gpa: std.mem.Allocator,
    io: std.Io,
    out: *std.Io.Writer,
    argv: []const []const u8,
    cwd_path: []const u8,
    env: *const std.process.Environ.Map,
    label: []const u8,
) !bool {
    const res = try runFossil(gpa, io, argv, cwd_path, env);
    defer gpa.free(res.stdout);
    defer gpa.free(res.stderr);
    if (!okExit(res.term)) {
        try out.print("FAIL S6 (§3.7/§4): {s} failed\n  stderr: {s}\n", .{ label, res.stderr });
        return false;
    }
    return true;
}

fn runOpenFromFossilServerScenario(
    arena: std.mem.Allocator,
    gpa: std.mem.Allocator,
    io: std.Io,
    tmp_root: []const u8,
    env: *const std.process.Environ.Map,
    out: *std.Io.Writer,
) !bool {
    const citation = "§3.7/§4";
    const abs_exe = try zitAbsPath(arena, io);

    const base = try std.fs.path.join(arena, &.{ tmp_root, "S6" });
    const co_dir = try std.fs.path.join(arena, &.{ base, "co" });
    try std.Io.Dir.cwd().createDirPath(io, co_dir);
    const repo_path = try std.fs.path.join(arena, &.{ base, "source.fossil" });

    // A real fossil repository holding one check-in of OUR bytes.
    if (!try fossilStep(gpa, io, out, &.{ "fossil", "init", repo_path }, base, env, "fossil init")) return false;
    if (!try fossilStep(gpa, io, out, &.{ "fossil", "open", "--nosync", repo_path }, co_dir, env, "fossil open")) return false;
    const payload_path = try std.fs.path.join(arena, &.{ co_dir, "big.txt" });
    try std.Io.Dir.cwd().writeFile(io, .{ .sub_path = payload_path, .data = s6_payload });
    if (!try fossilStep(gpa, io, out, &.{ "fossil", "add", "big.txt" }, co_dir, env, "fossil add")) return false;
    if (!try fossilStep(
        gpa,
        io,
        out,
        &.{ "fossil", "commit", "-m", "s6 seed", "--user-override", "u", "--no-warnings" },
        co_dir,
        env,
        "fossil commit",
    )) return false;

    // Serve it with the real fossil server and clone it with the real zit
    // binary over the bare-root URL (sync §4: the client posts the repo URL path
    // unchanged and MUST NOT append /xfer).
    const s6_serve_port = try freshLoopbackPort(io);
    const port_str = try std.fmt.allocPrint(arena, "{d}", .{s6_serve_port});
    var server = spawnFossilServe(gpa, io, out, repo_path, base, env, port_str) catch |err| {
        try out.print("FAIL S6 ({s}): could not spawn fossil server: {s}\n", .{ citation, @errorName(err) });
        return false;
    };
    defer stopFossilServe(io, &server);
    // The same retry-connect wait S5 uses: a successful TCP connect proves the
    // listener is up, whichever binary owns it.
    waitForListen(io, s6_serve_port) catch {
        try out.print("FAIL S6 ({s}): fossil server never bound 127.0.0.1:{d}\n", .{ citation, s6_serve_port });
        return false;
    };

    const url = try std.fmt.allocPrint(arena, "http://127.0.0.1:{d}/", .{s6_serve_port});
    {
        const r = (try zitStep(gpa, io, out, abs_exe, base, env, &.{ "clone", "--no-open", url, "cloned" }, "zit clone (from fossil server)")) orelse return false;
        gpa.free(r.stdout);
        gpa.free(r.stderr);
    }

    // OPEN: the clone's checkout is still unborn, so this is what turns the
    // converged repository into a working tree.
    const cloned_dir = try std.fs.path.join(arena, &.{ base, "cloned" });
    {
        const r = (try zitStep(gpa, io, out, abs_exe, cloned_dir, env, &.{"open"}, "zit open")) orelse return false;
        defer gpa.free(r.stdout);
        defer gpa.free(r.stderr);
        if (std.mem.indexOf(u8, r.stdout, "trunk") == null) {
            try out.print("FAIL S6 ({s}): zit open did not report opening trunk\n  stdout: {s}\n", .{ citation, r.stdout });
            return false;
        }
    }

    // THE PROOF: the file zit materialized == the bytes we committed through
    // fossil. A dropped artifact, a mis-reconstructed delta, or a materialization
    // that never wrote the file fails here.
    const opened_blob = try std.fs.path.join(arena, &.{ cloned_dir, "big.txt" });
    const zit_bytes = std.Io.Dir.cwd().readFileAlloc(io, opened_blob, gpa, .limited(1 << 20)) catch |err| {
        try out.print("FAIL S6 ({s}): zit open left no readable big.txt in the cloned tree: {s}\n", .{ citation, @errorName(err) });
        try printMismatchFooter(out, citation);
        return false;
    };
    defer gpa.free(zit_bytes);
    if (!std.mem.eql(u8, s6_payload, zit_bytes)) {
        try out.print("FAIL S6 ({s}): the tree zit opened does not match the bytes committed through fossil\n", .{citation});
        try printBytesPreview(out, "committed", s6_payload);
        try printBytesPreview(out, "zit opened", zit_bytes);
        try printDiffWindow(out, s6_payload, zit_bytes);
        try printMismatchFooter(out, citation);
        return false;
    }

    try out.print("PASS S6 ({s}): zit clone+open against a real fossil server materializes the committed bytes byte-for-byte\n", .{citation});
    return true;
}

// ---------------------------------------------------------------------------
// S7/S8 -- the PUSH-direction interop pair (sync §3.3/§3.7 push, §3.2/§3.7
// pull). S5/S6 exercise only pull/clone wire (fossil pulls from zit, zit pulls
// from fossil); the push-side wire -- zit as the sync CLIENT uploading to a
// server, its igot/gimme ordering and capability negotiation -- was invisible.
// Both scenarios share the same prelude: zit authors a seed check-in, a real
// fossil repo CLONES it (so client and server share a project-code, without
// which the sync layer refuses the exchange), zit grows a second check-in the
// fossil repo lacks, then zit PUSHES that growth up to a live `fossil server`.
//
// The file policy at the top holds throughout: every compared byte is OUR OWN
// -- we author the pushed file blob and (in S8) the file blob a fossil commit
// carries. fossil is the container and the transport; it never authors a byte
// of an oracle. A mismatch means zit's push wire, or its pull-and-reconstruct,
// dropped or corrupted content fossil relayed faithfully.
// ---------------------------------------------------------------------------

/// The seed the fossil repo clones and the zit repo shares -- our own bytes.
const interop_seed_payload =
    \\seed line shared by the fossil clone and the zit repo
    \\
;

/// The file zit authors, commits, and PUSHES to the fossil server. Multi-line
/// with a trailing newline so a truncated or line-ending-mangled push is caught.
const push_grow_payload =
    \\zit authored this line and pushed it to a fossil server
    \\second line so a dropped tail is visible
    \\
;

/// S8: authored here, committed THROUGH a fossil checkout (fossil is the
/// container), that zit must PULL and reconstruct. The library reader confirms
/// convergence by reading it back under its own artifact name.
const fossil_ext_payload =
    \\authored here, carried by a fossil commit, pulled by zit
    \\second line so a truncation is visible
    \\
;

/// The prelude state both push scenarios build on.
const InteropSetup = struct {
    base: []const u8,
    zsrc: []const u8,
    dst_fossil: []const u8,
    abs_exe: []const u8,
};

/// Disable Fossil's detached backoffice worker, then spawn
/// `fossil server --port <port> <repo>` in a dedicated process group. The
/// repositories served here are disposable smoke fixtures; disabling their
/// maintenance worker keeps every process inside the test-owned group.
fn spawnFossilServe(
    gpa: std.mem.Allocator,
    io: std.Io,
    out: *std.Io.Writer,
    repo_path: []const u8,
    cwd_path: []const u8,
    env: *const std.process.Environ.Map,
    port_str: []const u8,
) !std.process.Child {
    const disable = try runFossil(
        gpa,
        io,
        &.{ "fossil", "settings", "backoffice-disable", "1", "-R", repo_path },
        cwd_path,
        env,
    );
    defer gpa.free(disable.stdout);
    defer gpa.free(disable.stderr);
    if (!okExit(disable.term)) {
        try out.print(
            "fossil settings backoffice-disable failed ({any})\n  stderr: {s}\n",
            .{ disable.term, disable.stderr },
        );
        return error.CannotDisableFossilBackoffice;
    }

    return std.process.spawn(io, .{
        .argv = &.{ "fossil", "server", "--port", port_str, repo_path },
        .cwd = .{ .path = cwd_path },
        .environ_map = env,
        .stdout = .ignore,
        .stderr = .ignore,
        .pgid = if (builtin.os.tag == .windows) null else 0,
    });
}

/// Stop a Fossil server and all request handlers forked from it. `Child.kill`
/// addresses only the original PID, which can leave a POSIX handler holding the
/// listening socket after the parent is reaped.
fn stopFossilServe(io: std.Io, server: *std.process.Child) void {
    if (comptime builtin.os.tag != .windows) {
        if (server.id) |pid| std.posix.kill(-pid, .KILL) catch {};
    }
    server.kill(io);
}

/// `zit <tail...>` step, tagged with the scenario name/citation for failure
/// reporting (the S5-scoped `zitStep` bakes in "S5"). Returns the captured
/// result on success (caller frees), or null on a non-zero exit (already freed
/// and reported).
fn zitStepTagged(
    gpa: std.mem.Allocator,
    io: std.Io,
    out: *std.Io.Writer,
    abs_exe: []const u8,
    cwd_path: []const u8,
    env: *const std.process.Environ.Map,
    tail: []const []const u8,
    tag: []const u8,
    citation: []const u8,
    label: []const u8,
) !?std.process.RunResult {
    const res = try runZit(gpa, io, abs_exe, cwd_path, env, tail);
    if (!okExit(res.term)) {
        try out.print("FAIL {s} ({s}): {s} failed\n  stderr: {s}\n", .{ tag, citation, label, res.stderr });
        try printMismatchFooter(out, citation);
        gpa.free(res.stdout);
        gpa.free(res.stderr);
        return null;
    }
    return res;
}

/// `fossil <argv...>` step, tagged with the scenario name/citation (the
/// S6-scoped `fossilStep` bakes in "S6"). True on exit 0; the caller aborts the
/// scenario on false.
fn fossilStepTagged(
    gpa: std.mem.Allocator,
    io: std.Io,
    out: *std.Io.Writer,
    argv: []const []const u8,
    cwd_path: []const u8,
    env: *const std.process.Environ.Map,
    tag: []const u8,
    citation: []const u8,
    label: []const u8,
) !bool {
    const res = try runFossil(gpa, io, argv, cwd_path, env);
    defer gpa.free(res.stdout);
    defer gpa.free(res.stderr);
    if (!okExit(res.term)) {
        try out.print("FAIL {s} ({s}): {s} failed\n  stderr: {s}\n", .{ tag, citation, label, res.stderr });
        return false;
    }
    return true;
}

/// The shared prelude: a zit repo and a fossil repo that share a project-code,
/// with a zit-authored `grow` check-in PUSHED from zit up to the fossil server.
/// Returns the paths on success, or null on any failure (already reported under
/// `tag`).
fn establishPushedFossil(
    arena: std.mem.Allocator,
    gpa: std.mem.Allocator,
    io: std.Io,
    tmp_root: []const u8,
    env: *const std.process.Environ.Map,
    out: *std.Io.Writer,
    tag: []const u8,
    citation: []const u8,
    sub: []const u8,
) !?InteropSetup {
    const abs_exe = try zitAbsPath(arena, io);
    const base = try std.fs.path.join(arena, &.{ tmp_root, sub });
    const zsrc = try std.fs.path.join(arena, &.{ base, "zsrc" });
    const dst_fossil = try std.fs.path.join(arena, &.{ base, "dst.fossil" });
    try std.Io.Dir.cwd().createDirPath(io, zsrc);

    // zit authors a seed check-in.
    {
        const r = (try zitStepTagged(gpa, io, out, abs_exe, zsrc, env, &.{"init"}, tag, citation, "zit init")) orelse return null;
        gpa.free(r.stdout);
        gpa.free(r.stderr);
    }
    const seed_path = try std.fs.path.join(arena, &.{ zsrc, "a.txt" });
    try std.Io.Dir.cwd().writeFile(io, .{ .sub_path = seed_path, .data = interop_seed_payload });
    inline for (.{
        .{ &[_][]const u8{ "add", "a.txt" }, "zit add (seed)" },
        .{ &[_][]const u8{ "commit", "-m", "seed", "--user", "u" }, "zit commit (seed)" },
    }) |step| {
        const r = (try zitStepTagged(gpa, io, out, abs_exe, zsrc, env, step[0], tag, citation, step[1])) orelse return null;
        gpa.free(r.stdout);
        gpa.free(r.stderr);
    }

    // Serve the seed; the real fossil binary CLONES it, establishing the shared
    // project-code the sync layer requires before it will accept a push.
    const seed_port = try freshLoopbackPort(io);
    const seed_port_str = try std.fmt.allocPrint(arena, "{d}", .{seed_port});
    {
        var server = try spawnZitServe(io, abs_exe, zsrc, env, seed_port_str);
        waitForListen(io, seed_port) catch {
            server.kill(io);
            try out.print("FAIL {s} ({s}): zit serve never bound 127.0.0.1:{d}\n", .{ tag, citation, seed_port });
            return null;
        };
        const seed_url = try std.fmt.allocPrint(arena, "http://127.0.0.1:{d}/", .{seed_port});
        const ok_clone = try fossilStepTagged(gpa, io, out, &.{ "fossil", "clone", seed_url, dst_fossil }, base, env, tag, citation, "fossil clone of the zit seed");
        server.kill(io);
        if (!ok_clone) return null;
    }

    // The clone saved a remote (the now-dead seed server) as the fossil repo's
    // autosync target; disable autosync so a fossil-side commit (S8) cannot
    // silently abort trying to reach it. Grant the anonymous loopback sync user
    // check-in ('i') so the push is authorized (a bare sync request logs in as
    // "nobody", whose cloned caps are read-only).
    if (!try fossilStepTagged(gpa, io, out, &.{ "fossil", "settings", "autosync", "off", "-R", dst_fossil }, base, env, tag, citation, "fossil settings autosync off")) return null;
    if (!try fossilStepTagged(gpa, io, out, &.{ "fossil", "user", "capabilities", "nobody", "gjorzwik", "-R", dst_fossil }, base, env, tag, citation, "fossil user capabilities nobody")) return null;

    // zit grows a check-in the fossil repo lacks.
    const grow_path = try std.fs.path.join(arena, &.{ zsrc, "b.txt" });
    try std.Io.Dir.cwd().writeFile(io, .{ .sub_path = grow_path, .data = push_grow_payload });
    inline for (.{
        .{ &[_][]const u8{ "add", "b.txt" }, "zit add (grow)" },
        .{ &[_][]const u8{ "commit", "-m", "grow", "--user", "u" }, "zit commit (grow)" },
    }) |step| {
        const r = (try zitStepTagged(gpa, io, out, abs_exe, zsrc, env, step[0], tag, citation, step[1])) orelse return null;
        gpa.free(r.stdout);
        gpa.free(r.stderr);
    }

    // fossil serves the shared repo; zit PUSHES its growth up over the wire.
    const push_port = try freshLoopbackPort(io);
    const push_port_str = try std.fmt.allocPrint(arena, "{d}", .{push_port});
    {
        var server = spawnFossilServe(gpa, io, out, dst_fossil, base, env, push_port_str) catch |err| {
            try out.print("FAIL {s} ({s}): could not spawn fossil server: {s}\n", .{ tag, citation, @errorName(err) });
            return null;
        };
        waitForListen(io, push_port) catch {
            stopFossilServe(io, &server);
            try out.print("FAIL {s} ({s}): fossil server never bound 127.0.0.1:{d}\n", .{ tag, citation, push_port });
            return null;
        };
        const push_url = try std.fmt.allocPrint(arena, "http://127.0.0.1:{d}/", .{push_port});
        const push_res = try zitStepTagged(gpa, io, out, abs_exe, zsrc, env, &.{ "push", push_url }, tag, citation, "zit push to the fossil server");
        stopFossilServe(io, &server);
        const r = push_res orelse return null;
        gpa.free(r.stdout);
        gpa.free(r.stderr);
    }

    return .{ .base = base, .zsrc = zsrc, .dst_fossil = dst_fossil, .abs_exe = abs_exe };
}

/// S7 -- zit push -> fossil server. After the shared prelude has pushed zit's
/// grown check-in up to the fossil repo, a fresh fossil checkout of that repo
/// must reconstruct the pushed file byte-for-byte: fossil hash-verifies every
/// artifact it stores and reconstructs, so a checkout that yields our exact
/// bytes proves the push delivered the artifacts intact over the wire.
fn runPushToFossilServerScenario(
    arena: std.mem.Allocator,
    gpa: std.mem.Allocator,
    io: std.Io,
    tmp_root: []const u8,
    env: *const std.process.Environ.Map,
    out: *std.Io.Writer,
) !bool {
    const citation = "§3.3/§3.7";
    const setup = (try establishPushedFossil(arena, gpa, io, tmp_root, env, out, "S7", citation, "S7")) orelse return false;

    // THE PROOF: open the pushed-to repo and read back zit's grown file. fossil
    // materializes it from the artifacts the push delivered; a push that dropped
    // or corrupted a byte fails here.
    const co = try std.fs.path.join(arena, &.{ setup.base, "verify-co" });
    try std.Io.Dir.cwd().createDirPath(io, co);
    if (!try fossilStepTagged(gpa, io, out, &.{ "fossil", "open", "--nosync", setup.dst_fossil }, co, env, "S7", citation, "fossil open of the pushed repo")) return false;

    const co_blob = try std.fs.path.join(arena, &.{ co, "b.txt" });
    const fossil_bytes = std.Io.Dir.cwd().readFileAlloc(io, co_blob, gpa, .limited(1 << 20)) catch |err| {
        try out.print("FAIL S7 ({s}): the pushed repo has no readable b.txt after checkout: {s}\n", .{ citation, @errorName(err) });
        try printMismatchFooter(out, citation);
        return false;
    };
    defer gpa.free(fossil_bytes);
    if (!std.mem.eql(u8, push_grow_payload, fossil_bytes)) {
        try out.print("FAIL S7 ({s}): the file fossil reconstructed after zit's push does not match zit's committed bytes\n", .{citation});
        try printBytesPreview(out, "zit pushed", push_grow_payload);
        try printBytesPreview(out, "fossil rebuilt", fossil_bytes);
        try printDiffWindow(out, push_grow_payload, fossil_bytes);
        try printMismatchFooter(out, citation);
        return false;
    }

    try out.print("PASS S7 ({s}): fossil reconstructs zit's pushed check-in byte-for-byte from the upload\n", .{citation});
    return true;
}

/// S8 -- fossil commit -> zit pull. On the repo zit pushed to in the prelude, a
/// fossil checkout commits one more file (our bytes, carried inside a
/// fossil-authored check-in). zit then PULLS that extension. Convergence is
/// asserted through the library reader: `zit.Repository` opens the pulled store
/// and returns the carried blob under its own artifact name, byte-identical to
/// what we authored -- so the pull ingested and reconstructed a check-in it did
/// not itself write.
fn runPullFossilExtensionScenario(
    arena: std.mem.Allocator,
    gpa: std.mem.Allocator,
    io: std.Io,
    tmp_root: []const u8,
    env: *const std.process.Environ.Map,
    out: *std.Io.Writer,
) !bool {
    const citation = "§3.2/§3.7";
    const setup = (try establishPushedFossil(arena, gpa, io, tmp_root, env, out, "S8", citation, "S8")) orelse return false;

    // A fossil checkout extends the shared repo with one more commit wrapping
    // OUR bytes. autosync is already off on the repo, so the commit cannot stall
    // reaching the dead seed remote; --nosync is belt-and-suspenders.
    const fco = try std.fs.path.join(arena, &.{ setup.base, "fossil-co" });
    try std.Io.Dir.cwd().createDirPath(io, fco);
    if (!try fossilStepTagged(gpa, io, out, &.{ "fossil", "open", "--nosync", setup.dst_fossil }, fco, env, "S8", citation, "fossil open of the shared repo")) return false;
    if (!try fossilStepTagged(gpa, io, out, &.{ "fossil", "update", "trunk" }, fco, env, "S8", citation, "fossil update trunk")) return false;
    const ext_path = try std.fs.path.join(arena, &.{ fco, "c.txt" });
    try std.Io.Dir.cwd().writeFile(io, .{ .sub_path = ext_path, .data = fossil_ext_payload });
    if (!try fossilStepTagged(gpa, io, out, &.{ "fossil", "add", "c.txt" }, fco, env, "S8", citation, "fossil add")) return false;
    if (!try fossilStepTagged(
        gpa,
        io,
        out,
        &.{ "fossil", "commit", "-m", "fossil extends", "--user-override", "u", "--no-warnings", "--nosync" },
        fco,
        env,
        "S8",
        citation,
        "fossil commit",
    )) return false;

    // fossil serves the extended repo; zit PULLS the check-in it lacks.
    const pull_port = try freshLoopbackPort(io);
    const pull_port_str = try std.fmt.allocPrint(arena, "{d}", .{pull_port});
    {
        var server = spawnFossilServe(gpa, io, out, setup.dst_fossil, setup.base, env, pull_port_str) catch |err| {
            try out.print("FAIL S8 ({s}): could not spawn fossil server: {s}\n", .{ citation, @errorName(err) });
            return false;
        };
        waitForListen(io, pull_port) catch {
            stopFossilServe(io, &server);
            try out.print("FAIL S8 ({s}): fossil server never bound 127.0.0.1:{d}\n", .{ citation, pull_port });
            return false;
        };
        const pull_url = try std.fmt.allocPrint(arena, "http://127.0.0.1:{d}/", .{pull_port});
        const pull_res = try zitStepTagged(gpa, io, out, setup.abs_exe, setup.zsrc, env, &.{ "pull", pull_url }, "S8", citation, "zit pull from the fossil server");
        stopFossilServe(io, &server);
        const r = pull_res orelse return false;
        gpa.free(r.stdout);
        gpa.free(r.stderr);
    }

    // THE PROOF, through the library reader: the pulled store holds the
    // fossil-carried blob under its own artifact name (sha3-256 over our bytes),
    // and `Repository.blob` re-verifies content against that name (§7.2) before
    // returning it. A pull that fetched nothing, or reconstructed the blob
    // wrong, fails here.
    var root = std.Io.Dir.cwd().openDir(io, setup.zsrc, .{}) catch |err| {
        try out.print("FAIL S8 ({s}): could not open the pulled zit checkout root: {s}\n", .{ citation, @errorName(err) });
        return false;
    };
    defer root.close(io);
    var repo: zit.Repository = undefined;
    repo.open(gpa, io, root, .{}) catch |err| {
        try out.print("FAIL S8 ({s}): opening the pulled zit repo via the library failed: {s}\n", .{ citation, @errorName(err) });
        try printMismatchFooter(out, citation);
        return false;
    };
    defer repo.deinit(gpa);

    var name_buf: [64]u8 = undefined;
    const ext_name = artifact.artifactName(.sha3_256, fossil_ext_payload, &name_buf);
    const got = (repo.blob(gpa, ext_name) catch |err| {
        try out.print("FAIL S8 ({s}): reading the pulled blob {s} via the library failed: {s}\n", .{ citation, ext_name, @errorName(err) });
        try printMismatchFooter(out, citation);
        return false;
    }) orelse {
        try out.print("FAIL S8 ({s}): the pull did not converge — blob {s} absent from zit's store after pull\n", .{ citation, ext_name });
        try printMismatchFooter(out, citation);
        return false;
    };
    defer gpa.free(got);
    if (!std.mem.eql(u8, fossil_ext_payload, got)) {
        try out.print("FAIL S8 ({s}): the pulled blob does not match the bytes committed through fossil\n", .{citation});
        try printBytesPreview(out, "committed", fossil_ext_payload);
        try printBytesPreview(out, "zit pulled", got);
        try printDiffWindow(out, fossil_ext_payload, got);
        try printMismatchFooter(out, citation);
        return false;
    }

    try out.print("PASS S8 ({s}): zit pulls a fossil-authored check-in and the library reader confirms convergence\n", .{citation});
    return true;
}

// ---------------------------------------------------------------------------
// S9 -- a real fossil CLONE from `zit serve` of a DEFER-SHUNNED store: the
// shunned artifact must be ABSENT, not CORRUPT (sync §9.5 L1988-1992, §8.2
// L1504-1506).
//
// §9.5 L1989-1991 — "A subsequent read-authorized transfer path requests igot
// for every eligible holding after the Section 3.3 shun, phantom, private, and
// peer-has filters" — is a statement about what the server sends. This scenario
// is the only place a REAL peer judges the result.
//
// `zit shun --force --defer` records the row WITHOUT rewriting the generation, so
// two things are simultaneously true in the served store: the shunned bytes are
// still on disk, and a SURVIVING frame in the same generation is a delta rooted on
// the shunned one. Both halves of the send-side filter are therefore under test:
//
//   * the shunned artifact must not be announced or sent — fossil must end up
//     holding it as a PHANTOM, which is fossil's own word for "absent";
//   * the surviving delta must arrive in FULL form. Handing out its delta wire
//     form would ship a DELTASRC this server never sends; fossil would store a
//     permanently dangling delta and gimme a basis it is silently skipped on
//     (§3.3 L532-535) — CORRUPT, the outcome this scenario rules out.
//
// The oracle is `fossil test-integrity`, read for two things fossil says in its
// own words: the shunned name is skipped as a PHANTOM, and the blobs it did check
// report ZERO errors. "Absent" and "corrupt" are different outcomes and only one
// is acceptable. Every byte fossil sees is zit's own output; fossil only confirms.
// ---------------------------------------------------------------------------

/// The 64-hex artifact name `zit ls` prints for `want` (its lines are
/// `HASH SP PATH`), or null when the path is not listed.
/// From a `zit ls --long` listing (`<hash> <size> <perm> <class> <name>`;
/// the bare listing is names only, zit#170), the hash of `want`.
fn lsHashOf(listing: []const u8, want: []const u8) ?[]const u8 {
    var it = std.mem.splitScalar(u8, listing, '\n');
    while (it.next()) |line| {
        const sp = std.mem.indexOfScalar(u8, line, ' ') orelse continue;
        const last = std.mem.lastIndexOfScalar(u8, line, ' ') orelse continue;
        if (!std.mem.eql(u8, std.mem.trim(u8, line[last + 1 ..], " \t\r"), want)) continue;
        return line[0..sp];
    }
    return null;
}

/// The count parsed from `zit stat`'s `deltas N` line, or null if absent. Guards
/// S9's premise: with no delta frame in the store, the surviving-delta half of the
/// scenario is not being exercised at all.
fn statDeltaCount(text: []const u8) ?usize {
    var it = std.mem.splitScalar(u8, text, '\n');
    while (it.next()) |line| {
        if (!std.mem.startsWith(u8, line, "deltas ")) continue;
        return std.fmt.parseInt(usize, std.mem.trim(u8, line["deltas ".len..], " \t\r"), 10) catch null;
    }
    return null;
}

/// Does `text` carry a `skip phantom` line naming `hash`? That is fossil's own
/// report that it holds the name but no content — ABSENT.
fn reportsPhantom(text: []const u8, hash: []const u8) bool {
    var it = std.mem.splitScalar(u8, text, '\n');
    while (it.next()) |line| {
        if (std.mem.indexOf(u8, line, "skip phantom") == null) continue;
        if (std.mem.indexOf(u8, line, hash) != null) return true;
    }
    return false;
}

/// Does the clone hold `hash` in fossil's OWN shun table? The stronger form of
/// absence, and the one zit now produces (#73/#83).
///
/// Before the `/shun` configuration group was carried, a reference to a shunned
/// artifact left fossil holding a PHANTOM -- a name with no content, which
/// fossil treats as something to FETCH. That is the takedown-defeating state:
/// the clone would gimme those bytes from the next peer that had them. Now the
/// shun row travels with the clone, so fossil records the artifact as
/// SUPPRESSED and never asks anyone for it.
///
/// Both are "absent"; only one stays absent. Asked of the database rather than
/// of `test-integrity`'s prose because a row that exists is a fact, while the
/// absence of a phantom line is only the absence of evidence.
fn holdsShunRow(gpa: std.mem.Allocator, io: std.Io, repo_dir: []const u8, hash: []const u8, env: anytype) !bool {
    const sql = try std.fmt.allocPrint(gpa, "select count(*) from shun where uuid='{s}';", .{hash});
    defer gpa.free(sql);
    const res = try runFossil(gpa, io, &.{ "fossil", "sqlite3", "--readonly", sql }, repo_dir, env);
    defer gpa.free(res.stdout);
    defer gpa.free(res.stderr);
    if (!okExit(res.term)) return false;
    return std.mem.indexOfScalar(u8, std.mem.trim(u8, res.stdout, " \t\r\n"), '0') == null;
}

/// Does `text` report ZERO integrity errors? Anchored on the integer immediately
/// before the word `errors`, because the substring "0 errors" is also inside
/// "10 errors", "20 errors" and "100 errors" — the exact outputs that mean CORRUPT,
/// which is the outcome this scenario exists to tell apart from ABSENT. True only
/// when at least one `N errors` count was found and every one of them read zero.
fn reportsZeroErrors(text: []const u8) bool {
    const word = "errors";
    var found = false;
    var i: usize = 0;
    while (std.mem.indexOfPos(u8, text, i, word)) |p| {
        i = p + word.len;
        // Whole word only: `errorsomething` is not a count.
        if (i < text.len and (std.ascii.isAlphanumeric(text[i]) or text[i] == '_')) continue;
        var end = p;
        while (end > 0 and (text[end - 1] == ' ' or text[end - 1] == '\t')) end -= 1;
        var start = end;
        while (start > 0 and std.ascii.isDigit(text[start - 1])) start -= 1;
        if (start == end) continue; // no count in front of this occurrence
        // The digits must be their own token, not the tail of an identifier.
        if (start > 0 and (std.ascii.isAlphanumeric(text[start - 1]) or text[start - 1] == '_')) continue;
        const n = std.fmt.parseInt(u64, text[start..end], 10) catch continue;
        if (n != 0) return false;
        found = true;
    }
    return found;
}

test "smoke S9 oracle: a nonzero integrity count is never read as clean" {
    try std.testing.expect(reportsZeroErrors("low-level database integrity ok\n0 errors\n"));
    try std.testing.expect(reportsZeroErrors("skip phantom abc\n0 errors, 3 phantoms\n"));
    // The defect this helper exists to prevent: "0 errors" is a substring of these.
    try std.testing.expect(!reportsZeroErrors("10 errors\n"));
    try std.testing.expect(!reportsZeroErrors("100 errors detected\n"));
    try std.testing.expect(!reportsZeroErrors("0 errors\n20 errors\n"));
    // No count at all is not a pass either.
    try std.testing.expect(!reportsZeroErrors("errors\n"));
    try std.testing.expect(!reportsZeroErrors("integrity check complete\n"));
}

fn runCloneOfShunnedScenario(
    arena: std.mem.Allocator,
    gpa: std.mem.Allocator,
    io: std.Io,
    tmp_root: []const u8,
    env: *const std.process.Environ.Map,
    out: *std.Io.Writer,
) !bool {
    const citation = "§9.5/§8.2";
    const abs_exe = try zitAbsPath(arena, io);

    const base = try std.fs.path.join(arena, &.{ tmp_root, "S9" });
    const src_dir = try std.fs.path.join(arena, &.{ base, "src" });
    try std.Io.Dir.cwd().createDirPath(io, src_dir);

    {
        const r = (try zitStepIn(gpa, io, out, abs_exe, src_dir, env, &.{"init"}, "zit init", "S9", citation)) orelse return false;
        gpa.free(r.stdout);
        gpa.free(r.stderr);
    }

    // Two files: one to be shunned, one to prove the clone still converged on
    // something. The shunned file's FIRST version is the delta basis of its
    // second, which is what puts the surviving-delta half under test.
    const blob_path = try std.fs.path.join(arena, &.{ src_dir, "big.txt" });
    const keep_path = try std.fs.path.join(arena, &.{ src_dir, "keep.txt" });
    const keep_bytes = "a file with nothing against it\n";
    const seed = try buildBlob(arena, false);
    try std.Io.Dir.cwd().writeFile(io, .{ .sub_path = blob_path, .data = seed });
    try std.Io.Dir.cwd().writeFile(io, .{ .sub_path = keep_path, .data = keep_bytes });
    inline for (.{
        .{ &[_][]const u8{ "add", "big.txt", "keep.txt" }, "zit add (seed)" },
        .{ &[_][]const u8{ "commit", "-m", "seed", "--user", "u" }, "zit commit (seed)" },
    }) |st| {
        const r = (try zitStepIn(gpa, io, out, abs_exe, src_dir, env, st[0], st[1], "S9", citation)) orelse return false;
        gpa.free(r.stdout);
        gpa.free(r.stderr);
    }

    // The seed blob's complete artifact name, read off the tip while the seed IS
    // the tip — no abbreviation to resolve and no timeline parsing.
    const doomed = blk: {
        const r = (try zitStepIn(gpa, io, out, abs_exe, src_dir, env, &.{ "ls", "--long" }, "zit ls --long (seed)", "S9", citation)) orelse return false;
        defer gpa.free(r.stdout);
        defer gpa.free(r.stderr);
        const h = lsHashOf(r.stdout, "big.txt") orelse {
            try out.print("FAIL S9 ({s}): 'zit ls' did not list big.txt\n  stdout: {s}\n", .{ citation, r.stdout });
            return false;
        };
        break :blk try arena.dupe(u8, h);
    };

    const edited = try buildBlob(arena, true);
    try std.Io.Dir.cwd().writeFile(io, .{ .sub_path = blob_path, .data = edited });
    inline for (.{
        .{ &[_][]const u8{ "add", "big.txt" }, "zit add (grow)" },
        .{ &[_][]const u8{ "commit", "-m", "grow", "--user", "u" }, "zit commit (grow)" },
    }) |st| {
        const r = (try zitStepIn(gpa, io, out, abs_exe, src_dir, env, st[0], st[1], "S9", citation)) orelse return false;
        gpa.free(r.stdout);
        gpa.free(r.stderr);
    }

    // Record the shun WITHOUT reclaiming: the bytes stay, so only the send-side
    // filter can keep them off the wire.
    {
        const r = (try zitStepIn(gpa, io, out, abs_exe, src_dir, env, &.{ "shun", "--force", "--defer", doomed }, "zit shun --force --defer", "S9", citation)) orelse return false;
        gpa.free(r.stdout);
        gpa.free(r.stderr);
    }

    // The fixture's premise, stated rather than assumed: the store still carries
    // a delta frame. With none, the surviving-delta half proves nothing.
    {
        const r = (try zitStepIn(gpa, io, out, abs_exe, src_dir, env, &.{"stat"}, "zit stat", "S9", citation)) orelse return false;
        defer gpa.free(r.stdout);
        defer gpa.free(r.stderr);
        const deltas = statDeltaCount(r.stdout) orelse {
            try out.print("FAIL S9 ({s}): could not parse 'zit stat' delta count\n  stdout: {s}\n", .{ citation, r.stdout });
            return false;
        };
        if (deltas == 0) {
            try out.print("FAIL S9 ({s}): the store holds no delta frame — the surviving-delta half of this scenario is not being exercised\n  stdout: {s}\n", .{ citation, r.stdout });
            return false;
        }
    }

    const port = try freshLoopbackPort(io);
    const port_str = try std.fmt.allocPrint(arena, "{d}", .{port});
    var server = try spawnZitServe(io, abs_exe, src_dir, env, port_str);
    defer server.kill(io);
    waitForListen(io, port) catch {
        try out.print("FAIL S9 ({s}): zit serve never bound 127.0.0.1:{d}\n", .{ citation, port });
        return false;
    };

    const url = try std.fmt.allocPrint(arena, "http://127.0.0.1:{d}/", .{port});
    const clone_repo = try std.fs.path.join(arena, &.{ base, "verify.fossil" });
    {
        const res = try runFossil(gpa, io, &.{ "fossil", "clone", url, clone_repo }, base, env);
        defer gpa.free(res.stdout);
        defer gpa.free(res.stderr);
        // A CORRUPT outcome shows up here first: a dangling DELTASRC aborts the
        // clone rather than completing it minus one artifact.
        if (!okExit(res.term)) {
            try out.print("FAIL S9 ({s}): fossil clone of a defer-shunned zit store failed — the shunned artifact must be ABSENT, not a broken clone\n  stdout: {s}\n  stderr: {s}\n", .{ citation, res.stdout, res.stderr });
            try printMismatchFooter(out, citation);
            return false;
        }
    }

    const co_dir = try std.fs.path.join(arena, &.{ base, "co" });
    try std.Io.Dir.cwd().createDirPath(io, co_dir);
    {
        const res = try runFossil(gpa, io, &.{ "fossil", "open", "--nosync", clone_repo }, co_dir, env);
        defer gpa.free(res.stdout);
        defer gpa.free(res.stderr);
        if (!okExit(res.term)) {
            try out.print("FAIL S9 ({s}): fossil open of the clone failed\n  stderr: {s}\n", .{ citation, res.stderr });
            try printMismatchFooter(out, citation);
            return false;
        }
    }

    // The surviving delta arrived in a form fossil could reconstruct: the tip's
    // file is zit's own committed bytes, byte-for-byte.
    {
        const co_blob = try std.fs.path.join(arena, &.{ co_dir, "big.txt" });
        const fossil_bytes = try std.Io.Dir.cwd().readFileAlloc(io, co_blob, gpa, .limited(1 << 20));
        defer gpa.free(fossil_bytes);
        if (!std.mem.eql(u8, edited, fossil_bytes)) {
            try out.print("FAIL S9 ({s}): the clone did not reconstruct zit's committed tip bytes — a delta whose basis was shunned did not arrive in full form\n", .{citation});
            try printBytesPreview(out, "zit committed", edited);
            try printBytesPreview(out, "fossil clone", fossil_bytes);
            try printDiffWindow(out, edited, fossil_bytes);
            try printMismatchFooter(out, citation);
            return false;
        }
    }

    // THE PROOF, in fossil's own words: the shunned artifact is ABSENT, and every
    // blob fossil DID receive checks clean.
    //
    // Absence now has a stronger shape than it used to. Before zit carried the
    // `/shun` configuration group (#73/#83), fossil ended up holding the name as
    // a PHANTOM — absent, but absent in the way that makes fossil go looking for
    // it, so the very next sync with any third party would undo the takedown.
    // Now the shun row arrives with the clone and fossil records the artifact as
    // suppressed instead, which is why the phantom is gone. The shun row is
    // REQUIRED rather than merely accepted: the phantom is the state the
    // carriage exists to eliminate, so tolerating it would leave this scenario
    // unable to notice the carriage silently regressing — which is the one
    // regression it is now best placed to catch.
    {
        const res = try runFossil(gpa, io, &.{ "fossil", "test-integrity" }, co_dir, env);
        defer gpa.free(res.stdout);
        defer gpa.free(res.stderr);
        if (!okExit(res.term)) {
            try out.print("FAIL S9 ({s}): fossil test-integrity failed on the clone\n  stdout: {s}\n  stderr: {s}\n", .{ citation, res.stdout, res.stderr });
            try printMismatchFooter(out, citation);
            return false;
        }
        if (!(holdsShunRow(gpa, io, co_dir, doomed, env) catch false)) {
            // The phantom probe survives as a DIAGNOSTIC, not as an accepted
            // outcome: it separates "the carriage regressed" from "the artifact
            // was served", which are different bugs with different fixes.
            const phantom_there = reportsPhantom(res.stdout, doomed) or reportsPhantom(res.stderr, doomed);
            if (phantom_there) {
                try out.print("FAIL S9 ({s}): the clone holds the artifact as a PHANTOM rather than a shun row — the /shun configuration group is no longer being carried, so the next sync with any peer would undo the takedown\n", .{citation});
            } else {
                try out.print("FAIL S9 ({s}): the clone neither shuns nor phantoms the artifact — it received content zit must never have served\n  stdout: {s}\n  stderr: {s}\n", .{ citation, res.stdout, res.stderr });
            }
            try printMismatchFooter(out, citation);
            return false;
        }
        const clean = reportsZeroErrors(res.stdout) or reportsZeroErrors(res.stderr);
        if (!clean) {
            try out.print("FAIL S9 ({s}): fossil test-integrity did not report zero errors — the clone is CORRUPT, not merely missing the shunned artifact\n  stdout: {s}\n  stderr: {s}\n", .{ citation, res.stdout, res.stderr });
            try printMismatchFooter(out, citation);
            return false;
        }
    }

    try out.print("PASS S9 ({s}): a fossil clone of a defer-shunned zit store completes, reconstructs the tip byte-for-byte, and records the shunned artifact in its OWN shun table with zero integrity errors\n", .{citation});
    return true;
}

// ---------------------------------------------------------------------------
// S10 -- a real fossil PUSH into `zit serve` against a shunned store (sync
// §7.7 L1437-1440, §3.3 L527-530).
//
// S9 is the SEND direction of a shun against a real peer (fossil clones a
// defer-shunned zit store and the artifact is absent). This is the direction
// the harness had never driven at all: fossil as the sync CLIENT pushing INTO
// `zit serve`. S7 pushes the other way (zit client -> fossil server), so this
// closes the last untested corner of the four (client, server) x (zit, fossil)
// combinations, and it is the one an operator's shun depends on.
//
// WHAT THIS PINS, EXACTLY. A real fossil `push --verily` against a repository
// that has shunned and reclaimed one of its artifacts completes with no
// protocol error and lands nothing. That is the interop claim, and it holds.
//
// WHAT THIS DOES NOT PIN, STATED PLAINLY. It is NOT coverage of §7.7's
// receive-side discard: the scenario passes unchanged with the server's
// admission check deleted outright (checked with a real mutant, not assumed).
// The reason is structural rather than a harness limitation. Content transfer
// in this protocol is RECEIVER-DRIVEN: §3.3 L527-530 already excludes a shunned
// name from the receiver's phantom scan, so `zit serve` never emits a gimme for
// it, and fossil -- whose push offers `igot` and then uploads what the peer asks
// for, plus its own `unsent` artifacts -- has no reason to put the card on the
// wire. Reaching the §7.7 arm needs a peer that pushes a shunned target
// UNSOLICITED, which a conformant peer against this server never does.
//
// So §7.7 is defense in depth here, and its mutation-sensitive coverage lives
// where an unsolicited card can actually be constructed: the `src/sync_test.zig`
// §7.7 unit tests and the `src/e2e.zig` raw-POST scenario, both of which go red
// under the same mutant this one survives. This scenario is kept for the interop
// property in its PASS line and for the direction it opens, not as that proof --
// do not read its green as evidence the admission check is exercised.
//
// The store's `.zit` SIZE across the push is the oracle for "makes no repository
// mutation": `serveRound` persists only when the round wrote something
// (`overlay.writtenCount() > 0`), so a round that lands nothing appends nothing.
// Size is the one measure the shun mask cannot fool -- `zit cat` refuses the
// name whether or not the bytes came back, so it is no oracle here.
// ---------------------------------------------------------------------------

/// Byte length of the file at `path` (cwd-relative or absolute).
fn fileSizeAt(io: std.Io, path: []const u8) !u64 {
    var f = try std.Io.Dir.cwd().openFile(io, path, .{});
    defer f.close(io);
    const st = try f.stat(io);
    return st.size;
}

fn runPushOfShunnedScenario(
    arena: std.mem.Allocator,
    gpa: std.mem.Allocator,
    io: std.Io,
    tmp_root: []const u8,
    env: *const std.process.Environ.Map,
    out: *std.Io.Writer,
) !bool {
    const citation = "§7.7";
    const abs_exe = try zitAbsPath(arena, io);

    const base = try std.fs.path.join(arena, &.{ tmp_root, "S10" });
    const src_dir = try std.fs.path.join(arena, &.{ base, "src" });
    try std.Io.Dir.cwd().createDirPath(io, src_dir);

    {
        const r = (try zitStepIn(gpa, io, out, abs_exe, src_dir, env, &.{"init"}, "zit init", "S10", citation)) orelse return false;
        gpa.free(r.stdout);
        gpa.free(r.stderr);
    }

    // Two files: one the operator will remove, one that must be unaffected.
    const doomed_bytes = "content an operator ordered removed, which fossil will offer back\n";
    const keep_bytes = "a file with nothing against it\n";
    try std.Io.Dir.cwd().writeFile(io, .{
        .sub_path = try std.fs.path.join(arena, &.{ src_dir, "doomed.txt" }),
        .data = doomed_bytes,
    });
    try std.Io.Dir.cwd().writeFile(io, .{
        .sub_path = try std.fs.path.join(arena, &.{ src_dir, "keep.txt" }),
        .data = keep_bytes,
    });
    inline for (.{
        .{ &[_][]const u8{ "add", "doomed.txt", "keep.txt" }, "zit add" },
        .{ &[_][]const u8{ "commit", "-m", "seed", "--user", "u" }, "zit commit" },
    }) |st| {
        const r = (try zitStepIn(gpa, io, out, abs_exe, src_dir, env, st[0], st[1], "S10", citation)) orelse return false;
        gpa.free(r.stdout);
        gpa.free(r.stderr);
    }

    const doomed = blk: {
        const r = (try zitStepIn(gpa, io, out, abs_exe, src_dir, env, &.{ "ls", "--long" }, "zit ls --long", "S10", citation)) orelse return false;
        defer gpa.free(r.stdout);
        defer gpa.free(r.stderr);
        const h = lsHashOf(r.stdout, "doomed.txt") orelse {
            try out.print("FAIL S10 ({s}): 'zit ls' did not list doomed.txt\n  stdout: {s}\n", .{ citation, r.stdout });
            return false;
        };
        break :blk try arena.dupe(u8, h);
    };

    // A real fossil repo clones the whole thing, so it holds the doomed artifact
    // and shares zit's project code (without which the exchange is refused).
    const clone_repo = try std.fs.path.join(arena, &.{ base, "peer.fossil" });
    const co_dir = try std.fs.path.join(arena, &.{ base, "co" });
    try std.Io.Dir.cwd().createDirPath(io, co_dir);
    {
        const port = try freshLoopbackPort(io);
        const port_str = try std.fmt.allocPrint(arena, "{d}", .{port});
        var server = try spawnZitServe(io, abs_exe, src_dir, env, port_str);
        defer server.kill(io);
        waitForListen(io, port) catch {
            try out.print("FAIL S10 ({s}): zit serve never bound 127.0.0.1:{d}\n", .{ citation, port });
            return false;
        };
        const url = try std.fmt.allocPrint(arena, "http://127.0.0.1:{d}/", .{port});
        const res = try runFossil(gpa, io, &.{ "fossil", "clone", url, clone_repo }, base, env);
        defer gpa.free(res.stdout);
        defer gpa.free(res.stderr);
        if (!okExit(res.term)) {
            try out.print("FAIL S10 ({s}): fossil clone of the zit store failed\n  stdout: {s}\n  stderr: {s}\n", .{ citation, res.stdout, res.stderr });
            try printMismatchFooter(out, citation);
            return false;
        }
    }
    {
        const res = try runFossil(gpa, io, &.{ "fossil", "open", "--nosync", clone_repo }, co_dir, env);
        defer gpa.free(res.stdout);
        defer gpa.free(res.stderr);
        if (!okExit(res.term)) {
            try out.print("FAIL S10 ({s}): fossil open of the clone failed\n  stderr: {s}\n", .{ citation, res.stderr });
            try printMismatchFooter(out, citation);
            return false;
        }
    }

    // The operator removes it. `--force` reclaims, so the bytes are gone and the
    // ONLY thing that can keep them out on the way back is §7.7 admission.
    {
        const r = (try zitStepIn(gpa, io, out, abs_exe, src_dir, env, &.{ "shun", "--force", doomed }, "zit shun --force", "S10", citation)) orelse return false;
        gpa.free(r.stdout);
        gpa.free(r.stderr);
    }

    const store_path = try std.fs.path.join(arena, &.{ src_dir, ".zit" });
    const size_before = try fileSizeAt(io, store_path);

    // fossil pushes back into `zit serve`. fossil uploads what its own inventory
    // says the peer lacks, so the shunned artifact goes on the wire unsolicited.
    {
        const port = try freshLoopbackPort(io);
        const port_str = try std.fmt.allocPrint(arena, "{d}", .{port});
        var server = try spawnZitServe(io, abs_exe, src_dir, env, port_str);
        defer server.kill(io);
        waitForListen(io, port) catch {
            try out.print("FAIL S10 ({s}): zit serve never bound 127.0.0.1:{d}\n", .{ citation, port });
            return false;
        };
        const url = try std.fmt.allocPrint(arena, "http://127.0.0.1:{d}/", .{port});
        // `--verily` is fossil's own "exchange extra information ... to ensure no
        // content is overlooked" mode: it re-offers what its sent-flags would
        // otherwise suppress, which is what puts a file card for the shunned
        // artifact back on the wire. A plain `fossil push` after a clone has
        // nothing marked unsent and sends no content at all.
        const res = try runFossil(gpa, io, &.{ "fossil", "push", "--verily", url }, co_dir, env);
        defer gpa.free(res.stdout);
        defer gpa.free(res.stderr);
        // "it emits no error" — fossil reports a protocol error card as a failed
        // push, so a clean exit is the interoperable reading of that clause.
        if (!okExit(res.term)) {
            try out.print("FAIL S10 ({s}): fossil push into zit serve failed — a discarded shunned target must raise NO protocol error\n  stdout: {s}\n  stderr: {s}\n", .{ citation, res.stdout, res.stderr });
            try printMismatchFooter(out, citation);
            return false;
        }
        server.kill(io);
    }

    // THE PROOF: "makes no repository mutation". The store did not grow, so the
    // artifact the operator removed did not come back through the serve door.
    const size_after = try fileSizeAt(io, store_path);
    if (size_after != size_before) {
        try out.print("FAIL S10 ({s}): the served store GREW across a fossil push of shunned content ({d} -> {d} bytes) — the receiver re-admitted what the operator removed\n", .{ citation, size_before, size_after });
        try printMismatchFooter(out, citation);
        return false;
    }

    try out.print("PASS S10 ({s}): a real fossil push of a shunned artifact into zit serve completes with no protocol error and lands nothing\n", .{citation});
    return true;
}

// S11 -- a real fossil push REFUSED by `zit serve`'s content policy (#88).
//
// S10 drives the same direction (fossil as sync client into `zit serve`) for a
// shunned artifact, and its own comment is careful that the scenario survives
// deleting the server's admission check, because a conformant fossil never puts
// that card on the wire unsolicited. This one does NOT have that weakness: the
// violating file is ordinary new content fossil is pushing because it is new,
// so the server genuinely sees it and genuinely decides.
//
// WHAT THIS PINS. A stock fossil client pushing a check-in that violates the
// served repository's declared content policy is refused: the server stores
// nothing, and fossil surfaces the server's own refusal TEXT to the operator.
// The error card is the §11.1 r2 free-text carrier the auth and sparse-origin
// refusals already use; this proves a real fossil reads it rather than choking.
//
// WHAT THIS DOES NOT PIN, STATED PLAINLY. fossil exits ZERO after printing the
// refusal. Verified by hand against 2.28, and it is why the check below asserts
// on the store and on fossil's OUTPUT rather than on its exit status. So the
// refusal is advisory in exit-code terms to a fossil client: a script running
// `fossil push && deploy` proceeds. zit's own client exits 1. That is fossil's
// choice about a protocol-legal error card, not something this server can fix,
// and an operator relying on a policy gate needs to know it.
fn runPushPolicyRefusalScenario(
    arena: std.mem.Allocator,
    gpa: std.mem.Allocator,
    io: std.Io,
    tmp_root: []const u8,
    env: *const std.process.Environ.Map,
    out: *std.Io.Writer,
) !bool {
    const citation = "#88";
    const abs_exe = try zitAbsPath(arena, io);

    const base = try std.fs.path.join(arena, &.{ tmp_root, "S11" });
    const src_dir = try std.fs.path.join(arena, &.{ base, "src" });
    try std.Io.Dir.cwd().createDirPath(io, src_dir);

    {
        const r = (try zitStepIn(gpa, io, out, abs_exe, src_dir, env, &.{"init"}, "zit init", "S11", citation)) orelse return false;
        gpa.free(r.stdout);
        gpa.free(r.stderr);
    }
    try std.Io.Dir.cwd().writeFile(io, .{
        .sub_path = try std.fs.path.join(arena, &.{ src_dir, "seed.txt" }),
        .data = "a file with nothing against it\n",
    });
    inline for (.{
        .{ &[_][]const u8{ "add", "seed.txt" }, "zit add" },
        .{ &[_][]const u8{ "commit", "-m", "seed", "--user", "u" }, "zit commit" },
        // The server declares its policy: *.ok are the binaries it expects, so
        // anything else carrying a NUL is refused. Undeclared would accept
        // everything, which is the state of every server that never asked.
        .{ &[_][]const u8{ "settings", "binary-glob", "*.ok" }, "zit settings binary-glob" },
    }) |st| {
        const r = (try zitStepIn(gpa, io, out, abs_exe, src_dir, env, st[0], st[1], "S11", citation)) orelse return false;
        gpa.free(r.stdout);
        gpa.free(r.stderr);
    }

    const clone_repo = try std.fs.path.join(arena, &.{ base, "peer.fossil" });
    const co_dir = try std.fs.path.join(arena, &.{ base, "co" });
    try std.Io.Dir.cwd().createDirPath(io, co_dir);
    {
        const port = try freshLoopbackPort(io);
        const port_str = try std.fmt.allocPrint(arena, "{d}", .{port});
        var server = try spawnZitServe(io, abs_exe, src_dir, env, port_str);
        defer server.kill(io);
        waitForListen(io, port) catch {
            try out.print("FAIL S11 ({s}): zit serve never bound 127.0.0.1:{d}\n", .{ citation, port });
            return false;
        };
        const url = try std.fmt.allocPrint(arena, "http://127.0.0.1:{d}/", .{port});
        const res = try runFossil(gpa, io, &.{ "fossil", "clone", url, clone_repo }, base, env);
        defer gpa.free(res.stdout);
        defer gpa.free(res.stderr);
        if (!okExit(res.term)) {
            try out.print("FAIL S11 ({s}): fossil clone of the zit store failed\n  stderr: {s}\n", .{ citation, res.stderr });
            try printMismatchFooter(out, citation);
            return false;
        }
    }
    {
        const res = try runFossil(gpa, io, &.{ "fossil", "open", "--nosync", clone_repo }, co_dir, env);
        defer gpa.free(res.stdout);
        defer gpa.free(res.stderr);
        if (!okExit(res.term)) {
            try out.print("FAIL S11 ({s}): fossil open of the clone failed\n  stderr: {s}\n", .{ citation, res.stderr });
            try printMismatchFooter(out, citation);
            return false;
        }
    }

    // A binary file the policy does not cover, authored and committed by fossil.
    try std.Io.Dir.cwd().writeFile(io, .{
        .sub_path = try std.fs.path.join(arena, &.{ co_dir, "bad.bin" }),
        .data = "a\x00b\n",
    });
    inline for (.{
        .{ &[_][]const u8{ "fossil", "add", "bad.bin" }, "fossil add" },
        // --nosync: the served repository is deliberately not listening between
        // the clone and the push, and fossil aborts a commit whose autosync
        // cannot reach the peer. The push below is the exchange under test.
        .{ &[_][]const u8{ "fossil", "commit", "-m", "binary from fossil", "--no-warnings", "--nosync" }, "fossil commit" },
    }) |st| {
        const res = try runFossil(gpa, io, st[0], co_dir, env);
        defer gpa.free(res.stdout);
        defer gpa.free(res.stderr);
        if (!okExit(res.term)) {
            try out.print("FAIL S11 ({s}): {s} failed\n  stderr: {s}\n", .{ citation, st[1], res.stderr });
            try printMismatchFooter(out, citation);
            return false;
        }
    }

    const store_path = try std.fs.path.join(arena, &.{ src_dir, ".zit" });
    const size_before = try fileSizeAt(io, store_path);

    {
        const port = try freshLoopbackPort(io);
        const port_str = try std.fmt.allocPrint(arena, "{d}", .{port});
        var server = try spawnZitServe(io, abs_exe, src_dir, env, port_str);
        defer server.kill(io);
        waitForListen(io, port) catch {
            try out.print("FAIL S11 ({s}): zit serve never bound 127.0.0.1:{d}\n", .{ citation, port });
            return false;
        };
        const url = try std.fmt.allocPrint(arena, "http://127.0.0.1:{d}/", .{port});
        const res = try runFossil(gpa, io, &.{ "fossil", "push", url }, co_dir, env);
        defer gpa.free(res.stdout);
        defer gpa.free(res.stderr);
        // The interop claim: fossil READS the error card and shows its text.
        // Asserted on output, not on exit status -- see the header note.
        const combined_has = std.mem.indexOf(u8, res.stdout, "bad.bin") != null or
            std.mem.indexOf(u8, res.stderr, "bad.bin") != null;
        const named_rule = std.mem.indexOf(u8, res.stdout, "binary-glob") != null or
            std.mem.indexOf(u8, res.stderr, "binary-glob") != null;
        if (!combined_has or !named_rule) {
            try out.print("FAIL S11 ({s}): fossil did not surface the server's refusal naming the file and the rule\n  stdout: {s}\n  stderr: {s}\n", .{ citation, res.stdout, res.stderr });
            try printMismatchFooter(out, citation);
            return false;
        }
        server.kill(io);
    }

    // THE PROOF: the refused round stored nothing. A policy that printed a
    // refusal and kept the bytes would be worse than no policy.
    const size_after = try fileSizeAt(io, store_path);
    if (size_after != size_before) {
        try out.print("FAIL S11 ({s}): the served store GREW across a REFUSED push ({d} -> {d} bytes)\n", .{ citation, size_before, size_after });
        try printMismatchFooter(out, citation);
        return false;
    }

    try out.print("PASS S11 ({s}): a stock fossil push violating the server's content policy is refused, the store is unchanged, and fossil surfaces the refusal naming the file and the rule\n", .{citation});
    return true;
}

/// The first line of a tool's output, for a one-line mismatch report.
fn firstLine(text: []const u8) []const u8 {
    const trimmed = std.mem.trim(u8, text, " \t\r\n");
    const end = std.mem.indexOfScalar(u8, trimmed, '\n') orelse trimmed.len;
    return trimmed[0..end];
}

/// S13 (zit#174 item 6) — fossil's commit-time ENCODING warning, and the
/// `encoding-glob` setting that exists to silence it.
///
/// This is the oracle for zit's own encoding gate. zit refuses rather than
/// prompts, so the claim being confirmed is not "zit prints what fossil
/// prints" but the premise underneath it: a UTF-16 file really does stop a
/// stock fossil commit, and declaring the glob really is what lets it through.
/// `--no-prompt` pins the answer fossil would otherwise read from a terminal
/// ('N', checkin.c), which is what makes the exit status deterministic here.
fn runEncodingWarningScenario(
    arena: std.mem.Allocator,
    gpa: std.mem.Allocator,
    io: std.Io,
    tmp_root: []const u8,
    env: *const std.process.Environ.Map,
    out: *std.Io.Writer,
) !bool {
    const citation = "zit#174 item 6";
    const base = try std.fs.path.join(arena, &.{ tmp_root, "S13" });
    const co_dir = try std.fs.path.join(arena, &.{ base, "co" });
    try std.Io.Dir.cwd().createDirPath(io, co_dir);
    const repo = try std.fs.path.join(arena, &.{ base, "repo.fossil" });
    // UTF-16 LE, "hi": a byte-order mark and two 16-bit units. Not binary --
    // the zero bytes are unit halves, which is exactly the distinction
    // fossil's two scanners draw and zit's `classify` now draws with them.
    const utf16_hi = "\xFF\xFEh\x00i\x00";

    const Step = struct { argv: []const []const u8, cwd: []const u8, what: []const u8 };
    for ([_]Step{
        .{ .argv = &.{ "fossil", "init", repo }, .cwd = base, .what = "fossil init" },
        .{ .argv = &.{ "fossil", "open", "--nosync", repo }, .cwd = co_dir, .what = "fossil open" },
        .{ .argv = &.{ "fossil", "add", "wide.txt" }, .cwd = co_dir, .what = "fossil add" },
    }, 0..) |step, i| {
        // The file has to exist before `add` and cannot exist before `open`.
        if (i == 2) try std.Io.Dir.cwd().writeFile(io, .{
            .sub_path = try std.fs.path.join(arena, &.{ co_dir, "wide.txt" }),
            .data = utf16_hi,
        });
        const res = try runFossil(gpa, io, step.argv, step.cwd, env);
        defer gpa.free(res.stdout);
        defer gpa.free(res.stderr);
        if (!okExit(res.term)) {
            try out.print("FAIL S13 ({s}): {s} failed\n  stderr: {s}\n", .{ citation, step.what, res.stderr });
            try printMismatchFooter(out, citation);
            return false;
        }
    }

    // Undeclared: the commit is abandoned, and the message names the class.
    {
        const res = try runFossil(gpa, io, &.{ "fossil", "commit", "-m", "wide", "--no-prompt", "--nosync" }, co_dir, env);
        defer gpa.free(res.stdout);
        defer gpa.free(res.stderr);
        const said_unicode = std.mem.indexOf(u8, res.stderr, "Unicode") != null or
            std.mem.indexOf(u8, res.stdout, "Unicode") != null;
        if (okExit(res.term) or !said_unicode) {
            try out.print(
                "FAIL S13 ({s}): fossil committed a UTF-16 file with no encoding-glob, or did not name the class\n  stdout: {s}\n  stderr: {s}\n",
                .{ citation, firstLine(res.stdout), firstLine(res.stderr) },
            );
            try printMismatchFooter(out, citation);
            return false;
        }
    }

    // Declared: `*` exempts every path, and the same commit goes through.
    {
        const res = try runFossil(gpa, io, &.{ "fossil", "settings", "encoding-glob", "*" }, co_dir, env);
        defer gpa.free(res.stdout);
        defer gpa.free(res.stderr);
        if (!okExit(res.term)) {
            try out.print("FAIL S13 ({s}): fossil settings encoding-glob failed\n  stderr: {s}\n", .{ citation, res.stderr });
            try printMismatchFooter(out, citation);
            return false;
        }
    }
    {
        const res = try runFossil(gpa, io, &.{ "fossil", "commit", "-m", "wide", "--no-prompt", "--nosync" }, co_dir, env);
        defer gpa.free(res.stdout);
        defer gpa.free(res.stderr);
        if (!okExit(res.term)) {
            try out.print(
                "FAIL S13 ({s}): fossil still refused the UTF-16 file with encoding-glob set to *\n  stdout: {s}\n  stderr: {s}\n",
                .{ citation, firstLine(res.stdout), firstLine(res.stderr) },
            );
            try printMismatchFooter(out, citation);
            return false;
        }
    }

    if (!try binaryOutranksEncoding(arena, gpa, io, co_dir, env, out)) return false;

    try out.print("PASS S13 ({s}): a UTF-16 file abandons a stock fossil commit naming the Unicode class, encoding-glob lets the same commit through, and a file with a NUL stays in the binary class regardless\n", .{citation});
    return true;
}

/// The second half of S13: fossil's binary branch is TERMINAL. `encoding-glob`
/// is never consulted for a blob with a NUL, even set to `*`, and it is
/// `binary-glob` that exempts it. This is the oracle for the ordering in zit's
/// `ContentPolicy.fault` -- without it, declaring encoding-glob alone would
/// start refusing every PNG in a tree under a glob that cannot exempt them.
///
/// Runs in the checkout S13 already opened, with `encoding-glob` already `*`.
fn binaryOutranksEncoding(
    arena: std.mem.Allocator,
    gpa: std.mem.Allocator,
    io: std.Io,
    co_dir: []const u8,
    env: *const std.process.Environ.Map,
    out: *std.Io.Writer,
) !bool {
    const citation = "zit#174 item 6";
    // A PNG header: one NUL, and bytes that are not valid UTF-8 either -- so
    // both classes would claim it if binary did not win outright.
    try std.Io.Dir.cwd().writeFile(io, .{
        .sub_path = try std.fs.path.join(arena, &.{ co_dir, "pic.png" }),
        .data = "\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\xFF\xFE\x01",
    });
    {
        const res = try runFossil(gpa, io, &.{ "fossil", "add", "pic.png" }, co_dir, env);
        defer gpa.free(res.stdout);
        defer gpa.free(res.stderr);
        if (!okExit(res.term)) {
            try out.print("FAIL S13 ({s}): fossil add pic.png failed\n  stderr: {s}\n", .{ citation, res.stderr });
            try printMismatchFooter(out, citation);
            return false;
        }
    }
    {
        const res = try runFossil(gpa, io, &.{ "fossil", "commit", "-m", "pic", "--no-prompt", "--nosync" }, co_dir, env);
        defer gpa.free(res.stdout);
        defer gpa.free(res.stderr);
        const said_binary = std.mem.indexOf(u8, res.stderr, "binary") != null or
            std.mem.indexOf(u8, res.stdout, "binary") != null;
        if (okExit(res.term) or !said_binary) {
            try out.print(
                "FAIL S13 ({s}): encoding-glob '*' exempted a file with a NUL, or fossil did not name the binary class\n  stdout: {s}\n  stderr: {s}\n",
                .{ citation, firstLine(res.stdout), firstLine(res.stderr) },
            );
            try printMismatchFooter(out, citation);
            return false;
        }
    }
    // binary-glob is the glob that answers for it.
    {
        const res = try runFossil(gpa, io, &.{ "fossil", "settings", "binary-glob", "*" }, co_dir, env);
        defer gpa.free(res.stdout);
        defer gpa.free(res.stderr);
        if (!okExit(res.term)) {
            try out.print("FAIL S13 ({s}): fossil settings binary-glob failed\n  stderr: {s}\n", .{ citation, res.stderr });
            try printMismatchFooter(out, citation);
            return false;
        }
    }
    {
        const res = try runFossil(gpa, io, &.{ "fossil", "commit", "-m", "pic", "--no-prompt", "--nosync" }, co_dir, env);
        defer gpa.free(res.stdout);
        defer gpa.free(res.stderr);
        if (!okExit(res.term)) {
            try out.print(
                "FAIL S13 ({s}): fossil still refused the NUL-carrying file with binary-glob set to *\n  stdout: {s}\n  stderr: {s}\n",
                .{ citation, firstLine(res.stdout), firstLine(res.stderr) },
            );
            try printMismatchFooter(out, citation);
            return false;
        }
    }
    return true;
}

// ---------------------------------------------------------------------------
// S14 (zit#174 item 12) -- the full `.fossil-settings/` differential.
// ---------------------------------------------------------------------------

/// The one fixture both tools open: every versioned name fossil recognizes,
/// written with every separator form `glob.c glob_create` accepts, committed
/// through fossil, then cloned by zit over the wire. The junk that follows is
/// authored identically into both checkouts, so any difference in the three
/// comparisons below is a difference in how the two tools READ the fixture.
///
/// The comparisons, and why each is shaped the way it is:
///   - `extras` and `extras --dotfiles`: byte for byte. Both print one
///     root-relative path per line, ascending, nothing else (`fossil extras`
///     passes `C_EXTRA` alone to `status_report`, so no classify column).
///   - `clean -n`: as normalized candidate SETS. fossil prefixes each line with
///     `Removed unmanaged file: ` (checkin.c `clean_cmd`) where zit prints the
///     bare path; the two lists are otherwise the same question. There are no
///     `KEPT` lines to drop -- fossil prints those only under `--verbose`.
///   - the post-`update` tree: byte for byte, each tool's own checkout-state
///     names elided, so `manifest`/`manifest.uuid` and the `empty-dirs`
///     directory have to land in both or the listing diverges.
fn runSettingsFixtureScenario(
    arena: std.mem.Allocator,
    gpa: std.mem.Allocator,
    io: std.Io,
    tmp_root: []const u8,
    env: *const std.process.Environ.Map,
    out: *std.Io.Writer,
) !bool {
    const citation = "zit#174 item 12";
    const abs_exe = try zitAbsPath(arena, io);
    const base = try std.fs.path.join(arena, &.{ tmp_root, "S14" });
    const fos_dir = try std.fs.path.join(arena, &.{ base, "fos" });
    try std.Io.Dir.cwd().createDirPath(io, fos_dir);
    const repo = try std.fs.path.join(arena, &.{ base, "repo.fossil" });

    if (!try fossilStepTagged(gpa, io, out, &.{ "fossil", "init", repo }, base, env, "S14", citation, "fossil init")) return false;
    if (!try fossilStepTagged(gpa, io, out, &.{ "fossil", "open", "--nosync", repo }, fos_dir, env, "S14", citation, "fossil open")) return false;

    // The fixture, authored on the fossil side and added by explicit path: a
    // bare `fossil add .fossil-settings` would depend on how the scan treats a
    // named dot directory, which is a different item's question.
    try writeSettingsFixture(arena, io, fos_dir);
    var add_argv: std.ArrayList([]const u8) = .empty;
    try add_argv.appendSlice(arena, &.{ "fossil", "add" });
    for (fixture.settings) |s| {
        try add_argv.append(arena, try std.fmt.allocPrint(arena, ".fossil-settings/{s}", .{s.name}));
    }
    try add_argv.append(arena, fixture.tracked_file.name);
    if (!try fossilStepTagged(gpa, io, out, add_argv.items, fos_dir, env, "S14", citation, "fossil add")) return false;
    if (!try fossilStepTagged(
        gpa,
        io,
        out,
        &.{ "fossil", "commit", "-m", "settings fixture", "--no-prompt", "--nosync", "--user-override", "u" },
        fos_dir,
        env,
        "S14",
        citation,
        "fossil commit",
    )) return false;

    // zit takes the same check-in off a real fossil server.
    const port = try freshLoopbackPort(io);
    const port_str = try std.fmt.allocPrint(arena, "{d}", .{port});
    var server = spawnFossilServe(gpa, io, out, repo, base, env, port_str) catch |err| {
        try out.print("FAIL S14 ({s}): could not spawn fossil server: {s}\n", .{ citation, @errorName(err) });
        return false;
    };
    defer stopFossilServe(io, &server);
    waitForListen(io, port) catch {
        try out.print("FAIL S14 ({s}): fossil server never bound 127.0.0.1:{d}\n", .{ citation, port });
        return false;
    };
    const url = try std.fmt.allocPrint(arena, "http://127.0.0.1:{d}/", .{port});
    const zit_dir = try std.fs.path.join(arena, &.{ base, "zco" });
    {
        const r = (try zitStepTagged(gpa, io, out, abs_exe, base, env, &.{ "clone", "--no-open", url, "zco" }, "S14", citation, "zit clone")) orelse return false;
        gpa.free(r.stdout);
        gpa.free(r.stderr);
    }
    {
        const r = (try zitStepTagged(gpa, io, out, abs_exe, zit_dir, env, &.{"open"}, "S14", citation, "zit open")) orelse return false;
        gpa.free(r.stdout);
        gpa.free(r.stderr);
    }

    // One update each, with the settings already in the tree: this is the point
    // `manifest` and `empty-dirs` take effect in both tools (update.c reads
    // both before the update runs).
    if (!try fossilStepTagged(gpa, io, out, &.{ "fossil", "update" }, fos_dir, env, "S14", citation, "fossil update")) return false;
    {
        const r = (try zitStepTagged(gpa, io, out, abs_exe, zit_dir, env, &.{"update"}, "S14", citation, "zit update")) orelse return false;
        gpa.free(r.stdout);
        gpa.free(r.stderr);
    }

    try writeSettingsFixtureJunk(arena, io, fos_dir);
    try writeSettingsFixtureJunk(arena, io, zit_dir);

    const plain = (try compareListing(arena, gpa, io, out, abs_exe, env, fos_dir, zit_dir, &.{"extras"}, "extras")) orelse return false;
    const dotted = (try compareListing(arena, gpa, io, out, abs_exe, env, fos_dir, zit_dir, &.{ "extras", "--dotfiles" }, "extras --dotfiles")) orelse return false;
    if (!try expectListingShape(out, plain, dotted)) return false;
    if (!try compareCleanPreview(arena, gpa, io, out, abs_exe, env, fos_dir, zit_dir)) return false;
    if (!try compareTrees(arena, io, out, fos_dir, zit_dir)) return false;
    if (!try compareForcedAdd(arena, gpa, io, out, abs_exe, env, fos_dir, zit_dir)) return false;

    try out.print("PASS S14 ({s}): a checkout carrying every .fossil-settings name reads identically in both tools -- same extras with and without --dotfiles, same clean preview, same tree after update, same adds under --force\n", .{citation});
    return true;
}

/// zit#175 item 5 -- `add` reads the two glob lists the way fossil does: a
/// directory expansion skips ignore-glob AND clean-glob matches, `--force`
/// notwithstanding (add.c hands both to vfile_scan unconditionally); a file
/// named directly is refused when ignore-glob matches it (fossil prompts, a
/// non-tty answers no) and added silently when only clean-glob does. The
/// oracle is `changes` afterwards, byte for byte.
fn compareForcedAdd(
    arena: std.mem.Allocator,
    gpa: std.mem.Allocator,
    io: std.Io,
    out: *std.Io.Writer,
    abs_exe: []const u8,
    env: *const std.process.Environ.Map,
    fos_dir: []const u8,
    zit_dir: []const u8,
) !bool {
    const citation = "zit#175 item 5";
    for ([_][]const u8{ fos_dir, zit_dir }) |dir| {
        for ([_][2][]const u8{ .{ "sub/obj.o", "ignore-glob under sub\n" }, .{ "sub/junk.tmp", "clean-glob under sub\n" } }) |f| {
            try std.Io.Dir.cwd().writeFile(io, .{ .sub_path = try std.fs.path.join(arena, &.{ dir, f[0] }), .data = f[1] });
        }
    }
    const steps = [_][]const []const u8{
        &.{ "add", "--force", "sub" },
        &.{ "add", "x.o", "scratch.tmp" },
    };
    for (steps) |tail| {
        var fossil_argv: std.ArrayList([]const u8) = .empty;
        try fossil_argv.append(arena, "fossil");
        try fossil_argv.appendSlice(arena, tail);
        const label = try std.mem.join(arena, " ", tail);
        const f = try runFossil(gpa, io, fossil_argv.items, fos_dir, env);
        defer gpa.free(f.stdout);
        defer gpa.free(f.stderr);
        if (!okExit(f.term)) {
            try out.print("FAIL S14 ({s}): fossil {s} failed\n  stderr: {s}\n", .{ citation, label, f.stderr });
            return false;
        }
        const z = (try zitStepTagged(gpa, io, out, abs_exe, zit_dir, env, tail, "S14", citation, label)) orelse return false;
        gpa.free(z.stdout);
        gpa.free(z.stderr);
    }
    const agreed = (try compareListing(arena, gpa, io, out, abs_exe, env, fos_dir, zit_dir, &.{"changes"}, "changes")) orelse return false;
    const added = [_][]const u8{ "ADDED      sub/deep.log\n", "ADDED      scratch.tmp\n" };
    const refused = [_][]const u8{ "x.o\n", "sub/obj.o\n", "sub/junk.tmp\n" };
    for (added) |line| {
        if (std.mem.indexOf(u8, agreed, line) == null) {
            try out.print("FAIL S14 ({s}): both tools agreed, but `changes` omits {s}", .{ citation, line });
            return false;
        }
    }
    for (refused) |name| {
        if (std.mem.indexOf(u8, agreed, name) != null) {
            try out.print("FAIL S14 ({s}): both tools agreed, but `changes` lists {s}", .{ citation, name });
            return false;
        }
    }
    return true;
}

/// The S14 fixture table, shared with `src/e2e.zig` so the lane CI runs and the
/// lane fossil confirms can never build different trees.
const fixture = zit.settings_fixture;

/// Writes the fixture's `.fossil-settings/` files and its one tracked payload
/// into `dir_path`. Both checkouts get it: fossil authors it, zit receives the
/// same bytes back over the wire.
fn writeSettingsFixture(arena: std.mem.Allocator, io: std.Io, dir_path: []const u8) !void {
    try std.Io.Dir.cwd().createDirPath(io, try std.fs.path.join(arena, &.{ dir_path, ".fossil-settings" }));
    for (fixture.settings) |s| {
        const name = try std.fmt.allocPrint(arena, ".fossil-settings/{s}", .{s.name});
        try std.Io.Dir.cwd().writeFile(io, .{
            .sub_path = try std.fs.path.join(arena, &.{ dir_path, name }),
            .data = s.value,
        });
    }
    try std.Io.Dir.cwd().writeFile(io, .{
        .sub_path = try std.fs.path.join(arena, &.{ dir_path, fixture.tracked_file.name }),
        .data = fixture.tracked_file.value,
    });
}

/// The untracked files, authored identically into each checkout.
fn writeSettingsFixtureJunk(arena: std.mem.Allocator, io: std.Io, dir_path: []const u8) !void {
    for (fixture.junk_dirs) |d| {
        try std.Io.Dir.cwd().createDirPath(io, try std.fs.path.join(arena, &.{ dir_path, d }));
    }
    for (fixture.junk) |j| {
        try std.Io.Dir.cwd().writeFile(io, .{
            .sub_path = try std.fs.path.join(arena, &.{ dir_path, j.path }),
            .data = j.data,
        });
    }
}

/// Runs the SAME listing verb and flags in each checkout and requires the two
/// stdouts to be equal byte for byte. Returns the agreed output (arena-owned)
/// so the caller can assert on its content, or null when the scenario failed
/// and the failure has already been printed. `label` names the comparison.
fn compareListing(
    arena: std.mem.Allocator,
    gpa: std.mem.Allocator,
    io: std.Io,
    out: *std.Io.Writer,
    abs_exe: []const u8,
    env: *const std.process.Environ.Map,
    fos_dir: []const u8,
    zit_dir: []const u8,
    tail: []const []const u8,
    label: []const u8,
) !?[]const u8 {
    const citation = "zit#174 item 12";
    std.debug.assert(tail.len != 0);
    var fossil_argv: std.ArrayList([]const u8) = .empty;
    try fossil_argv.append(arena, "fossil");
    try fossil_argv.appendSlice(arena, tail);
    const f = try runFossil(gpa, io, fossil_argv.items, fos_dir, env);
    defer gpa.free(f.stdout);
    defer gpa.free(f.stderr);
    if (!okExit(f.term)) {
        try out.print("FAIL S14 ({s}): fossil {s} failed\n  stderr: {s}\n", .{ citation, label, f.stderr });
        return null;
    }
    const z = (try zitStepTagged(gpa, io, out, abs_exe, zit_dir, env, tail, "S14", citation, label)) orelse return null;
    defer gpa.free(z.stdout);
    defer gpa.free(z.stderr);
    if (!std.mem.eql(u8, f.stdout, z.stdout)) {
        try out.print("FAIL S14 ({s}): `{s}` differs between the two tools\n  fossil : {s}\n  zit    : {s}\n", .{ citation, label, f.stdout, z.stdout });
        try printDiffWindow(out, z.stdout, f.stdout);
        try printMismatchFooter(out, citation);
        return null;
    }
    return try arena.dupe(u8, f.stdout);
}

/// Agreement is only worth something if both tools agreed on the RIGHT set: two
/// empty listings would pass `compareListing` and prove nothing. These are the
/// rules the fixture declares, checked against the agreed output.
fn expectListingShape(out: *std.Io.Writer, plain: []const u8, dotted: []const u8) !bool {
    const citation = "zit#174 item 12";
    // Listed: the plain extra, the keep-glob match (still an extra), the
    // clean-glob match (listing does not exclude it), the nested one, and the
    // UTF-16 file.
    const listed = [_][]const u8{ "notes.txt\n", "precious.dat\n", "scratch.tmp\n", "sub/deep.log\n", "wide.wide\n" };
    // Excluded: the four ignore-glob matches -- one per separator form -- the
    // manifest stamps this update wrote, and fossil's reserved `.fos` name.
    const excluded = [_][]const u8{ "x.o\n", "build/gen.c\n", "logs/run.log\n", "spaced name.txt\n", "manifest\n", "manifest.uuid\n", ".fos\n" };
    for ([_][]const u8{ plain, dotted }, [_][]const u8{ "extras", "extras --dotfiles" }) |text, label| {
        for (listed) |name| {
            if (std.mem.indexOf(u8, text, name) == null) {
                try out.print("FAIL S14 ({s}): both tools agreed, but `{s}` omits {s}", .{ citation, label, name });
                return false;
            }
        }
        for (excluded) |name| {
            if (std.mem.indexOf(u8, text, name) != null) {
                try out.print("FAIL S14 ({s}): both tools agreed, but `{s}` lists {s}", .{ citation, label, name });
                return false;
            }
        }
    }
    // `dotfiles off` is the whole difference between the two runs.
    if (std.mem.indexOf(u8, plain, ".hidden\n") != null or std.mem.indexOf(u8, dotted, ".hidden\n") == null) {
        try out.print("FAIL S14 ({s}): --dotfiles did not change what either tool listed\n  plain  : {s}\n  dotted : {s}\n", .{ citation, plain, dotted });
        return false;
    }
    return true;
}

/// fossil's `clean -n` prose prefix, stripped so the two previews can be
/// compared as the candidate sets they both are (checkin.c `clean_cmd`).
const fossil_clean_prefix = "Removed unmanaged file: ";

/// `clean -n` in fossil against `clean --dry-run` in zit: same candidates,
/// different line text. Both are normalized to a sorted bare-path list first.
fn compareCleanPreview(
    arena: std.mem.Allocator,
    gpa: std.mem.Allocator,
    io: std.Io,
    out: *std.Io.Writer,
    abs_exe: []const u8,
    env: *const std.process.Environ.Map,
    fos_dir: []const u8,
    zit_dir: []const u8,
) !bool {
    const citation = "zit#174 item 12";
    const f = try runFossil(gpa, io, &.{ "fossil", "clean", "-n" }, fos_dir, env);
    defer gpa.free(f.stdout);
    defer gpa.free(f.stderr);
    if (!okExit(f.term)) {
        try out.print("FAIL S14 ({s}): fossil clean -n failed\n  stderr: {s}\n", .{ citation, f.stderr });
        return false;
    }
    const z = (try zitStepTagged(gpa, io, out, abs_exe, zit_dir, env, &.{ "clean", "--dry-run" }, "S14", citation, "clean --dry-run")) orelse return false;
    defer gpa.free(z.stdout);
    defer gpa.free(z.stderr);

    const f_norm = try normalizeCleanPreview(arena, f.stdout, fossil_clean_prefix);
    const z_norm = try normalizeCleanPreview(arena, z.stdout, "");
    if (!std.mem.eql(u8, f_norm, z_norm)) {
        try out.print("FAIL S14 ({s}): the clean previews name different files\n  fossil : {s}\n  zit    : {s}\n", .{ citation, f_norm, z_norm });
        try printMismatchFooter(out, citation);
        return false;
    }
    // The clean-glob match is a candidate under `-n` (fossil skips the
    // clean-glob test in dry run); the keep-glob match never is.
    if (std.mem.indexOf(u8, f_norm, "scratch.tmp\n") == null or std.mem.indexOf(u8, f_norm, "precious.dat\n") != null) {
        try out.print("FAIL S14 ({s}): both previews agreed, but on the wrong set\n  {s}", .{ citation, f_norm });
        return false;
    }
    return true;
}

/// Sorted, newline-joined bare paths from a clean preview: every non-empty line
/// with `prefix` removed. Bounded by the input's own line count.
fn normalizeCleanPreview(arena: std.mem.Allocator, text: []const u8, prefix: []const u8) ![]u8 {
    var lines: std.ArrayList([]const u8) = .empty;
    var it = std.mem.splitScalar(u8, text, '\n');
    while (it.next()) |raw| {
        const line = std.mem.trimEnd(u8, raw, "\r");
        if (line.len == 0) continue;
        if (prefix.len != 0 and !std.mem.startsWith(u8, line, prefix)) continue;
        try lines.append(arena, line[prefix.len..]);
    }
    std.mem.sort([]const u8, lines.items, {}, lessThanPath);
    var aw = std.Io.Writer.Allocating.init(arena); // arena-owned; no deinit
    for (lines.items) |line| try aw.writer.print("{s}\n", .{line});
    return aw.toOwnedSlice();
}

fn lessThanPath(_: void, a: []const u8, b: []const u8) bool {
    return std.mem.lessThan(u8, a, b);
}

/// Both post-update trees, rendered as one sorted path per line, and required
/// to be equal byte for byte.
fn compareTrees(
    arena: std.mem.Allocator,
    io: std.Io,
    out: *std.Io.Writer,
    fos_dir: []const u8,
    zit_dir: []const u8,
) !bool {
    const citation = "zit#174 item 12";
    const f = try listTree(arena, io, fos_dir);
    const z = try listTree(arena, io, zit_dir);
    if (!std.mem.eql(u8, f, z)) {
        try out.print("FAIL S14 ({s}): the two trees differ after update\n  fossil :\n{s}  zit    :\n{s}", .{ citation, f, z });
        try printMismatchFooter(out, citation);
        return false;
    }
    // The two settings that write into the tree must actually have fired, or
    // the comparison above proves only that both tools did nothing.
    if (std.mem.indexOf(u8, f, "manifest.uuid\n") == null or std.mem.indexOf(u8, f, "emptyd/\n") == null) {
        try out.print("FAIL S14 ({s}): neither tree carries the manifest stamps and the empty-dirs directory\n{s}", .{ citation, f });
        return false;
    }
    return true;
}

/// Every path under `root_path`, one per line, ascending, directories marked
/// with a trailing `/`. Each tool's own checkout-state names are elided so the
/// two listings are comparable at all; everything else -- including the
/// `.fos` junk both tools hide from `extras` -- is elided by the same rule in
/// both trees.
fn listTree(arena: std.mem.Allocator, io: std.Io, root_path: []const u8) ![]u8 {
    var paths: std.ArrayList([]const u8) = .empty;
    var budget: usize = tree_listing_max_entries;
    try walkTree(arena, io, root_path, "", 0, &paths, &budget);
    std.mem.sort([]const u8, paths.items, {}, lessThanPath);
    var aw = std.Io.Writer.Allocating.init(arena); // arena-owned; no deinit
    for (paths.items) |p| try aw.writer.print("{s}\n", .{p});
    return aw.toOwnedSlice();
}

/// Hard ceilings on the walk: the fixture is a couple of dozen entries two
/// levels deep, so either limit being reached means the fixture, not the tree,
/// is wrong.
const tree_listing_max_entries: usize = 512;
const tree_listing_max_depth: usize = 8;

fn walkTree(
    arena: std.mem.Allocator,
    io: std.Io,
    dir_path: []const u8,
    prefix: []const u8,
    depth: usize,
    paths: *std.ArrayList([]const u8),
    budget: *usize,
) !void {
    if (depth >= tree_listing_max_depth) return error.TreeTooDeep;
    var dir = try std.Io.Dir.cwd().openDir(io, dir_path, .{
        .access_sub_paths = true,
        .iterate = true,
        .follow_symlinks = false,
    });
    defer dir.close(io);
    var it = dir.iterate();
    while (try it.next(io)) |entry| {
        if (budget.* == 0) return error.TreeTooLarge;
        budget.* -= 1;
        if (depth == 0 and isCheckoutStateName(entry.name)) continue;
        const rel = if (prefix.len == 0)
            try arena.dupe(u8, entry.name)
        else
            try std.fmt.allocPrint(arena, "{s}/{s}", .{ prefix, entry.name });
        if (entry.kind == .directory) {
            try paths.append(arena, try std.fmt.allocPrint(arena, "{s}/", .{rel}));
            try walkTree(arena, io, try std.fs.path.join(arena, &.{ dir_path, entry.name }), rel, depth + 1, paths, budget);
        } else {
            try paths.append(arena, rel);
        }
    }
}

/// Root-level names one tool owns and the other does not: fossil's checkout
/// database family (`_FOSSIL_`, `.fslckout`, the deprecated `.fos`, each with
/// its SQLite `-journal`/`-wal`/`-shm` siblings, add.c's reserved list) and
/// zit's `.zit*` container roots (`control_paths.reserved`). `.fossil-settings`
/// is tracked content and is deliberately NOT matched by the `.fos` arm.
fn isCheckoutStateName(name: []const u8) bool {
    if (std.mem.eql(u8, name, ".zit") or std.mem.startsWith(u8, name, ".zit-")) return true;
    if (std.mem.startsWith(u8, name, ".fslckout")) return true;
    if (std.mem.startsWith(u8, name, "_FOSSIL_")) return true;
    if (std.mem.eql(u8, name, ".fos") or std.mem.startsWith(u8, name, ".fos-")) return true;
    return false;
}