main
Drew Owens 0.9.0: bridge data moved out of the install cache (recovery survives updates), closed-in-place records, gates route thr… 5d02ca9 20d ago
//! ADVERSARIAL REVIEW OF 0.8.8 — pass thirty-eight, the convergence pass.
//!
//! Master is green at 5f2a9df: 400 pass, 0 fail on a clean checkout, verified
//! first.
//!
//! (2) THE REFUSED-SWEEP FIXTURE PINS THE MANIFEST, NOT JUST THE REPLY, and it
//!     does it properly: `a_refused_document_is_recorded_as_refused_in_the_manifest`
//!     reads `manifest.json` off disk and asserts `verified == false`, a
//!     `verdictReason` of real length, and `closed == false` per entry — then
//!     follows through to `swx_recover_list` and asserts the listing carries
//!     `verified:false` and never says "rescue". One gap, small: it does not
//!     assert `source == "manifest"` on those entries, so a refusal that
//!     arrived from the `reverified` path instead would satisfy it. The
//!     neighbouring test asserts `source` for the preserved case; adding the
//!     same line here costs nothing.

mod common;
use common::*;
use serde_json::json;
use std::path::Path;

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

/// SEVERITY 1, CONSENT — "FOLLOWED" IS A SHORTER LIST THAN THE FILES AN AGENT
/// FOLLOWS.
///
/// MEASURED, planting the same sentence in two places and counting offenders:
///
/// ```text
///                                    unquoted   blockquoted
/// docs/RUNBOOK.md                       1            0
/// skills/swx-drive/SKILL.md             0            0
/// ```
///
/// Two different holes with one cause.
///
/// `docs/` strips quotes, which is right for `QUIT-INVESTIGATION.md` and the
/// build logs — they describe. It is wrong for a runbook, and `docs/` is exactly
/// where a runbook would go. The per-file policy asks "which file is this?" and
/// answers by directory, but the property that matters is whether the prose
/// INSTRUCTS or DESCRIBES, and both kinds live in `docs/`.
///
/// `skills/**` is not scanned AT ALL — the unquoted sentence there produces zero
/// offenders, so this is not stripping, it is absence. The pin walks `src/`,
/// `tools/`, `docs/` plus two root files. `skills/<name>/SKILL.md` is the
/// standard layout for sub-skills in this organisation, and a skillpack is
/// precisely a set of files written to be loaded and followed. Today the
/// directory does not exist, so this is latent; the day it does, the most
/// followed files in the repository are outside the rule that governs them.
///
/// THE LIST, and the shape I would argue for: invert the default. Scan
/// everything, strip nothing, and carry an explicit allowlist of files that
/// DESCRIBE — `README.md` and the named `docs/*.md` reports. A file added later
/// then defaults to being governed rather than silently exempt, which is the
/// direction this project chooses everywhere else it has a default: unknown kind
/// takes the strictest floor, an unreadable census is not permission, a document
/// of unknown ownership is not closed. The one place a new unknown currently
/// defaults to "allowed" is the rule about asking Drew to close his documents.
#[test]
fn every_file_an_agent_may_follow_is_governed_by_the_rule() {
    let root = Path::new(env!("CARGO_MANIFEST_DIR"));
    let pin = std::fs::read_to_string(root.join("tests/wiring.rs")).expect("wiring.rs");
    let body = pin
        .split("fn nothing_asks_the_user_to_close_documents")
        .nth(1)
        .expect("the pin exists")
        .chars()
        .take(4000)
        .collect::<String>();

    // Is the walk rooted at the repository, or at a hand-listed set of folders?
    let walks_everything = body.contains("stack = vec![root.to_path_buf()]")
        || body.contains("read_dir(root)")
        || body.contains("walk(root)");
    // Is the exemption an allowlist of files that DESCRIBE, rather than a
    // directory rule?
    let describes_allowlist = body.contains("DESCRIBES")
        || body.contains("describes")
        || body.contains("allowlist");
    let scans_skills = body.contains("skills");

    assert!(
        (walks_everything || scans_skills) && describes_allowlist,
        "the pin walks src/, tools/, docs/ and two root files. Measured: a blockquoted ask in \
         docs/RUNBOOK.md produces 0 offenders (stripped, though a runbook INSTRUCTS), and the \
         same sentence UNQUOTED in skills/swx-drive/SKILL.md produces 0 offenders (not scanned at \
         all). skills/<name>/SKILL.md is the standard sub-skill layout here and a skillpack is by \
         definition written to be followed. Invert the default: scan everything, strip nothing, \
         and allowlist the files that DESCRIBE — README.md and the named docs reports. Then a new \
         file defaults to governed, which is the direction every other default in this project \
         takes. (walks everything: {walks_everything}, scans skills: {scans_skills}, describes \
         allowlist: {describes_allowlist})"
    );
}

/// SEVERITY 2 — THE ONE SURVIVING MUTANT: `hostError` IS PINNED BY PRESENCE, NOT
/// BY CONTENT.
///
/// Full mutation table over every non-`cfg(windows)` guard added since 0.7.8,
/// re-run this pass:
///
/// ```text
/// acknowledge_only   len()==1 -> >=1                    CAUGHT (2)
/// write_manifest     verified -> always true            CAUGHT (1)
/// recoveryBytes      Changed -> Same                    CAUGHT (2)
/// recoveryBytes      Unbindable -> Same (trusted)       CAUGHT (1)
/// unknown-kind recovery floor -> 0                      CAUGHT (2)
/// estimate floor     max -> min                         CAUGHT (3)
/// import_diagnostics -> unknown                         CAUGHT (1)
/// reverified_entry   unknown -> true                    CAUGHT (2)
/// host_failure_entry drops e.data                       *** SURVIVES ***
/// ```
///
/// Eight of nine now bite, including the two that survived pass 37 — the
/// refused-document fixture earned its keep. The ninth is the fix for my own
/// pass-30 finding, and the reason it survives is visible in its test:
///
/// ```ignore
/// // THE FIELD ITSELF must exist — it can be null because the host sent
/// // nothing, but it must not be ABSENT because nothing constructs it.
/// check!(b, "...and carries the structured failure field", e.get("hostError").is_some(), …);
/// ```
///
/// That reasoning is right about the fixture it has: `mock-com-timeout` produces
/// a `SidecarError` with no `data`, so `hostError` is legitimately null and the
/// only thing the test CAN assert is presence. But `Value::Null` is present, so
/// replacing `e.data.clone()` with `Value::Null` — throwing the structure away
/// entirely — passes. The guard cannot distinguish "the host sent nothing" from
/// "we discarded what it sent", which is the exact distinction it exists to make.
///
/// What is missing is a fault, not an assertion: a sweep-path failure that
/// carries structured COM data, the way `mock-import-load4-throws` does for the
/// import. This test fails today because no fixture in the suite produces a
/// non-null `hostError` from a sweep — so the data path has never once been
/// exercised end to end.
#[test]
fn a_sweep_failure_carrying_com_detail_delivers_it() {
    let scratch = TempDir::new("adv36-hostdata");
    std::fs::write(scratch.join("mock-com-timeout"), "").unwrap();
    let machine = TempDir::new("adv36-hostdata-machine");
    std::fs::write(machine.join("windows.json"), "[]").unwrap();
    let b = Bridge::start_in(
        TempDir::borrowed(&scratch.0),
        &[("SWX_LAUNCH_FAKE_DIR", &machine.path())],
    );

    let r = b.call("swx_close_all_safely", json!({"reason": "closing"}));
    let failed = r["closeFailed"].as_array().cloned().unwrap_or_default();
    assert!(!failed.is_empty(), "precondition: the host failed the sweep: {}", s(&r));
    let e = &failed[0];
    assert!(
        e.get("hostError").is_some(),
        "precondition: the field is constructed (this much IS pinned): {}",
        s(e)
    );

    assert!(
        !e["hostError"].is_null(),
        "no fixture in this suite produces a sweep failure carrying structured COM detail, so \
         `hostError` is null in every test that touches it — and Value::Null satisfies the \
         `.is_some()` pin. Replacing `e.data.clone()` with `Value::Null` in host_failure_entry \
         passes all 400 tests: the guard cannot tell \"the host sent nothing\" from \"we threw \
         away what it sent\", which is the distinction it was built for. The gap is a FAULT, not \
         an assertion — mock-import-load4-throws already shows the shape for the import path. \
         Entry: {}",
        s(e)
    );
}