main
Drew Owens 0.9.10 source: presence baseline holds only bridge-put documents (adversary pass 46); manifest as published bf99cca 19d ago
//! ADVERSARIAL REVIEW OF 0.7.7 — pass twenty-nine.
//!
//! The listing is right about the case it was built for: a husk with no marker
//! is now `verified:false`, is never called a rescue, and says the document was
//! left open. Pass 28 is closed.
//!
//! ON THE CIRCULARITY CLAIM — I agree with it for every file except the one
//! class 0.7.6 introduced, and that exception matters.
//!
//! ```ignore
//! // The KIND from the extension is legitimate here and is not the 0.7.5
//! // circularity: this verb did not choose the name, it is reading a directory.
//! ```
//!
//! Who READS the name is not what made 0.7.5 circular; where the name CAME FROM
//! is. For a `.SLDPRT` written because `GetType` said part, the extension
//! records an independent observation and reading it back is sound. But a
//! `.SLDPRT` written by `namingFallback: "unknownKindNamedAsPart"` carries the
//! opposite: the sweep wrote that extension **because it could not establish the
//! kind**. The save verified it with `kind = None` against
//! `unknown_kind_floor()` — 256 KB. The listing reads `.SLDPRT`, gets
//! `DocKind::Part`, and applies 24 KB. Ten times weaker, on precisely the files
//! whose kind is unknown, from a name the failure produced. That is the same
//! shape as 0.7.4, one hop further downstream.
//!
//! AND THE WEAKNESS IS NOT SYMMETRIC THE WAY THE COMMENT ARGUES:
//!
//! ```ignore
//! // That makes this check WEAKER than the one at save time and never stricter,
//! // so nothing that was truly preserved can be reported as unverified here.
//! ```
//!
//! True, and it is the harmless half. A weaker check cannot produce a false
//! NEGATIVE; it is exactly what produces a false POSITIVE — `verified:true` for
//! a file the sweep refused. 0.7.6 said nothing about these files. 0.7.7 asserts
//! something about them, and an assertion that is wrong is worse than silence on
//! the day it is read.

mod common;
use common::*;
use serde_json::json;

fn s(v: &serde_json::Value) -> String {
    serde_json::to_string_pretty(v).unwrap_or_default()
}

fn a_visible_machine(tag: &str) -> TempDir {
    let d = TempDir::new(tag);
    std::fs::write(d.join("windows.json"), "[]").unwrap();
    d
}

fn a_difficult_session(scratch: &TempDir) {
    std::fs::write(
        scratch.join("mock-solidworks-open-docs.json"),
        json!({
            "running": true, "untitled": 3, "active": "untitled:a",
            "docs": [
                {"docType": "part", "path": "C:\\Users\\drew\\work\\bracket.SLDPRT",
                 "title": "bracket.SLDPRT", "saved": true, "dirty": true},
                {"docType": "part", "path": "untitled:a", "title": "First.step.SLDPRT",
                 "saved": false, "dirty": true},
                {"docType": "part", "path": "untitled:b", "title": "First.step.SLDPRT",
                 "saved": false, "dirty": true},
                {"docType": "assembly", "path": "C:\\Users\\drew\\work\\gearbox.SLDASM",
                 "title": "gearbox.SLDASM", "saved": true, "dirty": false},
                {"docType": "part", "path": "C:\\Users\\drew\\work\\shaft.SLDPRT",
                 "title": "shaft.SLDPRT", "saved": true, "dirty": false,
                 "heldOpenByAssembly": true}
            ],
            "files": {}
        })
        .to_string(),
    )
    .unwrap();
}


/// A FILE THE SWEEP REFUSED FOR SIZE IS REPORTED AS VERIFIED.
///
/// Reachable two ways, both through the unknown-kind branch:
///
///   * `≥ original`. An unknown-kind document whose own file was 4 MB is
///     rescued at 300 KB. Save time: floor = `max(256 KB, 4 MB)` = 4 MB →
///     REFUSED, `preserved:false`, document left OPEN. Listing: the original
///     size is not knowable from a directory, so the floor alone applies —
///     300 KB clears 256 KB — `verified:true`, and `howToOpen` says "The bytes
///     verify as a document; only the name is unknown."
///
///   * the naming fallback. An unknown-kind document saved as `.SLDPRT` at
///     30 KB. Save time: `kind = None` → 256 KB floor → REFUSED. Listing:
///     `.SLDPRT` → Part → 24 KB → `verified:true`, `howToOpen: "swx_open this
///     path."`
///
/// In both, the sweep's record says the document is still open and the listing
/// says its work is safely on disk. The user reading the listing is the user
/// whose sweep just failed.
///
/// The floor-only weakening cannot be patched by making the listing stricter —
/// it genuinely cannot know the original size, and for the naming fallback it
/// cannot know the extension was a guess. Both facts existed at save time and
/// were thrown away. The fix is the durable one: the sweep writes its record
/// beside the files it wrote — a `manifest.json` per recovery folder — and the
/// listing reports what the sweep decided, falling back to re-verification only
/// for a folder with no manifest. That also makes `namingFallback` visible to
/// the reader, which nothing downstream can currently see.
#[test]
fn a_file_the_sweep_refused_is_not_reported_as_verified() {
    let scratch = TempDir::new("adv28-refused");
    let b = Bridge::start_in(TempDir::borrowed(&scratch.0), &[]);

    let root = scratch.join("recovery").join("2026-09-12T00-00-00");
    std::fs::create_dir_all(&root).unwrap();
    let mut body = vec![0u8; 300_000];
    body[4..8].copy_from_slice(&[0x00, 0x00, 0x00, 0x04]);
    // Unknown kind, rescued at 300 KB from a document whose own file was 4 MB.
    // The sweep refused this: max(256 KB, 4 MB) = 4 MB. The document is OPEN.
    std::fs::write(root.join("Assem1.sldrecover"), &body).unwrap();

    let r = b.call("swx_recover_list", json!({}));
    let files = r["sweeps"][0]["files"].as_array().cloned().unwrap_or_default();
    assert_eq!(files.len(), 1, "precondition: the file is listed: {}", s(&r));
    let f = &files[0];
    assert!(
        f["signatureOk"] == json!(true),
        "precondition: this fails on SIZE, not on the marker: {}",
        s(f)
    );

    assert!(
        f["verified"] != json!(true),
        "the listing reports verified:true for a file the sweep REFUSED and whose document it \
         left OPEN. At save time the unknown-kind floor was max(256 KB, original) — 4 MB for this \
         document — and the listing applies 256 KB because a directory cannot tell it what the \
         original weighed. A weaker check cannot produce a false negative; this is the false \
         POSITIVE it produces instead, and it is the direction that costs the user. Entry: {}",
        s(f)
    );
}

/// THE SWEEP DOES NOT WRITE DOWN WHAT IT DECIDED.
///
/// Everything the listing has to guess at — the original document's size, the
/// kind `GetType` reported, whether the extension was a guess — was in hand at
/// save time and is discarded when the process ends. The recovery folder holds
/// files and nothing else, so the listing is left re-deriving a verdict from
/// strictly less evidence than the one it is trying to reproduce, and the gap
/// between them is exactly the two false positives above.
///
/// A `manifest.json` written by the sweep into its own folder closes both, needs
/// no new judgement, and survives the process that made it — which matters most
/// here, because a sweep that ran during a crash is the one whose files someone
/// reads later from a fresh bridge.
#[test]
fn a_sweep_records_its_verdicts_beside_the_files_it_wrote() {
    let scratch = TempDir::new("adv28-manifest");
    a_difficult_session(&scratch);
    let machine = a_visible_machine("adv28-manifest-machine");
    let b = Bridge::start_in(
        TempDir::borrowed(&scratch.0),
        &[("SWX_LAUNCH_FAKE_DIR", &machine.path())],
    );
    b.call("swx_start", json!({}));
    // BUILDER NOTE (0.9.10): the documents here were not opened by the bridge, so
    // the presence guard counts them as the user's; the override stands in for
    // the user's explicit request to sweep them.
    let swept = b.call("swx_close_all_safely", json!({"userPresentOverride": true, "userAuthorized": true, "reason": "closing"}));
    assert!(swept["done"] == json!(true), "precondition: the sweep ran: {}", s(&swept));

    let listed = b.call("swx_recover_list", json!({}));
    let dir = listed["sweeps"][0]["directory"].as_str().unwrap_or_default().to_string();
    assert!(!dir.is_empty(), "precondition: a sweep folder exists: {}", s(&listed));

    let names: Vec<String> = std::fs::read_dir(&dir)
        .map(|e| {
            e.flatten().map(|f| f.file_name().to_string_lossy().to_string()).collect()
        })
        .unwrap_or_default();

    assert!(
        names.iter().any(|n| n.to_ascii_lowercase().contains("manifest")),
        "the sweep wrote {} file(s) into {dir} and no record of what it decided about any of \
         them. The original sizes, the kinds GetType reported and whether an extension was a \
         guess all existed at save time; the listing has to re-derive a verdict without them, \
         and the two facts it cannot recover are exactly where it reports verified:true for a \
         refused file. Files present: {names:?}",
        names.len()
    );
}