#!/usr/bin/env python3
"""GDR-8050L firmware READ-window path probe / proof-gap audit.

This helper is intentionally non-authoritative.  It ties together the v230 live
trace, the offline firmware READ CDB CPU sweep, and a small direct-operand scan
of the firmware image.  The goal is to prevent over-promoting the current bridge
READ-window mapping: host READ LBA ingestion is firmware-derived, but the
unlocked media-window delta is still selected by the bridge/media profile.

No firmware is bundled by this helper.  It resolves an analyzer-produced
firmware path from the config or accepts --firmware.
"""
from __future__ import annotations

import argparse
import csv
import json
import os
import re
from pathlib import Path
from typing import Any

try:
    from hlds_gdr_firmware_lba_cpu_smoke import (
        EXPECTED_GDR8050L_0L23_BASENAME,
        resolve_firmware,
        load_json,
        write_json,
        parse_int,
    )
except Exception:  # pragma: no cover - standalone fallback
    EXPECTED_GDR8050L_0L23_BASENAME = "GDR-8050L_0L23_5af3ae9b_firmware_4c61eb12_plaintext_7cab0f5c.bin"
    def load_json(path: Path) -> dict[str, Any]:
        with path.open("r", encoding="utf-8") as f:
            return json.load(f)
    def write_json(path: Path, obj: Any) -> None:
        path.parent.mkdir(parents=True, exist_ok=True)
        with path.open("w", encoding="utf-8") as f:
            json.dump(obj, f, indent=2)
            f.write("\n")
    def parse_int(v: Any, default: int = 0) -> int:
        if v is None or v == "":
            return default
        if isinstance(v, int):
            return v
        return int(str(v), 0)
    def resolve_firmware(config: dict[str, Any], config_path: Path, firmware_arg: str | None) -> str:
        if firmware_arg:
            return firmware_arg
        for key in ("custom_bin", "proof_firmware_image"):
            if config.get(key):
                return str(config.get(key))
        return EXPECTED_GDR8050L_0L23_BASENAME

VERSION = "v256"
ROM_BASE = 0x90000000

READ_CDB_LBA_STORE_PC = "0x9001E541"
READ_CDB_LBA_SRAM = 0x05EC
D108_DATA_PHASE_PC = "0x9001CD59"
MAIN_LOOP_PC = "0x9000606B"
EXPECTED_DESCRIPTOR_COPY_PC = "0x9001B5EF"
EXPECTED_DESCRIPTOR_DEST = 0x06E4

# Direct short absolute operand byte patterns observed in the current MN103S
# disassembly.  These are not a full decoder; they are an intentional narrow
# audit for the SRAM state cells relevant to the READ path.
REG_NAMES = {0: "d0", 1: "d1", 2: "d2", 3: "d3"}
PATTERN_GROUPS: dict[str, list[dict[str, Any]]] = {}

def _add_pattern(group: str, op: str, addr: int, pat: bytes, reg: str) -> None:
    PATTERN_GROUPS.setdefault(group, []).append({"op": op, "addr": addr, "pattern": pat, "reg": reg})

# dword direct reads: 0x30 + reg, lo, hi  -> mov (abs16), Dn
# dword direct writes: 0x01 + 4*reg, lo, hi -> mov Dn,(abs16)
for addr, group in [
    (0x05EC, "assembled_lba_05ec"),
    (0x0760, "current_lba_0760"),
    (0x076C, "transfer_counter_076c"),
    (0x077C, "transfer_counter_077c"),
    (0x06E4, "descriptor_lba_06e4"),
]:
    lo, hi = addr & 0xFF, (addr >> 8) & 0xFF
    for r in range(4):
        _add_pattern(group, "read_dword", addr, bytes([0x30 + r, lo, hi]), REG_NAMES[r])
        _add_pattern(group, "write_dword", addr, bytes([0x01 + (r * 4), lo, hi]), REG_NAMES[r])

# byte direct reads/writes used as state flags: movbu (abs16),Dn / movbu Dn,(abs16)
for addr, group in [
    (0x05D0, "cdb_shadow_05d0"),
    (0x05F0, "packet_state_05f0"),
    (0x06F1, "packet_phase_06f1"),
    (0x0778, "lba_state_0778"),
    (0x0780, "transfer_state_0780"),
    (0x0A8D, "host_gate_0a8d"),
    (0xD108, "host_if_d108"),
    (0xD109, "host_if_d109"),
    (0xD954, "host_status_d954"),
]:
    lo, hi = addr & 0xFF, (addr >> 8) & 0xFF
    for r in range(4):
        _add_pattern(group, "read_byte", addr, bytes([0x34 + r, lo, hi]), REG_NAMES[r])
        _add_pattern(group, "write_byte", addr, bytes([0x02 + (r * 4), lo, hi]), REG_NAMES[r])


def norm_pc(v: Any) -> str:
    try:
        return f"0x{parse_int(v) & 0xFFFFFFFF:08X}"
    except Exception:
        s = str(v or "")
        return s.upper().replace("0X", "0x")


def parse_notes(notes: str) -> dict[str, str]:
    out: dict[str, str] = {}
    for part in str(notes or "").split(";"):
        if "=" not in part:
            continue
        k, v = part.split("=", 1)
        out[k.strip()] = v.strip()
    return out


def read_jsonl(path: Path) -> list[dict[str, Any]]:
    rows = []
    if not path or not path.exists():
        return rows
    with path.open("r", encoding="utf-8") as f:
        for line in f:
            line = line.strip()
            if not line:
                continue
            try:
                rows.append(json.loads(line))
            except Exception:
                continue
    return rows


def live_summary(jsonl: Path) -> dict[str, Any]:
    rows = read_jsonl(jsonl)
    scsi = [r for r in rows if "cdb_hex" in r]
    reads = [r for r in scsi if str(r.get("cdb_hex", "")).upper().startswith(("28 ", "A8 ", "BE "))]
    unlocked = [r for r in reads if str(r.get("dynamic_media_auth_stage", "")).lower() == "unlocked"]
    locked = [r for r in reads if str(r.get("dynamic_media_auth_stage", "")).lower() != "unlocked"]
    unexpected_cc = [r for r in scsi if str(r.get("scsi_status", "")).upper() == "CHECK CONDITION" and not r.get("expected_boundary_failure")]
    eff_counts: dict[str, int] = {}
    for r in reads:
        d = r.get("effective_media_lba_delta", r.get("dynamic_media_effective_lba_delta", r.get("dynamic_media_lba_delta")))
        eff_counts[str(d)] = eff_counts.get(str(d), 0) + 1
    source_counts: dict[str, int] = {}
    for r in scsi:
        s = str(r.get("source", ""))
        source_counts[s] = source_counts.get(s, 0) + 1
    return {
        "jsonl": str(jsonl),
        "rows": len(rows),
        "scsi_rows": len(scsi),
        "read_rows": len(reads),
        "locked_read_rows": len(locked),
        "unlocked_read_rows": len(unlocked),
        "unexpected_check_condition_count": len(unexpected_cc),
        "effective_delta_counts": eff_counts,
        "read_source_counts": {k: v for k, v in source_counts.items() if "read" in k.lower() or "dynamic_media_window" in k},
        "dynamic_media_window_read_rows": sum(1 for r in reads if r.get("source") == "dynamic_media_window"),
        "firmware_shadow_lba_map_source_counts": dict(sorted({str(r.get("firmware_shadow_lba_map_source", "")): 0 for r in reads}.items())),
        "known_good_v206_complete": bool(any(r.get("known_good_v206_complete") for r in rows if isinstance(r, dict))),
        "ok": len(rows) > 0 and len(unexpected_cc) == 0,
    }


def load_csv(path: Path) -> list[dict[str, str]]:
    try:
        with path.open("r", encoding="utf-8", newline="") as f:
            return list(csv.DictReader(f))
    except Exception:
        return []


def resolve_case_dir(sweep_json: dict[str, Any], explicit: str | None, base_dir: Path) -> Path | None:
    if explicit:
        p = Path(explicit)
        return p if p.is_absolute() else (base_dir / p)
    raw = sweep_json.get("case_dir")
    if raw:
        p = Path(str(raw).replace("\\", os.sep))
        if p.exists():
            return p
        # Artifact ZIPs usually contain the basename only in the current dir.
        q = base_dir / p.name
        if q.exists():
            return q
    return None


def summarize_case_trace(path: Path, case_report: dict[str, Any]) -> dict[str, Any]:
    rows = load_csv(path)
    if not rows:
        return {"trace_csv": str(path), "exists": False, "ok": False}
    cdb_lba_writes = [r for r in rows if r.get("event") == "watch_write" and r.get("name") == "watch_05ec_assembled_lba" and norm_pc(r.get("pc")) == READ_CDB_LBA_STORE_PC]
    post_store_seq = max([parse_int(r.get("seq"), -1) for r in cdb_lba_writes] or [-1])
    post_rows = [r for r in rows if parse_int(r.get("seq"), -1) > post_store_seq] if post_store_seq >= 0 else []
    read_source_decision = [r for r in rows if r.get("event") == "gdr_session_read_source_decision"]
    d108_decision = [r for r in read_source_decision if norm_pc(r.get("pc")) == D108_DATA_PHASE_PC]
    command_complete = [r for r in rows if r.get("event") == "gdr_session_command_complete"]
    main_loop_complete = [r for r in command_complete if norm_pc(r.get("pc")) == MAIN_LOOP_PC]
    # Direct evidence for the desired next stage.
    reads_05ec_after_store = [r for r in post_rows if r.get("event") == "watch_read" and r.get("name") == "watch_05ec_assembled_lba" and norm_pc(r.get("pc")) != READ_CDB_LBA_STORE_PC]
    descriptor_copy_hits = [r for r in post_rows if norm_pc(r.get("pc")) == EXPECTED_DESCRIPTOR_COPY_PC]
    descriptor_dest_writes = [r for r in post_rows if r.get("event") == "watch_write" and parse_int(r.get("addr"), -1) in range(EXPECTED_DESCRIPTOR_DEST, EXPECTED_DESCRIPTOR_DEST + 4)]
    # PCs hit after store, compacted to first occurrence order.
    pc_chain: list[str] = []
    seen = set()
    for r in post_rows:
        pc = norm_pc(r.get("pc"))
        if pc and pc not in seen:
            seen.add(pc)
            pc_chain.append(pc)
        if len(pc_chain) >= 24:
            break
    decision_notes = [parse_notes(r.get("notes", "")) for r in read_source_decision]
    return {
        "trace_csv": str(path),
        "exists": True,
        "case": case_report.get("name") or path.stem,
        "kind": case_report.get("kind"),
        "expected_host_lba_hex": case_report.get("expected_host_lba_hex"),
        "assembled_05ec_lba_hex": case_report.get("assembled_05ec_lba_hex"),
        "assembled_match": bool(case_report.get("assembled_05ec_match")),
        "rows": len(rows),
        "post_store_seq": post_store_seq,
        "post_store_unique_pc_prefix": pc_chain,
        "read_source_decision_count": len(read_source_decision),
        "read_source_decision_pcs": sorted({norm_pc(r.get("pc")) for r in read_source_decision}),
        "d108_model_decision_count": len(d108_decision),
        "main_loop_command_complete_count": len(main_loop_complete),
        "post_05ec_read_after_store_count": len(reads_05ec_after_store),
        "descriptor_copy_pc_9001b5ef_count": len(descriptor_copy_hits),
        "descriptor_06e4_write_count": len(descriptor_dest_writes),
        "decision_notes": decision_notes[-3:],
        "firmware_lba_to_descriptor_proven": len(descriptor_copy_hits) > 0 and len(descriptor_dest_writes) > 0,
        "decision_is_hardware_model_d108": len(d108_decision) > 0,
        "ok": True,
    }


def sweep_summary(sweep_json_path: Path | None, case_dir_arg: str | None, base_dir: Path) -> dict[str, Any]:
    if not sweep_json_path or not sweep_json_path.exists():
        return {"ok": False, "reason": "read CDB CPU sweep JSON missing"}
    sweep = load_json(sweep_json_path)
    case_dir = resolve_case_dir(sweep, case_dir_arg, base_dir)
    cases = []
    for c in sweep.get("cases", []) or []:
        trace_path = None
        raw = c.get("trace_csv")
        if raw:
            p = Path(str(raw).replace("\\", os.sep))
            if p.exists():
                trace_path = p
            elif case_dir:
                q = case_dir / Path(str(raw).replace("\\", os.sep)).name
                if q.exists():
                    trace_path = q
        if trace_path is None and case_dir:
            name = str(c.get("name") or "") + ".trace.csv"
            q = case_dir / name
            if q.exists():
                trace_path = q
        cases.append(summarize_case_trace(trace_path, c) if trace_path else {"case": c.get("name"), "exists": False, "ok": False})
    return {
        "sweep_json": str(sweep_json_path),
        "case_dir": str(case_dir) if case_dir else None,
        "case_count": len(cases),
        "passed_count": sum(1 for c in cases if c.get("ok")),
        "firmware_derived_host_lba": bool(sweep.get("firmware_derived_host_lba")),
        "firmware_derived_delta": bool(sweep.get("firmware_derived_delta")),
        "cases": cases,
        "all_cases_d108_model_decision": bool(cases) and all(c.get("decision_is_hardware_model_d108") for c in cases if c.get("exists")),
        "any_lba_to_descriptor_proven": any(c.get("firmware_lba_to_descriptor_proven") for c in cases),
        "ok": bool(sweep.get("ok")) and bool(cases) and all(c.get("ok") for c in cases),
    }


def find_all(data: bytes, pat: bytes) -> list[int]:
    out = []
    start = 0
    while True:
        idx = data.find(pat, start)
        if idx < 0:
            break
        out.append(idx)
        start = idx + 1
    return out


def static_firmware_scan(config: dict[str, Any], config_path: Path, firmware_arg: str | None) -> dict[str, Any]:
    firmware = resolve_firmware(config, config_path, firmware_arg)
    p = Path(str(firmware))
    if not p.exists():
        return {
            "firmware": str(firmware),
            "firmware_resolved": False,
            "ok": False,
            "reason": "firmware_missing; analyzer output may still be running or path not available",
        }
    data = p.read_bytes()
    groups: dict[str, dict[str, Any]] = {}
    for group, specs in PATTERN_GROUPS.items():
        hits = []
        for spec in specs:
            for off in find_all(data, spec["pattern"]):
                hits.append({
                    "rom_address": f"0x{ROM_BASE + off:08X}",
                    "offset": off,
                    "op": spec["op"],
                    "addr": f"0x{spec['addr']:04X}",
                    "reg": spec["reg"],
                    "pattern_hex": " ".join(f"{b:02X}" for b in spec["pattern"]),
                })
        hits.sort(key=lambda h: h["offset"])
        groups[group] = {
            "hit_count": len(hits),
            "read_count": sum(1 for h in hits if str(h["op"]).startswith("read")),
            "write_count": sum(1 for h in hits if str(h["op"]).startswith("write")),
            "hits": hits[:64],
        }
    expected_edges = {
        "has_05ec_read_candidate_9001b5ef": any(h["rom_address"] == EXPECTED_DESCRIPTOR_COPY_PC and h["op"] == "read_dword" for h in groups.get("assembled_lba_05ec", {}).get("hits", [])),
        "has_05ec_store_9001e541": any(h["rom_address"] == READ_CDB_LBA_STORE_PC and h["op"] == "write_dword" for h in groups.get("assembled_lba_05ec", {}).get("hits", [])),
        "has_descriptor_06e4_write_candidate": groups.get("descriptor_lba_06e4", {}).get("write_count", 0) > 0,
        "has_0760_state_refs": groups.get("current_lba_0760", {}).get("hit_count", 0) > 0,
    }
    return {
        "firmware": str(p),
        "firmware_resolved": True,
        "firmware_size": len(data),
        "groups": groups,
        "expected_edges": expected_edges,
        "ok": True,
    }


def load_optional_json(path: str | None, base_dir: Path) -> tuple[Path | None, dict[str, Any] | None]:
    if not path:
        return None, None
    p = Path(path)
    if not p.is_absolute():
        p = base_dir / p
    if not p.exists():
        return p, None
    return p, load_json(p)


def write_md(path: Path, report: dict[str, Any]) -> None:
    lines = [
        f"# GDR-8050L {VERSION} firmware READ-window path probe",
        "",
        f"- ok: `{str(report.get('ok')).lower()}`",
        f"- live_trace_ok: `{str(report.get('live', {}).get('ok')).lower()}`",
        f"- firmware_derived_host_lba: `{str(report.get('firmware_derived_host_lba')).lower()}`",
        f"- firmware_derived_delta: `{str(report.get('firmware_derived_delta')).lower()}`",
        f"- current_gap: `{report.get('current_gap')}`",
        "",
        "## Interpretation",
        "",
        "v253 keeps the live bridge behavior unchanged.  The v230/v231/v234/v250/v253 evidence proves firmware-side READ(10)/READ(12) host-LBA ingestion into SRAM `0x05EC`.  v250 proved the raw descriptor-builder copy block, and v253 adds a descriptor-gate probe proving the seeded `0x05EC` value can flow through the `0x9001B54E` setup/gate path into `0x9001B5EF` when SRAM `0x05B6` mask `0x04` is set.  The offline natural READ CDB sweep still reaches a D108 host-interface data-phase model decision at `0x9001CD59` and then command completion at the main loop; it does not yet prove the full live READ parser route enters `0x9001B54E`/`0x9001B5EF`.",
        "",
        "## Live trace summary",
        "",
    ]
    live = report.get("live", {})
    for k in ["rows", "scsi_rows", "read_rows", "locked_read_rows", "unlocked_read_rows", "unexpected_check_condition_count", "dynamic_media_window_read_rows", "effective_delta_counts"]:
        lines.append(f"- {k}: `{live.get(k)}`")
    lines += ["", "## CPU sweep post-LBA path cases", "", "| case | assembled | D108 model decision | 0x05EC post-read | 0x9001B5EF descriptor copy | 0x06E4 writes |", "|---|---:|---:|---:|---:|---:|"]
    for c in report.get("sweep", {}).get("cases", []) or []:
        lines.append(
            f"| `{c.get('case')}` | `{c.get('assembled_05ec_lba_hex')}` | {c.get('d108_model_decision_count')} | {c.get('post_05ec_read_after_store_count')} | {c.get('descriptor_copy_pc_9001b5ef_count')} | {c.get('descriptor_06e4_write_count')} |"
        )
    descriptor = report.get("descriptor_builder") or {}
    lines += [
        "",
        "## Descriptor-builder microprobe",
        "",
        f"- descriptor_builder_seeded_lba_proven: `{descriptor.get('firmware_descriptor_builder_seeded_lba_proven')}`",
        f"- descriptor_builder_case_count: `{descriptor.get('case_count')}`",
        f"- descriptor_builder_passed_count: `{descriptor.get('passed_count')}`",
        "",
        "This proves the descriptor block itself copies seeded `0x05EC` into `0x06E4`; it still does not prove the live READ route reaches that block.",
        "",
    ]
    gate = report.get("descriptor_gate") or {}
    lines += [
        "## Descriptor-gate microprobe",
        "",
        f"- descriptor_gate_proven: `{gate.get('descriptor_gate_proven')}`",
        f"- descriptor_setup_to_builder_proven: `{gate.get('descriptor_setup_to_builder_proven')}`",
        f"- descriptor_gate_negative_proven: `{gate.get('descriptor_gate_negative_proven')}`",
        f"- descriptor_gate_bit_mask: `{gate.get('descriptor_gate_bit_mask_hex')}` at SRAM `0x05B6`",
        f"- descriptor_gate_case_count: `{gate.get('case_count')}`",
        f"- descriptor_gate_passed_count: `{gate.get('passed_count')}`",
        "",
        "This proves a seeded `0x05EC` value can flow through the `0x9001B54E` descriptor setup function and its `0x05B6` gate into `0x9001B5EF`.  It still does not prove the full READ parser route reaches that function.",
        "",
    ]
    static = report.get("static_firmware", {})
    edges = static.get("expected_edges", {}) if isinstance(static, dict) else {}
    lines += [
        "",
        "## Static firmware breadcrumbs",
        "",
        f"- firmware_resolved: `{static.get('firmware_resolved')}`",
        f"- has_05ec_store_9001e541: `{edges.get('has_05ec_store_9001e541')}`",
        f"- has_05ec_read_candidate_9001b5ef: `{edges.get('has_05ec_read_candidate_9001b5ef')}`",
        f"- has_descriptor_06e4_write_candidate: `{edges.get('has_descriptor_06e4_write_candidate')}`",
        f"- has_0760_state_refs: `{edges.get('has_0760_state_refs')}`",
        "",
        "These static breadcrumbs identify the next firmware PCs to drive dynamically; they are not treated as media-window proof by themselves.",
        "",
        "## Next probe target",
        "",
        "Drive the firmware path beyond `0x9001E541`/`0x06F1` into the descriptor builder around `0x9001B5EF`, then trace `0x06E4`/`0x0760`/`0x0780` state through the transfer decision.  Only after that path consumes or derives the unlocked delta should `firmware_derived_delta` become true.",
        "",
    ]
    if static.get("groups"):
        lines += ["## Static direct-reference counts", "", "| group | hits | reads | writes | first PCs |", "|---|---:|---:|---:|---|"]
        for group, g in static["groups"].items():
            hits = g.get("hits") or []
            first = ", ".join(h["rom_address"] for h in hits[:6])
            lines.append(f"| `{group}` | {g.get('hit_count')} | {g.get('read_count')} | {g.get('write_count')} | `{first}` |")
        lines.append("")
    path.parent.mkdir(parents=True, exist_ok=True)
    path.write_text("\n".join(lines), encoding="utf-8")


def main(argv: list[str] | None = None) -> int:
    ap = argparse.ArgumentParser(description="Audit GDR-8050L READ LBA path through current firmware/window proof gap")
    ap.add_argument("jsonl", nargs="?", default="gdr8050l_auth_v234_stateful_relock_keep_loaded_iscsi_start_live.jsonl")
    ap.add_argument("--config", default="gdr8050l_auth_v234_stateful_relock_keep_loaded_iscsi_config.json")
    ap.add_argument("--firmware")
    ap.add_argument("--read-cdb-sweep-json")
    ap.add_argument("--sweep-case-dir")
    ap.add_argument("--delta-audit-json")
    ap.add_argument("--descriptor-builder-json")
    ap.add_argument("--descriptor-gate-json")
    ap.add_argument("--json", default="gdr8050l_auth_v234_firmware_read_window_path_probe_report.json")
    ap.add_argument("--md", default="gdr8050l_auth_v234_firmware_read_window_path_probe_report.md")
    args = ap.parse_args(argv)

    base_dir = Path.cwd()
    jsonl = Path(args.jsonl)
    if not jsonl.is_absolute():
        jsonl = base_dir / jsonl
    config_path = Path(args.config)
    if not config_path.is_absolute():
        config_path = base_dir / config_path
    config = load_json(config_path) if config_path.exists() else {}

    sweep_path = None
    if args.read_cdb_sweep_json:
        sweep_path = Path(args.read_cdb_sweep_json)
        if not sweep_path.is_absolute():
            sweep_path = base_dir / sweep_path
    else:
        base = jsonl.stem if jsonl else ""
        cand = base_dir / f"{base}_firmware_read_cdb_cpu_sweep_report.json"
        if cand.exists():
            sweep_path = cand

    delta_path, delta = load_optional_json(args.delta_audit_json, base_dir) if args.delta_audit_json else (None, None)
    descriptor_path, descriptor = load_optional_json(args.descriptor_builder_json, base_dir) if args.descriptor_builder_json else (None, None)
    gate_path, gate = load_optional_json(args.descriptor_gate_json, base_dir) if args.descriptor_gate_json else (None, None)
    if delta is None and jsonl:
        cand = base_dir / f"{jsonl.stem}_firmware_window_delta_audit_report.json"
        if cand.exists():
            delta_path, delta = cand, load_json(cand)
    if descriptor is None and jsonl:
        cand = base_dir / f"{jsonl.stem}_firmware_descriptor_builder_probe_report.json"
        if cand.exists():
            descriptor_path, descriptor = cand, load_json(cand)
    if gate is None and jsonl:
        cand = base_dir / f"{jsonl.stem}_firmware_descriptor_gate_probe_report.json"
        if cand.exists():
            gate_path, gate = cand, load_json(cand)

    live = live_summary(jsonl)
    sweep = sweep_summary(sweep_path, args.sweep_case_dir, base_dir) if sweep_path else {"ok": False, "reason": "sweep_json_not_found"}
    static = static_firmware_scan(config, config_path, args.firmware) if config else {"ok": False, "firmware_resolved": False, "reason": "config_missing"}

    firmware_derived_host_lba = bool(sweep.get("firmware_derived_host_lba"))
    firmware_derived_delta = False
    if delta and bool(delta.get("firmware_derived_delta")):
        firmware_derived_delta = True
    # Do not promote on static breadcrumbs or bridge/media-profile live agreement.
    current_gap = "firmware_lba_ingestion_proven_but_window_delta_still_bridge_media_profile_selected"
    descriptor_gate_ok = True if gate is None else bool(gate.get("ok"))
    ok = bool(live.get("ok")) and bool(sweep.get("ok")) and descriptor_gate_ok and firmware_derived_host_lba and not firmware_derived_delta
    report = {
        "version": VERSION,
        "jsonl": str(jsonl),
        "config": str(config_path),
        "read_cdb_sweep_json": str(sweep_path) if sweep_path else None,
        "delta_audit_json": str(delta_path) if delta_path else None,
        "descriptor_builder_json": str(descriptor_path) if descriptor_path else None,
        "descriptor_gate_json": str(gate_path) if gate_path else None,
        "live": live,
        "sweep": sweep,
        "static_firmware": static,
        "delta_audit_firmware_derived_delta": bool(delta.get("firmware_derived_delta")) if isinstance(delta, dict) else False,
        "descriptor_builder": descriptor if isinstance(descriptor, dict) else None,
        "descriptor_builder_seeded_lba_proven": bool(descriptor.get("firmware_descriptor_builder_seeded_lba_proven")) if isinstance(descriptor, dict) else False,
        "descriptor_gate": gate if isinstance(gate, dict) else None,
        "descriptor_gate_proven": bool(gate.get("descriptor_gate_proven")) if isinstance(gate, dict) else False,
        "firmware_derived_host_lba": firmware_derived_host_lba,
        "firmware_derived_delta": firmware_derived_delta,
        "current_gap": current_gap,
        "ok": ok,
    }
    json_path = Path(args.json)
    md_path = Path(args.md)
    if not json_path.is_absolute():
        json_path = base_dir / json_path
    if not md_path.is_absolute():
        md_path = base_dir / md_path
    write_json(json_path, report)
    write_md(md_path, report)
    print(f"[FIRMWARE-READ-WINDOW-PATH] live_reads={live.get('read_rows')} sweep_cases={sweep.get('case_count')} firmware_derived_host_lba={str(firmware_derived_host_lba).lower()} firmware_derived_delta=false ok={str(ok).lower()}")
    return 0 if ok else 1


if __name__ == "__main__":
    raise SystemExit(main())
