haematite-cli 0.21.7

The haematite command: serve a store, its door and Studio, answer MCP, query with jq, and host the store's assistant seat
Documentation
use std::path::PathBuf;

use haematite::db::vacuum::{
    DebrisTotals, EntryKind, IndexCheckpointReport, MalformedEntry, ManifestEntryReport,
    ManifestReport, MarkTallies, MetadataOrigin, MetadataReport, MetadataSourceKind,
    MetadataSourceReport, Mode as VacuumMode, NodeTotals, Report as VacuumReport, ShardPresence,
    ShardReport, SweepBlocker, TornTailFileReport, UninventoriedEntry, WalTailReport,
};

/// The one shard of [`sample_report`], with every field populated.
fn sample_shard() -> ShardReport {
    ShardReport {
        shard_id: 0,
        presence: ShardPresence::Materialised,
        wal_committed_root: false,
        nodes: NodeTotals {
            enumerated_nodes: 3,
            enumerated_bytes: 300,
            marked_nodes: 1,
            marked_bytes: 100,
            unmarked_nodes: 2,
            unmarked_bytes: 200,
        },
        temp_debris_files: 1,
        temp_debris_bytes: 11,
        index_checkpoint: Some(IndexCheckpointReport { size_bytes: 4_096 }),
        index_temp_debris_files: 1,
        index_temp_debris_bytes: 13,
        torn_tails: vec![TornTailFileReport {
            path: PathBuf::from(concat!(
                "/db/shard-0/shard.wal.torn-64-8-",
                "0000000000000000000000000000000000000000000000000000000000000000",
            )),
            offset: 64,
            length: 8,
        }],
        wal_tail: Some(WalTailReport::TornLast {
            offset: 128,
            length: 9,
        }),
        malformed: vec![MalformedEntry {
            path: PathBuf::from("/db/shard-0/store/zz"),
            reason: "strange".to_owned(),
        }],
        roots_moved: true,
    }
}

/// A report with every section populated.
fn sample_report() -> VacuumReport {
    VacuumReport {
        mode: VacuumMode::Stats,
        data_dir: PathBuf::from("/db"),
        shard_count: 2,
        duration_ms: 7,
        lock_anchor_created: true,
        beside_writer: true,
        moving_shards: vec![1],
        never_materialised_shards: 1,
        torn_tail_count: 1,
        debris: DebrisTotals {
            files: 2,
            bytes: 64,
        },
        shards: vec![sample_shard()],
        totals: NodeTotals::default(),
        metadata: MetadataReport {
            manifest: Some(ManifestReport {
                path: PathBuf::from("/db/metadata-manifest.json"),
                generation: 4,
                entries: vec![ManifestEntryReport {
                    path: PathBuf::from("/db/branches"),
                    kind: MetadataSourceKind::RefsDir,
                    state: "Publishing".to_owned(),
                    source_readable: false,
                }],
            }),
            attestation: true,
            sources: vec![MetadataSourceReport {
                path: PathBuf::from("/elsewhere/refs"),
                kind: MetadataSourceKind::RefsDir,
                origin: MetadataOrigin::Supplied,
                records: 2,
                temp_debris_files: 1,
                foreign_entries: 1,
            }],
            supplied_missing: vec![PathBuf::from("/typo/refs")],
        },
        marks: MarkTallies {
            history_roots: 0,
            wal_roots: 1,
            branch_anchors: 2,
            branch_heads: 2,
            branch_kept_heads: 0,
            snapshot_roots: 1,
        },
        verified_nodes: 42,
        uninventoried: vec![UninventoriedEntry {
            name: "stray.bin".to_owned(),
            kind: EntryKind::File,
            size_bytes: Some(9),
        }],
        sweep_blockers: vec![
            SweepBlocker::UnreconciledWalTail {
                shard_id: 0,
                tail: WalTailReport::TornLast {
                    offset: 128,
                    length: 9,
                },
            },
            SweepBlocker::MetadataUnattested,
            SweepBlocker::SourceUnreadable {
                path: PathBuf::from("/db/locked"),
                detail: "permission denied".to_owned(),
            },
        ],
        sweep: None,
        trust_boundary: "the boundary text".to_owned(),
    }
}

#[test]
fn writer_refused_tail_is_distinct_from_a_cut_owed() -> Result<(), std::fmt::Error> {
    let mut report = sample_report();
    let tail = WalTailReport::Overrun {
        offset: 128,
        length: 9,
    };
    report.shards[0].wal_tail = Some(tail);
    report.sweep_blockers[0] = SweepBlocker::UnreconciledWalTail { shard_id: 0, tail };
    let text = super::render(&report)?;
    assert!(text.contains("WRITER OPEN REFUSES: Overrun · offset 128 · length 9 bytes"));
    assert!(text.contains("writer open refuses this tail and sweep refuses without deleting"));
    assert!(!text.contains("sweep blockers: none observed"));
    assert!(!text.contains("CUT OWED"));
    Ok(())
}

/// Field-parity check: a report with every section populated renders
/// every fact — the human output must not know less than the JSON.
#[test]
fn rendering_carries_every_report_section() -> Result<(), std::fmt::Error> {
    let text = super::render(&sample_report())?;
    for needle in [
        "report-only",
        "/db",
        "shard count: 2",
        "torn tails: 1 set-aside file(s)",
        "install debris: 2 file(s), 64 bytes",
        "SET-ASIDE: /db/shard-0/shard.wal.torn-64-8-",
        "offset 64 · length 8 bytes",
        "CUT OWED: offset 128 · length 9 bytes",
        "shard-0 WAL has a TornLast tail at byte 128 (9 bytes)",
        "sweep refuses without deleting; a writable open must preserve and cut the tail",
        "7 ms",
        "created one empty anchor",
        "shard-0",
        "NO committed root",
        "read BESIDE a live writer",
        "left out of the totals): shards [1]",
        "roots MOVED during the read",
        "debris 1",
        "MALFORMED: /db/shard-0/store/zz — strange",
        "generation 4",
        "state Publishing",
        "MISSING/UNREADABLE",
        "attestation (sweep-only, ignored by stats): asserted",
        "/elsewhere/refs",
        "2 record(s)",
        "1 temp debris",
        "1 foreign entries",
        "SUPPLIED PATH MISSING: /typo/refs",
        "WAL roots 1",
        "hash verifications: 42",
        "stray.bin",
        "not positively established",
        "could not be read (permission denied)",
        "the boundary text",
    ] {
        assert!(
            text.contains(needle),
            "rendering must carry {needle:?}:\n{text}"
        );
    }
    Ok(())
}