#!/usr/bin/env python3
"""Generate/run route-variant CPU traces for GDR-8050L LBA proof work.

v170 proved the measurement harness works, v191 proved the natural READ/LBA route can sweep multiple host LBAs, and v192 added optional media-backed READ payload generation. v193 adds block/boundary media-window stress presets for testing multi-block Windows-like READs before moving the same mapping back into the live iSCSI bridge.

The known-good persistent packet
session can still complete READ(10) through the bridge/data-phase model without
hitting the firmware READ CDB/LBA dispatcher candidates.  This tool runs a small
matrix of conservative host-interface variants to discover which gate/packet
presentation route reaches those candidate PCs.

It does not start iSCSI and does not mutate media/flash.  It only generates
mn103s_emu.py configs and optionally runs them offline.
"""
from __future__ import annotations

import argparse
import json
import subprocess
import sys
from copy import deepcopy
from pathlib import Path
from typing import Any

from hlds_profiles import load_json_or_jsonc
from hlds_lba_cpu_trace_builder import build_config
from hlds_lba_trace_validator import validate_lba_trace, write_md as write_lba_md


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).strip(), 0)


def safe_name(text: Any) -> str:
    out = "".join(ch if ch.isalnum() or ch in "._-" else "_" for ch in str(text))
    return out.strip("._-") or "route"


def short_variant_dir(index: int, name: str) -> str:
    """Return a Windows MAX_PATH-safe directory name for one variant.

    v179c still used long file basenames inside long variant directories. On
    Windows this can surface as FileNotFoundError around 260 characters even
    when the directory exists. Keep the report/name fields verbose, but keep
    paths intentionally short.
    """
    aliases = {
        "firmware_ingress_05ec_source": "ingress05ec",
        "natural_dfa0_latch_tail_gate_0a8d08": "nat_a8d",
        "natural_dfa0_latch_frame_gate_0a8d08": "nat_frame_a8d",
        "firmware_caller_9001e3d6_gate_0a8d08": "e3d6_a8d",
        "firmware_caller_9001e3d6_gate_0a8d08_02c01": "e3d6_a8d_02c01",
        "firmware_caller_9001e3d6_gate_0a8d08_d95404": "e3d6_a8d_d954",
        "firmware_caller_9001e3d6_gate_0a8d08_d95404_clear_d954": "e3d6_d954clr",
        "firmware_caller_9001e3d6_gate_0a8d08_d95404_clear_d954_df38_cdb": "e3d6_d954clr_df38",
        "firmware_caller_9001e303_gate_0a8d08_d95404_clear_d954": "e303_d954clr",
        "firmware_caller_9001e303_gate_0a8d08_d95404_clear_d954_0774_lba_le": "e303_d954_0774le",
        "firmware_caller_9001e303_gate_0a8d08_d95404_clear_d954_0774_lba_be": "e303_d954_0774be",
        "natural_dfa0_latch_tail_gate_0a8d08_d95404_clear_d954_sched052e1": "nat_052e1_d954",
        "natural_dfa0_latch_tail_gate_0a8d08_d95404_clear_d954_ca2f_sched052e1": "nat_052e1_ca2f",
        "natural_dfa0_latch_tail_gate_0a8d08_d95404_clear_d954_ca2f_sched052e1_df38_cdb": "nat_052e1_ca2f_df38",
        "natural_dfa0_latch_tail_gate_0a8d08_d95404_clear_d954_ca77_sched052e1": "nat_052e1_ca77",
        "natural_dfa0_latch_tail_gate_0a8d08_d95404_clear_d954_ca2f_sched052e1_timer053a0": "nat_tmr0",
        "natural_dfa0_latch_tail_gate_0a8d08_d95404_clear_d954_ca2f_sched052e1_timer053a0_052f1": "nat_tmr0_f1",
        "natural_dfa0_latch_tail_gate_0a8d08_d95404_clear_d954_ca2f_sched052e1_timer053a0_052f1_0532_20": "nat_tmr0_f1_acc",
        "natural_dfa0_latch_tail_gate_0a8d08_d95404_clear_d954_ca2f_sched052e1_0536_evt": "nat_0536_evt",
        "natural_dfa0_latch_tail_gate_0a8d08_d95404_clear_d954_ca2f_sched052e1_0536_evt_bc1000": "nat_0536_bc",
        "natural_dfa0_latch_tail_gate_0a8d08_d95404_clear_d954_ca2f_sched052e1_0536_evt_bc1000_002c200_ed1": "nat_0536_evt2",
        "natural_dfa0_latch_tail_gate_0a8d08_d95404_clear_d954_ca2f_sched052e1_0536_mask02": "nat_0536_m02",
        "natural_dfa0_latch_tail_gate_0a8d08_d95404_clear_d954_ca2f_sched052e1_0536_mask02_bc1000": "nat_0536_m02_bc",
        "natural_dfa0_latch_tail_gate_0a8d08_d95404_clear_d954_ca2f_sched052e1_0536_mask02_bc1000_002c200_ed1": "nat_0536_m02_evt2",
        "natural_dfa0_latch_tail_gate_0a8d08_d95404_clear_d954_ca2f_sched052e1_0606b_0728_18_0536_mask02": "nat_0606b_728_m02",
        "natural_dfa0_latch_tail_gate_0a8d08_d95404_clear_d954_ca2f_sched052e1_0606b_0728_18": "nat_0606b_728",
        "firmware_dispatcher_9001d56d_0728_18": "disp_d56d_728",
        "firmware_dispatcher_9001d56d_0728_07": "disp_d56d_728_07",
        "firmware_dispatcher_9001d56d_0728_08": "disp_d56d_728_08",
        "firmware_dispatcher_9001d56d_0728_09": "disp_d56d_728_09",
        "natural_dfa0_latch_tail_gate_0a8d08_d95404_clear_d954_ca2f_0606b_0728_07": "nat_0606b_728_07",
        "natural_dfa0_latch_tail_gate_0a8d08_d95404_clear_d954_ca2f_0606b_0728_07_0536_mask02": "nat_0606b_728_07_m02",
        "natural_dfa0_latch_tail_gate_0a8d08_d95404_clear_d954_ca2f_0606b_0728_08": "nat_0606b_728_08",
        "natural_dfa0_latch_tail_gate_0a8d08_d95404_clear_d954_ca2f_0606b_0728_09": "nat_0606b_728_09",
        "firmware_dispatcher_9001d56d_0728_07_e4cf_clear": "disp_d56d_07_e4cf",
        "natural_dfa0_latch_tail_gate_0a8d08_d95404_clear_d954_ca2f_0606b_0728_07_002c02": "nat_0606b_07_2c02",
        "natural_dfa0_latch_tail_gate_0a8d08_d95404_clear_d954_ca2f_0606b_0728_07_002c02_e4cf_clear": "nat_0606b_07_2c02_e4cf",
        "natural_dfa0_latch_tail_gate_0a8d08_d95404_clear_d954_ca2f_0606b_0728_07_002c02_e4cf_clear_0774_cdb": "nat_0606b_07_2c02_774",
        "firmware_dispatcher_9001d56d_0728_07_ca1a_d95404_e4cf_clear": "disp_d56d_07_ca1a",
        "natural_readonly_0606b_0728_07": "nat_ro_728_07",
        "natural_readonly_0606b_0728_07_002c02": "nat_ro_728_07_2c02",
        "natural_readonly_0606b_0728_07_002c02_0774_cdb": "nat_ro_728_07_774",
        "natural_readonly_0606b_0728_07_002c02_0536_mask02": "nat_ro_728_07_m02",
        "firmware_dispatcher_9001d56d_0728_07_ca1a_d954read_df38fifo": "disp_d56d_07_fifo",
        "natural_readonly_0606b_0728_07_d954read_df38fifo": "nat_ro_07_fifo",
        "natural_readonly_0606b_0728_07_002c02_d954read_df38fifo": "nat_ro_07_2c_fifo",
        "natural_readonly_0606b_0728_07_002c02_0774_cdb_d954read_df38fifo": "nat_ro_07_774_fifo",
        "natural_readonly_0606b_0728_07_002c02_0536_mask02_d954read_df38fifo": "nat_ro_07_m02_fifo",
        "firmware_caller_9001e55e_df4c80": "e55e_df4c",
    }
    base = aliases.get(name, safe_name(name)[:32])
    return f"v{index:02d}_{base}"


def ensure_parent(path: str | Path) -> Path:
    """Resolve path and force-create its parent before writing.

    v179c: some PARTIAL/max-cycle variants exposed a Windows relative-path edge
    where proof artifact parent directories disappeared between config creation
    and validation writes.  Keep all route-scan artifacts absolute and create the
    parent immediately before every write.
    """
    p = Path(path).resolve()
    p.parent.mkdir(parents=True, exist_ok=True)
    return p


def write_json_artifact(path: str | Path, obj: dict[str, Any]) -> Path:
    p = ensure_parent(path)
    p.write_text(json.dumps(obj, indent=2, sort_keys=False) + "\n", encoding="utf-8")
    return p


def deep_set(d: dict[str, Any], path: str, value: Any) -> None:
    cur: dict[str, Any] = d
    parts = path.split(".")
    for p in parts[:-1]:
        nxt = cur.get(p)
        if not isinstance(nxt, dict):
            nxt = {}
            cur[p] = nxt
        cur = nxt
    cur[parts[-1]] = value


def variant_specs() -> list[dict[str, Any]]:
    """Route variants, ordered from known-good baseline to increasingly exploratory.

    Each variant is intentionally a small diff over the v166/v170 baseline so the
    report can tell us which gate change moved firmware execution closer to the
    real READ handler.
    """
    return [
        {
            "name": "baseline_d108_complete",
            "description": "Known-good persistent packet path. Expected to complete but likely bypasses READ/LBA dispatcher candidates.",
            "patch": {},
        },
        {
            "name": "firmware_ingress_microcall",
            "description": "Proof splice: for READ-family commands, seed DFA0..DFAB from the active host CDB and architecturally call the firmware READ packet-ingress routine at 0x9001E4E5 before the normal persistent session continues.",
            "patch": {
                "gdr_packet_session_model.firmware_ingress.enabled": True,
                "gdr_packet_session_model.firmware_ingress.entry_pc": "0x9001E4E5",
                "gdr_packet_session_model.firmware_ingress.trigger_pc": "0x90006608",
                "gdr_packet_session_model.firmware_ingress.frame_size": "0x08",
                "gdr_packet_session_model.firmware_ingress.opcodes": ["0x28", "0xA8", "0xBE"]
            },
        },
        {
            "name": "firmware_ingress_05ec_source",
            "description": "v174 proof splice: same firmware ingress call as v173, plus emit/use the firmware-written 0x05EC assembled LBA as the explicit READ data-phase source decision surface.",
            "patch": {
                "gdr_packet_session_model.firmware_ingress.enabled": True,
                "gdr_packet_session_model.firmware_ingress.entry_pc": "0x9001E4E5",
                "gdr_packet_session_model.firmware_ingress.trigger_pc": "0x90006608",
                "gdr_packet_session_model.firmware_ingress.frame_size": "0x08",
                "gdr_packet_session_model.firmware_ingress.opcodes": ["0x28", "0xA8", "0xBE"],
                "gdr_packet_session_model.firmware_ingress.emit_05ec_source_event": True,
                "gdr_packet_session_model.firmware_ingress.use_05ec_source_for_read": True
            },
        },

        {
            "name": "natural_dfa0_latch_frame",
            "description": "v178 natural-route diagnostic: when the FE packet frame is accepted, mirror the host CDB into DFA0..DFAB but do not call firmware. Tests whether CDB latch alone lets the existing scheduler reach the READ/LBA ingress routine.",
            "patch": {
                "gdr_packet_session_model.host_event_gate.seed_dfa0_after_packet_frame": True
            },
        },
        {
            "name": "natural_dfa0_latch_tail",
            "description": "v178 natural-route diagnostic: mirror the full host CDB into DFA0..DFAB when the packet tail is accepted, with no firmware microcall. This is the closest current model to a natural packet register latch.",
            "patch": {
                "gdr_packet_session_model.host_event_gate.seed_dfa0_after_packet_tail": True
            },
        },
        {
            "name": "firmware_caller_9001e3d6",
            "description": "v178 caller-chain probe: splice at the higher caller around 0x9001E3D6 instead of at 0x9001E4E5. This tests whether the surrounding READ dispatch state naturally reaches the proven ingress code.",
            "patch": {
                "gdr_packet_session_model.firmware_ingress.enabled": True,
                "gdr_packet_session_model.firmware_ingress.entry_pc": "0x9001E3D6",
                "gdr_packet_session_model.firmware_ingress.trigger_pc": "0x90006608",
                "gdr_packet_session_model.firmware_ingress.frame_size": "0x14",
                "gdr_packet_session_model.firmware_ingress.opcodes": ["0x28", "0xA8", "0xBE"],
                "gdr_packet_session_model.firmware_ingress.emit_05ec_source_event": True,
                "gdr_packet_session_model.firmware_ingress.use_05ec_source_for_read": True
            },
        },
        {
            "name": "natural_dfa0_latch_tail_gate_0a8d08",
            "description": "v179 diagnostic: after natural CDB tail latch, set the observed 0x0A8D bit 0x08 service gate. Tests whether DFA0 plus this scheduler bit is enough without a firmware microcall.",
            "patch": {
                "gdr_packet_session_model.host_event_gate.seed_dfa0_after_packet_tail": True,
                "gdr_packet_session_model.host_event_gate.seed_memory_after_latch": [
                    {"addr": "0x0A8D", "or": "0x08", "size": 1}
                ]
            },
        },
        {
            "name": "firmware_caller_9001e3d6_gate_0a8d08",
            "description": "v179 caller-chain diagnostic: enter the caller at 0x9001E3D6 but seed the 0x0A8D bit 0x08 gate that controls the READ10/READ12 branch to 0x9001E4BE/E4E5.",
            "patch": {
                "gdr_packet_session_model.firmware_ingress.enabled": True,
                "gdr_packet_session_model.firmware_ingress.entry_pc": "0x9001E3D6",
                "gdr_packet_session_model.firmware_ingress.trigger_pc": "0x90006608",
                "gdr_packet_session_model.firmware_ingress.frame_size": "0x14",
                "gdr_packet_session_model.firmware_ingress.opcodes": ["0x28", "0xA8", "0xBE"],
                "gdr_packet_session_model.firmware_ingress.seed_memory": [
                    {"addr": "0x0A8D", "or": "0x08", "size": 1}
                ],
                "gdr_packet_session_model.firmware_ingress.emit_05ec_source_event": True,
                "gdr_packet_session_model.firmware_ingress.use_05ec_source_for_read": True
            },
        },
        {
            "name": "firmware_caller_9001e3d6_gate_0a8d08_d95404",
            "description": "v179 caller-chain diagnostic: seed 0x0A8D bit 0x08 to take the READ branch and D954 bit 0x04 so the 0x9001CA1A readiness wait inside 0x9001E4BE can complete and fall through to 0x9001E4E5.",
            "patch": {
                "gdr_packet_session_model.firmware_ingress.enabled": True,
                "gdr_packet_session_model.firmware_ingress.entry_pc": "0x9001E3D6",
                "gdr_packet_session_model.firmware_ingress.trigger_pc": "0x90006608",
                "gdr_packet_session_model.firmware_ingress.frame_size": "0x14",
                "gdr_packet_session_model.firmware_ingress.opcodes": ["0x28", "0xA8", "0xBE"],
                "gdr_packet_session_model.firmware_ingress.seed_memory": [
                    {"addr": "0x0A8D", "or": "0x08", "size": 1},
                    {"addr": "0xD954", "or": "0x04", "size": 1}
                ],
                "gdr_packet_session_model.firmware_ingress.emit_05ec_source_event": True,
                "gdr_packet_session_model.firmware_ingress.use_05ec_source_for_read": True
            },
        },
        {
            "name": "firmware_caller_9001e3d6_gate_0a8d08_d95404_clear_d954",
            "description": "v180 transient-readiness diagnostic: seed 0x0A8D and D954 bit 0x04 to open the 0x9001E4BE wait, then clear D954 at 0x9001E4CF so the later 0x9001B95C service loop does not spin forever on a stuck readiness bit.",
            "patch": {
                "gdr_packet_session_model.firmware_ingress.enabled": True,
                "gdr_packet_session_model.firmware_ingress.entry_pc": "0x9001E3D6",
                "gdr_packet_session_model.firmware_ingress.trigger_pc": "0x90006608",
                "gdr_packet_session_model.firmware_ingress.frame_size": "0x14",
                "gdr_packet_session_model.firmware_ingress.opcodes": ["0x28", "0xA8", "0xBE"],
                "gdr_packet_session_model.firmware_ingress.seed_memory": [
                    {"addr": "0x0A8D", "or": "0x08", "size": 1},
                    {"addr": "0xD954", "or": "0x04", "size": 1}
                ],
                "gdr_packet_session_model.firmware_ingress.seed_memory_on_pc": [
                    {"pc": "0x9001E4CF", "addr": "0xD954", "clear": "0x04", "size": 1, "once": True}
                ],
                "gdr_packet_session_model.firmware_ingress.emit_05ec_source_event": True,
                "gdr_packet_session_model.firmware_ingress.use_05ec_source_for_read": True
            },
        },
        {
            "name": "firmware_caller_9001e3d6_gate_0a8d08_d95404_clear_d954_df38_cdb",
            "description": "v180 transient-readiness plus DF38 diagnostic: after opening the 0x9001E4BE wait and clearing D954 at 0x9001E4CF, also seed DF38..DF3F from CDB[0..7] so the 0x9001CA42 service-copy path can be tested with non-zero packet bytes.",
            "patch": {
                "gdr_packet_session_model.firmware_ingress.enabled": True,
                "gdr_packet_session_model.firmware_ingress.entry_pc": "0x9001E3D6",
                "gdr_packet_session_model.firmware_ingress.trigger_pc": "0x90006608",
                "gdr_packet_session_model.firmware_ingress.frame_size": "0x14",
                "gdr_packet_session_model.firmware_ingress.opcodes": ["0x28", "0xA8", "0xBE"],
                "gdr_packet_session_model.firmware_ingress.seed_memory": [
                    {"addr": "0x0A8D", "or": "0x08", "size": 1},
                    {"addr": "0xD954", "or": "0x04", "size": 1},
                    {"addr": "0xDF38", "from_cdb": True, "offset": 0, "length": 8, "size": 1}
                ],
                "gdr_packet_session_model.firmware_ingress.seed_memory_on_pc": [
                    {"pc": "0x9001E4CF", "addr": "0xD954", "clear": "0x04", "size": 1, "once": True}
                ],
                "gdr_packet_session_model.firmware_ingress.emit_05ec_source_event": True,
                "gdr_packet_session_model.firmware_ingress.use_05ec_source_for_read": True
            },
        },
        {
            "name": "firmware_caller_9001e303_gate_0a8d08_d95404_clear_d954",
            "description": "v181 high-function diagnostic: enter at the apparent function prologue 0x9001E303 instead of the mid-function 0x9001E3D6 branch, seed the same 0x0A8D/D954 gates, then clear D954 at 0x9001E4CF. Uses frame_size 0x08 so the prologue's movm/add -12 and final ret [d2,a2],20 land on the synthetic return frame.",
            "patch": {
                "gdr_packet_session_model.firmware_ingress.enabled": True,
                "gdr_packet_session_model.firmware_ingress.entry_pc": "0x9001E303",
                "gdr_packet_session_model.firmware_ingress.trigger_pc": "0x90006608",
                "gdr_packet_session_model.firmware_ingress.frame_size": "0x08",
                "gdr_packet_session_model.firmware_ingress.opcodes": ["0x28", "0xA8", "0xBE"],
                "gdr_packet_session_model.firmware_ingress.seed_memory": [
                    {"addr": "0x0A8D", "or": "0x08", "size": 1},
                    {"addr": "0xD954", "or": "0x04", "size": 1}
                ],
                "gdr_packet_session_model.firmware_ingress.seed_memory_on_pc": [
                    {"pc": "0x9001E4CF", "addr": "0xD954", "clear": "0x04", "size": 1, "once": True}
                ],
                "gdr_packet_session_model.firmware_ingress.emit_05ec_source_event": True,
                "gdr_packet_session_model.firmware_ingress.use_05ec_source_for_read": True
            },
        },
        {
            "name": "firmware_caller_9001e303_gate_0a8d08_d95404_clear_d954_0774_lba_le",
            "description": "v181 high-function diagnostic: same as the 0x9001E303 probe, also seeds 0x0774..0x0777 from the active READ LBA in little-endian memory order because the prologue copies 0x0774..0x0777 into stack bytes before reaching the READ branch.",
            "patch": {
                "gdr_packet_session_model.firmware_ingress.enabled": True,
                "gdr_packet_session_model.firmware_ingress.entry_pc": "0x9001E303",
                "gdr_packet_session_model.firmware_ingress.trigger_pc": "0x90006608",
                "gdr_packet_session_model.firmware_ingress.frame_size": "0x08",
                "gdr_packet_session_model.firmware_ingress.opcodes": ["0x28", "0xA8", "0xBE"],
                "gdr_packet_session_model.firmware_ingress.seed_memory": [
                    {"addr": "0x0A8D", "or": "0x08", "size": 1},
                    {"addr": "0xD954", "or": "0x04", "size": 1},
                    {"addr": "0x0774", "from_cdb_lba_le": True, "length": 4, "size": 4}
                ],
                "gdr_packet_session_model.firmware_ingress.seed_memory_on_pc": [
                    {"pc": "0x9001E4CF", "addr": "0xD954", "clear": "0x04", "size": 1, "once": True}
                ],
                "gdr_packet_session_model.firmware_ingress.emit_05ec_source_event": True,
                "gdr_packet_session_model.firmware_ingress.use_05ec_source_for_read": True
            },
        },
        {
            "name": "firmware_caller_9001e303_gate_0a8d08_d95404_clear_d954_0774_lba_be",
            "description": "v181 high-function diagnostic: same as the 0x9001E303 probe, but seeds 0x0774..0x0777 from the active READ LBA in big-endian/CDB byte order to distinguish the upstream 0x0774 state representation.",
            "patch": {
                "gdr_packet_session_model.firmware_ingress.enabled": True,
                "gdr_packet_session_model.firmware_ingress.entry_pc": "0x9001E303",
                "gdr_packet_session_model.firmware_ingress.trigger_pc": "0x90006608",
                "gdr_packet_session_model.firmware_ingress.frame_size": "0x08",
                "gdr_packet_session_model.firmware_ingress.opcodes": ["0x28", "0xA8", "0xBE"],
                "gdr_packet_session_model.firmware_ingress.seed_memory": [
                    {"addr": "0x0A8D", "or": "0x08", "size": 1},
                    {"addr": "0xD954", "or": "0x04", "size": 1},
                    {"addr": "0x0774", "from_cdb_lba_be": True, "length": 4, "size": 4}
                ],
                "gdr_packet_session_model.firmware_ingress.seed_memory_on_pc": [
                    {"pc": "0x9001E4CF", "addr": "0xD954", "clear": "0x04", "size": 1, "once": True}
                ],
                "gdr_packet_session_model.firmware_ingress.emit_05ec_source_event": True,
                "gdr_packet_session_model.firmware_ingress.use_05ec_source_for_read": True
            },
        },
        {
            "name": "natural_dfa0_latch_tail_gate_0a8d08_d95404_clear_d954_sched052e1",
            "description": "v181 natural scheduler diagnostic: keep the natural DFA0 latch, seed 0x0A8D/D954, set scheduler byte 0x052E=1 after packet latch, and clear D954 at the firmware wait exit if the natural path reaches it. This tests whether the main-loop scheduler state, not just DFA0, is the missing natural route trigger.",
            "patch": {
                "gdr_packet_session_model.host_event_gate.seed_dfa0_after_packet_tail": True,
                "gdr_packet_session_model.host_event_gate.seed_memory_after_latch": [
                    {"addr": "0x0A8D", "or": "0x08", "size": 1},
                    {"addr": "0xD954", "or": "0x04", "size": 1},
                    {"addr": "0x052E", "value": "0x01", "size": 1}
                ],
                "gdr_packet_session_model.firmware_ingress.enabled": False,
                "gdr_packet_session_model.host_event_gate.seed_memory_on_pc": [
                    {"pc": "0x9001E4CF", "addr": "0xD954", "clear": "0x04", "size": 1, "once": True}
                ]
            },
        },
        {
            "name": "natural_dfa0_latch_tail_gate_0a8d08_d95404_clear_d954_ca2f_sched052e1",
            "description": "v182 natural scheduler diagnostic: same as v181 natural DFA0 latch + 0x0A8D/D954 + 0x052E=1, but clear D954 at 0x9001CA2F after the 0x9001CA42 service-copy helper returns. v181 proved the natural route does not hit 0x9001E4CF, so this tests the D954 edge at the actual natural service loop.",
            "patch": {
                "gdr_packet_session_model.host_event_gate.seed_dfa0_after_packet_tail": True,
                "gdr_packet_session_model.host_event_gate.seed_memory_after_latch": [
                    {"addr": "0x0A8D", "or": "0x08", "size": 1},
                    {"addr": "0xD954", "or": "0x04", "size": 1},
                    {"addr": "0x052E", "value": "0x01", "size": 1}
                ],
                "gdr_packet_session_model.firmware_ingress.enabled": False,
                "gdr_packet_session_model.host_event_gate.seed_memory_on_pc": [
                    {"pc": "0x9001CA2F", "addr": "0xD954", "clear": "0x04", "size": 1, "once": True}
                ]
            },
        },
        {
            "name": "natural_dfa0_latch_tail_gate_0a8d08_d95404_clear_d954_ca2f_sched052e1_df38_cdb",
            "description": "v182 natural scheduler diagnostic: clear the D954 edge at 0x9001CA2F and also seed DF38..DF3F from CDB[0..7] after the DFA0 latch. This distinguishes whether the next natural blocker is the D954 edge itself or a zero service-copy frame.",
            "patch": {
                "gdr_packet_session_model.host_event_gate.seed_dfa0_after_packet_tail": True,
                "gdr_packet_session_model.host_event_gate.seed_memory_after_latch": [
                    {"addr": "0x0A8D", "or": "0x08", "size": 1},
                    {"addr": "0xD954", "or": "0x04", "size": 1},
                    {"addr": "0x052E", "value": "0x01", "size": 1},
                    {"addr": "0xDF38", "from_cdb": True, "offset": 0, "length": 8, "size": 1}
                ],
                "gdr_packet_session_model.firmware_ingress.enabled": False,
                "gdr_packet_session_model.host_event_gate.seed_memory_on_pc": [
                    {"pc": "0x9001CA2F", "addr": "0xD954", "clear": "0x04", "size": 1, "once": True}
                ]
            },
        },
        {
            "name": "natural_dfa0_latch_tail_gate_0a8d08_d95404_clear_d954_ca77_sched052e1",
            "description": "v182 natural scheduler diagnostic: same natural scheduler route, but clear D954 at 0x9001CA77, immediately before the 0x9001CA42 helper returns to 0x9001CA2F. This tests the precise timing of the hardware consumed-edge clear.",
            "patch": {
                "gdr_packet_session_model.host_event_gate.seed_dfa0_after_packet_tail": True,
                "gdr_packet_session_model.host_event_gate.seed_memory_after_latch": [
                    {"addr": "0x0A8D", "or": "0x08", "size": 1},
                    {"addr": "0xD954", "or": "0x04", "size": 1},
                    {"addr": "0x052E", "value": "0x01", "size": 1}
                ],
                "gdr_packet_session_model.firmware_ingress.enabled": False,
                "gdr_packet_session_model.host_event_gate.seed_memory_on_pc": [
                    {"pc": "0x9001CA77", "addr": "0xD954", "clear": "0x04", "size": 1, "once": True}
                ]
            },
        },
        {
            "name": "natural_dfa0_latch_tail_gate_0a8d08_d95404_clear_d954_ca2f_sched052e1_timer053a0",
            "description": "v183 natural scheduler diagnostic: v182 reached scheduler slot 0x052E=1 at 0x90016D84 but returned early because (0x0538 - DC54) was below threshold 0x053A. This keeps the natural DFA0 latch and CA2F D954 edge clear, then forces 0x053A=0 so the 0x90016D94 bge timing gate can advance.",
            "patch": {
                "gdr_packet_session_model.host_event_gate.seed_dfa0_after_packet_tail": True,
                "gdr_packet_session_model.host_event_gate.seed_memory_after_latch": [
                    {"addr": "0x0A8D", "or": "0x08", "size": 1},
                    {"addr": "0xD954", "or": "0x04", "size": 1},
                    {"addr": "0x052E", "value": "0x01", "size": 1},
                    {"addr": "0x053A", "value": "0x0000", "size": 2}
                ],
                "gdr_packet_session_model.firmware_ingress.enabled": False,
                "gdr_packet_session_model.host_event_gate.seed_memory_on_pc": [
                    {"pc": "0x9001CA2F", "addr": "0xD954", "clear": "0x04", "size": 1, "once": True}
                ]
            },
        },
        {
            "name": "natural_dfa0_latch_tail_gate_0a8d08_d95404_clear_d954_ca2f_sched052e1_timer053a0_052f1",
            "description": "v183 natural scheduler diagnostic: same timer-ready route as timer053a0, but seeds scheduler substep 0x052F=1 so the first 0x90016D84 advance increments it to 2 and reaches the deeper 0x90016DC2 path in a single packet turn.",
            "patch": {
                "gdr_packet_session_model.host_event_gate.seed_dfa0_after_packet_tail": True,
                "gdr_packet_session_model.host_event_gate.seed_memory_after_latch": [
                    {"addr": "0x0A8D", "or": "0x08", "size": 1},
                    {"addr": "0xD954", "or": "0x04", "size": 1},
                    {"addr": "0x052E", "value": "0x01", "size": 1},
                    {"addr": "0x052F", "value": "0x01", "size": 1},
                    {"addr": "0x053A", "value": "0x0000", "size": 2}
                ],
                "gdr_packet_session_model.firmware_ingress.enabled": False,
                "gdr_packet_session_model.host_event_gate.seed_memory_on_pc": [
                    {"pc": "0x9001CA2F", "addr": "0xD954", "clear": "0x04", "size": 1, "once": True}
                ]
            },
        },
        {
            "name": "natural_dfa0_latch_tail_gate_0a8d08_d95404_clear_d954_ca2f_sched052e1_timer053a0_052f1_0532_20",
            "description": "v183 natural scheduler diagnostic: same as timer053a0_052f1, but seeds accumulator 0x0532=0x0020 so the 0x90016DC2 averaging/threshold path can cross the later >=4 decision and expose whether the scheduler calls the next state function.",
            "patch": {
                "gdr_packet_session_model.host_event_gate.seed_dfa0_after_packet_tail": True,
                "gdr_packet_session_model.host_event_gate.seed_memory_after_latch": [
                    {"addr": "0x0A8D", "or": "0x08", "size": 1},
                    {"addr": "0xD954", "or": "0x04", "size": 1},
                    {"addr": "0x052E", "value": "0x01", "size": 1},
                    {"addr": "0x052F", "value": "0x01", "size": 1},
                    {"addr": "0x0532", "value": "0x0020", "size": 2},
                    {"addr": "0x053A", "value": "0x0000", "size": 2}
                ],
                "gdr_packet_session_model.firmware_ingress.enabled": False,
                "gdr_packet_session_model.host_event_gate.seed_memory_on_pc": [
                    {"pc": "0x9001CA2F", "addr": "0xD954", "clear": "0x04", "size": 1, "once": True}
                ]
            },
        },
        {
            "name": "natural_dfa0_latch_tail_gate_0a8d08_d95404_clear_d954_ca2f_sched052e1_0536_evt",
            "description": "v184 natural scheduler diagnostic: v183 showed slot 0x052E=1 reaches 0x90016D84/0x90016E4C but returns because 0x0536 bit 0 is clear. This variant applies the proven natural DFA0 latch + 0x0A8D/D954 + CA2F edge clear, then sets 0x0536 bit 0 and forces the 0x16E4C elapsed-time gate open at the PC where it is consumed.",
            "patch": {
                "gdr_packet_session_model.host_event_gate.seed_dfa0_after_packet_tail": True,
                "gdr_packet_session_model.host_event_gate.seed_memory_after_latch": [
                    {"addr": "0x0A8D", "or": "0x08", "size": 1},
                    {"addr": "0xD954", "or": "0x04", "size": 1},
                    {"addr": "0x052E", "value": "0x01", "size": 1}
                ],
                "gdr_packet_session_model.firmware_ingress.enabled": False,
                "gdr_packet_session_model.host_event_gate.seed_memory_on_pc": [
                    {"pc": "0x9001CA2F", "addr": "0xD954", "clear": "0x04", "size": 1, "once": True},
                    {"pc": "0x90016E4C", "addr": "0x0536", "or": "0x01", "size": 1, "once": False},
                    {"pc": "0x90016E4C", "addr": "0x0053C", "value": "0x0000", "size": 2, "once": False},
                    {"pc": "0x90016E4C", "addr": "0x0053E", "value": "0x0000", "size": 2, "once": False}
                ]
            },
        },
        {
            "name": "natural_dfa0_latch_tail_gate_0a8d08_d95404_clear_d954_ca2f_sched052e1_0536_evt_bc1000",
            "description": "v184 natural scheduler diagnostic: same as 0536_evt, also seeds timer source 0x00BC=0x1000 at 0x90016E4C so the elapsed-time compare cannot remain zero when 0x053C/0x053E are forced open.",
            "patch": {
                "gdr_packet_session_model.host_event_gate.seed_dfa0_after_packet_tail": True,
                "gdr_packet_session_model.host_event_gate.seed_memory_after_latch": [
                    {"addr": "0x0A8D", "or": "0x08", "size": 1},
                    {"addr": "0xD954", "or": "0x04", "size": 1},
                    {"addr": "0x052E", "value": "0x01", "size": 1}
                ],
                "gdr_packet_session_model.firmware_ingress.enabled": False,
                "gdr_packet_session_model.host_event_gate.seed_memory_on_pc": [
                    {"pc": "0x9001CA2F", "addr": "0xD954", "clear": "0x04", "size": 1, "once": True},
                    {"pc": "0x90016E4C", "addr": "0x0536", "or": "0x01", "size": 1, "once": False},
                    {"pc": "0x90016E4C", "addr": "0x0053C", "value": "0x0000", "size": 2, "once": False},
                    {"pc": "0x90016E4C", "addr": "0x0053E", "value": "0x0000", "size": 2, "once": False},
                    {"pc": "0x90016E4C", "addr": "0x0000BC", "value": "0x1000", "size": 2, "once": False}
                ]
            },
        },
        {
            "name": "natural_dfa0_latch_tail_gate_0a8d08_d95404_clear_d954_ca2f_sched052e1_0536_evt_bc1000_002c200_ed1",
            "description": "v184 natural scheduler diagnostic: same as 0536_evt_bc1000, also seeds the optional 0x16E4C branch inputs 0x002C bit 0x0200 and 0x00ED=1 while keeping 0x0A8D bit 0x08 set. This distinguishes the 0x90016FA7 versus 0x90016FBD service-update branch.",
            "patch": {
                "gdr_packet_session_model.host_event_gate.seed_dfa0_after_packet_tail": True,
                "gdr_packet_session_model.host_event_gate.seed_memory_after_latch": [
                    {"addr": "0x0A8D", "or": "0x08", "size": 1},
                    {"addr": "0xD954", "or": "0x04", "size": 1},
                    {"addr": "0x052E", "value": "0x01", "size": 1},
                    {"addr": "0x002C", "or": "0x0200", "size": 2},
                    {"addr": "0x00ED", "value": "0x01", "size": 1}
                ],
                "gdr_packet_session_model.firmware_ingress.enabled": False,
                "gdr_packet_session_model.host_event_gate.seed_memory_on_pc": [
                    {"pc": "0x9001CA2F", "addr": "0xD954", "clear": "0x04", "size": 1, "once": True},
                    {"pc": "0x90016E4C", "addr": "0x0536", "or": "0x01", "size": 1, "once": False},
                    {"pc": "0x90016E4C", "addr": "0x0053C", "value": "0x0000", "size": 2, "once": False},
                    {"pc": "0x90016E4C", "addr": "0x0053E", "value": "0x0000", "size": 2, "once": False},
                    {"pc": "0x90016E4C", "addr": "0x0000BC", "value": "0x1000", "size": 2, "once": False}
                ]
            },
        },
        {
            "name": "natural_dfa0_latch_tail_gate_0a8d08_d95404_clear_d954_ca2f_sched052e1_0536_mask02",
            "description": "v185 correction: the emulator's FE immediate 0x01 maps to mask 0x02. v184 seeded 0x0536 with 0x01 and therefore did not satisfy btst 0x01,(0x0536). This repeats the natural scheduler route with 0x0536 |= 0x02 at 0x90016E4C.",
            "patch": {
                "gdr_packet_session_model.host_event_gate.seed_dfa0_after_packet_tail": True,
                "gdr_packet_session_model.host_event_gate.seed_memory_after_latch": [
                    {"addr": "0x0A8D", "or": "0x08", "size": 1},
                    {"addr": "0xD954", "or": "0x04", "size": 1},
                    {"addr": "0x052E", "value": "0x01", "size": 1}
                ],
                "gdr_packet_session_model.firmware_ingress.enabled": False,
                "gdr_packet_session_model.host_event_gate.seed_memory_on_pc": [
                    {"pc": "0x9001CA2F", "addr": "0xD954", "clear": "0x04", "size": 1, "once": True},
                    {"pc": "0x90016E4C", "addr": "0x0536", "or": "0x02", "size": 1, "once": False},
                    {"pc": "0x90016E4C", "addr": "0x0053C", "value": "0x0000", "size": 2, "once": False},
                    {"pc": "0x90016E4C", "addr": "0x0053E", "value": "0x0000", "size": 2, "once": False}
                ]
            },
        },
        {
            "name": "natural_dfa0_latch_tail_gate_0a8d08_d95404_clear_d954_ca2f_sched052e1_0536_mask02_bc1000",
            "description": "v185 correction: same as 0536_mask02, also writes 0x00BC=0x1000 at 0x90016E4C to force the elapsed-time source nonzero.",
            "patch": {
                "gdr_packet_session_model.host_event_gate.seed_dfa0_after_packet_tail": True,
                "gdr_packet_session_model.host_event_gate.seed_memory_after_latch": [
                    {"addr": "0x0A8D", "or": "0x08", "size": 1},
                    {"addr": "0xD954", "or": "0x04", "size": 1},
                    {"addr": "0x052E", "value": "0x01", "size": 1}
                ],
                "gdr_packet_session_model.firmware_ingress.enabled": False,
                "gdr_packet_session_model.host_event_gate.seed_memory_on_pc": [
                    {"pc": "0x9001CA2F", "addr": "0xD954", "clear": "0x04", "size": 1, "once": True},
                    {"pc": "0x90016E4C", "addr": "0x0536", "or": "0x02", "size": 1, "once": False},
                    {"pc": "0x90016E4C", "addr": "0x0053C", "value": "0x0000", "size": 2, "once": False},
                    {"pc": "0x90016E4C", "addr": "0x0053E", "value": "0x0000", "size": 2, "once": False},
                    {"pc": "0x90016E4C", "addr": "0x0000BC", "value": "0x1000", "size": 2, "once": False}
                ]
            },
        },
        {
            "name": "natural_dfa0_latch_tail_gate_0a8d08_d95404_clear_d954_ca2f_sched052e1_0536_mask02_bc1000_002c200_ed1",
            "description": "v185 correction: same as 0536_mask02_bc1000, plus 0x002C|=0x0200 and 0x00ED=1 to distinguish the 0x90016FA7 versus 0x90016FBD branch after the corrected 0x0536 gate opens.",
            "patch": {
                "gdr_packet_session_model.host_event_gate.seed_dfa0_after_packet_tail": True,
                "gdr_packet_session_model.host_event_gate.seed_memory_after_latch": [
                    {"addr": "0x0A8D", "or": "0x08", "size": 1},
                    {"addr": "0xD954", "or": "0x04", "size": 1},
                    {"addr": "0x052E", "value": "0x01", "size": 1},
                    {"addr": "0x002C", "or": "0x0200", "size": 2},
                    {"addr": "0x00ED", "value": "0x01", "size": 1}
                ],
                "gdr_packet_session_model.firmware_ingress.enabled": False,
                "gdr_packet_session_model.host_event_gate.seed_memory_on_pc": [
                    {"pc": "0x9001CA2F", "addr": "0xD954", "clear": "0x04", "size": 1, "once": True},
                    {"pc": "0x90016E4C", "addr": "0x0536", "or": "0x02", "size": 1, "once": False},
                    {"pc": "0x90016E4C", "addr": "0x0053C", "value": "0x0000", "size": 2, "once": False},
                    {"pc": "0x90016E4C", "addr": "0x0053E", "value": "0x0000", "size": 2, "once": False},
                    {"pc": "0x90016E4C", "addr": "0x0000BC", "value": "0x1000", "size": 2, "once": False}
                ]
            },
        },
        {
            "name": "natural_dfa0_latch_tail_gate_0a8d08_d95404_clear_d954_ca2f_sched052e1_0606b_0728_18_0536_mask02",
            "description": "v185 natural-main-loop diagnostic: same as the v184 0x9000606B/0x0728 dispatcher-anchor probe, but also applies the corrected 0x0536 mask 0x02 at 0x90016E4C when slot 1 service runs.",
            "patch": {
                "gdr_packet_session_model.main_loop_pc": "0x9000606B",
                "gdr_packet_session_model.host_event_gate.seed_dfa0_after_packet_tail": True,
                "gdr_packet_session_model.host_event_gate.seed_memory_after_latch": [
                    {"addr": "0x0A8D", "or": "0x08", "size": 1},
                    {"addr": "0xD954", "or": "0x04", "size": 1},
                    {"addr": "0x0728", "value": "0x18", "size": 1},
                    {"addr": "0x06F1", "value": "0x04", "size": 1},
                    {"addr": "0x052E", "value": "0x01", "size": 1}
                ],
                "gdr_packet_session_model.firmware_ingress.enabled": False,
                "gdr_packet_session_model.host_event_gate.seed_memory_on_pc": [
                    {"pc": "0x9001CA2F", "addr": "0xD954", "clear": "0x04", "size": 1, "once": True},
                    {"pc": "0x90016E4C", "addr": "0x0536", "or": "0x02", "size": 1, "once": False}
                ]
            },
        },
        {
            "name": "natural_dfa0_latch_tail_gate_0a8d08_d95404_clear_d954_ca2f_sched052e1_0606b_0728_18",
            "description": "v184 natural-main-loop diagnostic: switch the persistent model's main loop anchor to 0x9000606B/0x90006077, the loop that calls 0x9001B285, while latching DFA0 and seeding 0x0728=0x18 (the table index that resolves through 0x9003C044 to 0x9001E303). This tests whether the current 0x90006608 anchor is below the real packet dispatcher.",
            "patch": {
                "gdr_packet_session_model.main_loop_pc": "0x9000606B",
                "gdr_packet_session_model.host_event_gate.seed_dfa0_after_packet_tail": True,
                "gdr_packet_session_model.host_event_gate.seed_memory_after_latch": [
                    {"addr": "0x0A8D", "or": "0x08", "size": 1},
                    {"addr": "0xD954", "or": "0x04", "size": 1},
                    {"addr": "0x0728", "value": "0x18", "size": 1},
                    {"addr": "0x06F1", "value": "0x04", "size": 1},
                    {"addr": "0x052E", "value": "0x00", "size": 1}
                ],
                "gdr_packet_session_model.firmware_ingress.enabled": False,
                "gdr_packet_session_model.host_event_gate.seed_memory_on_pc": [
                    {"pc": "0x9001CA2F", "addr": "0xD954", "clear": "0x04", "size": 1, "once": True}
                ]
            },
        },
        {
            "name": "firmware_dispatcher_9001d56d_0728_18",
            "description": "v184 controlled dispatcher diagnostic: call the real command-dispatch wrapper at 0x9001D56D with 0x0728=0x18, which should resolve the 0x9003C044 table to the already-proven 0x9001E303 READ/LBA caller. This is not natural proof, but validates the dispatcher-table index and wrapper frame before using it in natural probes.",
            "patch": {
                "gdr_packet_session_model.firmware_ingress.enabled": True,
                "gdr_packet_session_model.firmware_ingress.entry_pc": "0x9001D56D",
                "gdr_packet_session_model.firmware_ingress.trigger_pc": "0x90006608",
                "gdr_packet_session_model.firmware_ingress.frame_size": "0x0C",
                "gdr_packet_session_model.firmware_ingress.opcodes": ["0x28", "0xA8", "0xBE"],
                "gdr_packet_session_model.firmware_ingress.seed_memory": [
                    {"addr": "0x0728", "value": "0x18", "size": 1},
                    {"addr": "0x0A8D", "or": "0x08", "size": 1},
                    {"addr": "0xD954", "or": "0x04", "size": 1}
                ],
                "gdr_packet_session_model.firmware_ingress.seed_memory_on_pc": [
                    {"pc": "0x9001CA2F", "addr": "0xD954", "clear": "0x04", "size": 1, "once": True}
                ],
                "gdr_packet_session_model.firmware_ingress.emit_05ec_source_event": True,
                "gdr_packet_session_model.firmware_ingress.use_05ec_source_for_read": True
            },
        },
        {
            "name": "firmware_dispatcher_9001d56d_0728_07",
            "description": "v186 correction: static dispatcher table at 0x9003C044 maps index 0x07 to 0x9001E303, not 0x18. Controlled wrapper probe with the corrected index and the already-proven D954 consumed-edge clear.",
            "patch": {
                "gdr_packet_session_model.firmware_ingress.enabled": True,
                "gdr_packet_session_model.firmware_ingress.entry_pc": "0x9001D56D",
                "gdr_packet_session_model.firmware_ingress.trigger_pc": "0x90006608",
                "gdr_packet_session_model.firmware_ingress.frame_size": "0x0C",
                "gdr_packet_session_model.firmware_ingress.opcodes": ["0x28", "0xA8", "0xBE"],
                "gdr_packet_session_model.firmware_ingress.seed_memory": [
                    {"addr": "0x0728", "value": "0x07", "size": 1},
                    {"addr": "0x0A8D", "or": "0x08", "size": 1},
                    {"addr": "0xD954", "or": "0x04", "size": 1}
                ],
                "gdr_packet_session_model.firmware_ingress.seed_memory_on_pc": [
                    {"pc": "0x9001CA2F", "addr": "0xD954", "clear": "0x04", "size": 1, "once": True}
                ],
                "gdr_packet_session_model.firmware_ingress.emit_05ec_source_event": True,
                "gdr_packet_session_model.firmware_ingress.use_05ec_source_for_read": True
            },
        },
        {
            "name": "firmware_dispatcher_9001d56d_0728_08",
            "description": "v186 table-adjacent control: index 0x08 maps to 0x9001E55C. This checks the neighboring wrapper path that falls into the 0x9001E55E family.",
            "patch": {
                "gdr_packet_session_model.firmware_ingress.enabled": True,
                "gdr_packet_session_model.firmware_ingress.entry_pc": "0x9001D56D",
                "gdr_packet_session_model.firmware_ingress.trigger_pc": "0x90006608",
                "gdr_packet_session_model.firmware_ingress.frame_size": "0x0C",
                "gdr_packet_session_model.firmware_ingress.opcodes": ["0x28", "0xA8", "0xBE"],
                "gdr_packet_session_model.firmware_ingress.seed_memory": [
                    {"addr": "0x0728", "value": "0x08", "size": 1},
                    {"addr": "0x0A8D", "or": "0x08", "size": 1},
                    {"addr": "0xD954", "or": "0x04", "size": 1}
                ],
                "gdr_packet_session_model.firmware_ingress.seed_memory_on_pc": [
                    {"pc": "0x9001CA2F", "addr": "0xD954", "clear": "0x04", "size": 1, "once": True}
                ],
                "gdr_packet_session_model.firmware_ingress.emit_05ec_source_event": True,
                "gdr_packet_session_model.firmware_ingress.use_05ec_source_for_read": True
            },
        },
        {
            "name": "firmware_dispatcher_9001d56d_0728_09",
            "description": "v186 table-adjacent control: index 0x09 maps to 0x9001E55E. This distinguishes the direct E55E entry from the corrected E303 index.",
            "patch": {
                "gdr_packet_session_model.firmware_ingress.enabled": True,
                "gdr_packet_session_model.firmware_ingress.entry_pc": "0x9001D56D",
                "gdr_packet_session_model.firmware_ingress.trigger_pc": "0x90006608",
                "gdr_packet_session_model.firmware_ingress.frame_size": "0x0C",
                "gdr_packet_session_model.firmware_ingress.opcodes": ["0x28", "0xA8", "0xBE"],
                "gdr_packet_session_model.firmware_ingress.seed_memory": [
                    {"addr": "0x0728", "value": "0x09", "size": 1},
                    {"addr": "0x0A8D", "or": "0x08", "size": 1},
                    {"addr": "0xD954", "or": "0x04", "size": 1}
                ],
                "gdr_packet_session_model.firmware_ingress.seed_memory_on_pc": [
                    {"pc": "0x9001CA2F", "addr": "0xD954", "clear": "0x04", "size": 1, "once": True}
                ],
                "gdr_packet_session_model.firmware_ingress.emit_05ec_source_event": True,
                "gdr_packet_session_model.firmware_ingress.use_05ec_source_for_read": True
            },
        },
        {
            "name": "natural_dfa0_latch_tail_gate_0a8d08_d95404_clear_d954_ca2f_0606b_0728_07",
            "description": "v186 natural-main-loop correction: use 0x9000606B as the loop anchor and seed 0x0728=0x07, the dispatcher-table index for 0x9001E303. This removes the earlier wrong 0x18 index that routed to 0x9001F837.",
            "patch": {
                "gdr_packet_session_model.main_loop_pc": "0x9000606B",
                "gdr_packet_session_model.host_event_gate.seed_dfa0_after_packet_tail": True,
                "gdr_packet_session_model.host_event_gate.seed_memory_after_latch": [
                    {"addr": "0x0A8D", "or": "0x08", "size": 1},
                    {"addr": "0xD954", "or": "0x04", "size": 1},
                    {"addr": "0x0728", "value": "0x07", "size": 1},
                    {"addr": "0x06F1", "value": "0x04", "size": 1},
                    {"addr": "0x052E", "value": "0x00", "size": 1}
                ],
                "gdr_packet_session_model.firmware_ingress.enabled": False,
                "gdr_packet_session_model.host_event_gate.seed_memory_on_pc": [
                    {"pc": "0x9001CA2F", "addr": "0xD954", "clear": "0x04", "size": 1, "once": True}
                ]
            },
        },
        {
            "name": "natural_dfa0_latch_tail_gate_0a8d08_d95404_clear_d954_ca2f_0606b_0728_07_0536_mask02",
            "description": "v186 natural-main-loop correction with corrected 0x0536 mask. Combines the 0x0728=0x07 E303 dispatcher index with the v185 scheduler-bitmask fix.",
            "patch": {
                "gdr_packet_session_model.main_loop_pc": "0x9000606B",
                "gdr_packet_session_model.host_event_gate.seed_dfa0_after_packet_tail": True,
                "gdr_packet_session_model.host_event_gate.seed_memory_after_latch": [
                    {"addr": "0x0A8D", "or": "0x08", "size": 1},
                    {"addr": "0xD954", "or": "0x04", "size": 1},
                    {"addr": "0x0728", "value": "0x07", "size": 1},
                    {"addr": "0x06F1", "value": "0x04", "size": 1},
                    {"addr": "0x052E", "value": "0x01", "size": 1}
                ],
                "gdr_packet_session_model.firmware_ingress.enabled": False,
                "gdr_packet_session_model.host_event_gate.seed_memory_on_pc": [
                    {"pc": "0x9001CA2F", "addr": "0xD954", "clear": "0x04", "size": 1, "once": True},
                    {"pc": "0x90016E4C", "addr": "0x0536", "or": "0x02", "size": 1, "once": False}
                ]
            },
        },
        {
            "name": "natural_dfa0_latch_tail_gate_0a8d08_d95404_clear_d954_ca2f_0606b_0728_08",
            "description": "v186 table-adjacent natural control using 0x0728=0x08, expected to target the 0x9001E55C/E55E family rather than E303.",
            "patch": {
                "gdr_packet_session_model.main_loop_pc": "0x9000606B",
                "gdr_packet_session_model.host_event_gate.seed_dfa0_after_packet_tail": True,
                "gdr_packet_session_model.host_event_gate.seed_memory_after_latch": [
                    {"addr": "0x0A8D", "or": "0x08", "size": 1},
                    {"addr": "0xD954", "or": "0x04", "size": 1},
                    {"addr": "0x0728", "value": "0x08", "size": 1},
                    {"addr": "0x06F1", "value": "0x04", "size": 1}
                ],
                "gdr_packet_session_model.firmware_ingress.enabled": False,
                "gdr_packet_session_model.host_event_gate.seed_memory_on_pc": [
                    {"pc": "0x9001CA2F", "addr": "0xD954", "clear": "0x04", "size": 1, "once": True}
                ]
            },
        },
        {
            "name": "natural_dfa0_latch_tail_gate_0a8d08_d95404_clear_d954_ca2f_0606b_0728_09",
            "description": "v186 table-adjacent natural control using 0x0728=0x09, expected to target direct 0x9001E55E rather than E303.",
            "patch": {
                "gdr_packet_session_model.main_loop_pc": "0x9000606B",
                "gdr_packet_session_model.host_event_gate.seed_dfa0_after_packet_tail": True,
                "gdr_packet_session_model.host_event_gate.seed_memory_after_latch": [
                    {"addr": "0x0A8D", "or": "0x08", "size": 1},
                    {"addr": "0xD954", "or": "0x04", "size": 1},
                    {"addr": "0x0728", "value": "0x09", "size": 1},
                    {"addr": "0x06F1", "value": "0x04", "size": 1}
                ],
                "gdr_packet_session_model.firmware_ingress.enabled": False,
                "gdr_packet_session_model.host_event_gate.seed_memory_on_pc": [
                    {"pc": "0x9001CA2F", "addr": "0xD954", "clear": "0x04", "size": 1, "once": True}
                ]
            },
        },

        {
            "name": "firmware_caller_9001e3d6_gate_0a8d08_02c01",
            "description": "v179 caller-chain diagnostic: same as 0x0A8D gate seed, also sets status word 0x002C bit 0 to match the upstream branch conditions seen around 0x9001E303.",
            "patch": {
                "gdr_packet_session_model.firmware_ingress.enabled": True,
                "gdr_packet_session_model.firmware_ingress.entry_pc": "0x9001E3D6",
                "gdr_packet_session_model.firmware_ingress.trigger_pc": "0x90006608",
                "gdr_packet_session_model.firmware_ingress.frame_size": "0x14",
                "gdr_packet_session_model.firmware_ingress.opcodes": ["0x28", "0xA8", "0xBE"],
                "gdr_packet_session_model.firmware_ingress.seed_memory": [
                    {"addr": "0x0A8D", "or": "0x08", "size": 1},
                    {"addr": "0x002C", "or": "0x0001", "size": 2}
                ],
                "gdr_packet_session_model.firmware_ingress.emit_05ec_source_event": True,
                "gdr_packet_session_model.firmware_ingress.use_05ec_source_for_read": True
            },
        },
        {
            "name": "firmware_dispatcher_9001d56d_0728_07_e4cf_clear",
            "description": "v187 controlled dispatcher retest: 0x0728=0x07 selects the 0x9001E303 table entry. v186 proved the index but then stuck at the 0x9001E4BE/D954 wait because the dispatcher path rewrote D954 to 0x60. This variant applies the same proven E4CF consumed-edge clear used by the direct E303/E3D6 probes.",
            "patch": {
                "gdr_packet_session_model.firmware_ingress.enabled": True,
                "gdr_packet_session_model.firmware_ingress.entry_pc": "0x9001D56D",
                "gdr_packet_session_model.firmware_ingress.trigger_pc": "0x90006608",
                "gdr_packet_session_model.firmware_ingress.frame_size": "0x0C",
                "gdr_packet_session_model.firmware_ingress.opcodes": ["0x28", "0xA8", "0xBE"],
                "gdr_packet_session_model.firmware_ingress.seed_memory": [
                    {"addr": "0x0728", "value": "0x07", "size": 1},
                    {"addr": "0x0A8D", "or": "0x08", "size": 1},
                    {"addr": "0xD954", "or": "0x04", "size": 1}
                ],
                "gdr_packet_session_model.firmware_ingress.seed_memory_on_pc": [
                    {"pc": "0x9001E4CF", "addr": "0xD954", "clear": "0x04", "size": 1, "once": True}
                ],
                "gdr_packet_session_model.firmware_ingress.emit_05ec_source_event": True,
                "gdr_packet_session_model.firmware_ingress.use_05ec_source_for_read": True
            },
        },
        {
            "name": "natural_dfa0_latch_tail_gate_0a8d08_d95404_clear_d954_ca2f_0606b_0728_07_002c02",
            "description": "v187 natural-main-loop diagnostic: v186 reached the 0x9001E303 table entry, but the natural path fell through the non-READ/status branch around 0x9001E432. Seed 0x002C bit mask 0x0002 (the bit tested by btst 1,d0) so the E303 prologue can take the E376/E3D6 READ branch rather than the E432 branch.",
            "patch": {
                "gdr_packet_session_model.main_loop_pc": "0x9000606B",
                "gdr_packet_session_model.host_event_gate.seed_dfa0_after_packet_tail": True,
                "gdr_packet_session_model.host_event_gate.seed_memory_after_latch": [
                    {"addr": "0x0A8D", "or": "0x08", "size": 1},
                    {"addr": "0xD954", "or": "0x04", "size": 1},
                    {"addr": "0x0728", "value": "0x07", "size": 1},
                    {"addr": "0x06F1", "value": "0x04", "size": 1},
                    {"addr": "0x002C", "or": "0x0002", "size": 2}
                ],
                "gdr_packet_session_model.firmware_ingress.enabled": False,
                "gdr_packet_session_model.host_event_gate.seed_memory_on_pc": [
                    {"pc": "0x9001CA2F", "addr": "0xD954", "clear": "0x04", "size": 1, "once": True}
                ]
            },
        },
        {
            "name": "natural_dfa0_latch_tail_gate_0a8d08_d95404_clear_d954_ca2f_0606b_0728_07_002c02_e4cf_clear",
            "description": "v187 natural-main-loop diagnostic with both corrected 0x002C READ-branch bit and the E4CF consumed-edge clear. This tests whether the natural dispatcher can now reach the same E4BE/E4E5 CDB/LBA path as the controlled 0x9001E303 probe.",
            "patch": {
                "gdr_packet_session_model.main_loop_pc": "0x9000606B",
                "gdr_packet_session_model.host_event_gate.seed_dfa0_after_packet_tail": True,
                "gdr_packet_session_model.host_event_gate.seed_memory_after_latch": [
                    {"addr": "0x0A8D", "or": "0x08", "size": 1},
                    {"addr": "0xD954", "or": "0x04", "size": 1},
                    {"addr": "0x0728", "value": "0x07", "size": 1},
                    {"addr": "0x06F1", "value": "0x04", "size": 1},
                    {"addr": "0x002C", "or": "0x0002", "size": 2}
                ],
                "gdr_packet_session_model.firmware_ingress.enabled": False,
                "gdr_packet_session_model.host_event_gate.seed_memory_on_pc": [
                    {"pc": "0x9001CA2F", "addr": "0xD954", "clear": "0x04", "size": 1, "once": True},
                    {"pc": "0x9001E4CF", "addr": "0xD954", "clear": "0x04", "size": 1, "once": True}
                ]
            },
        },
        {
            "name": "natural_dfa0_latch_tail_gate_0a8d08_d95404_clear_d954_ca2f_0606b_0728_07_002c02_e4cf_clear_0774_cdb",
            "description": "v187 natural-main-loop diagnostic plus explicit 0x0774..0x077B CDB mirror. E303 snapshots 0x0774..0x0777 before the READ branch; seed that mirror from the active CDB to distinguish missing branch status from missing command mirror state.",
            "patch": {
                "gdr_packet_session_model.main_loop_pc": "0x9000606B",
                "gdr_packet_session_model.host_event_gate.seed_dfa0_after_packet_tail": True,
                "gdr_packet_session_model.host_event_gate.seed_memory_after_latch": [
                    {"addr": "0x0A8D", "or": "0x08", "size": 1},
                    {"addr": "0xD954", "or": "0x04", "size": 1},
                    {"addr": "0x0728", "value": "0x07", "size": 1},
                    {"addr": "0x06F1", "value": "0x04", "size": 1},
                    {"addr": "0x002C", "or": "0x0002", "size": 2},
                    {"addr": "0x0774", "from_cdb": True, "offset": 0, "length": 8, "size": 1}
                ],
                "gdr_packet_session_model.firmware_ingress.enabled": False,
                "gdr_packet_session_model.host_event_gate.seed_memory_on_pc": [
                    {"pc": "0x9001CA2F", "addr": "0xD954", "clear": "0x04", "size": 1, "once": True},
                    {"pc": "0x9001E4CF", "addr": "0xD954", "clear": "0x04", "size": 1, "once": True}
                ]
            },
        },

        {
            "name": "firmware_dispatcher_9001d56d_0728_07_ca1a_d95404_e4cf_clear",
            "description": "v188 controlled dispatcher retest: corrected table index 0x07 with a transient D954 readiness bit applied exactly when 0x9001CA1A consumes it, then cleared at 0x9001E4CF. v187's pre-call OR was later overwritten to 0x60, so this tests the real consumed-edge timing under the 0x9001D56D wrapper.",
            "patch": {
                "gdr_packet_session_model.firmware_ingress.enabled": True,
                "gdr_packet_session_model.firmware_ingress.entry_pc": "0x9001D56D",
                "gdr_packet_session_model.firmware_ingress.trigger_pc": "0x90006608",
                "gdr_packet_session_model.firmware_ingress.frame_size": "0x0C",
                "gdr_packet_session_model.firmware_ingress.opcodes": ["0x28", "0xA8", "0xBE"],
                "gdr_packet_session_model.firmware_ingress.seed_memory": [
                    {"addr": "0x0728", "value": "0x07", "size": 1},
                    {"addr": "0x0A8D", "or": "0x08", "size": 1}
                ],
                "gdr_packet_session_model.firmware_ingress.seed_memory_on_pc": [
                    {"pc": "0x9001CA1A", "addr": "0xD954", "or": "0x04", "size": 1, "once": True},
                    {"pc": "0x9001E4CF", "addr": "0xD954", "clear": "0x04", "size": 1, "once": True}
                ],
                "gdr_packet_session_model.firmware_ingress.emit_05ec_source_event": True,
                "gdr_packet_session_model.firmware_ingress.use_05ec_source_for_read": True
            },
        },
        {
            "name": "firmware_dispatcher_9001d56d_0728_07_ca1a_d954read_df38fifo",
            "description": "v189 controlled dispatcher/FIFO diagnostic: enter the 0x9001D56D dispatcher wrapper with 0x0728=0x07 and 0x0A8D bit 0x08, but present D954 readiness and DF38..DF3F service bytes as host-interface read surfaces instead of pre-seeded SRAM. This checks whether the lower-level FIFO model can replace the v188 D954/DF38 seed workaround.",
            "patch": {
                "gdr_packet_session_model.firmware_ingress.enabled": True,
                "gdr_packet_session_model.firmware_ingress.entry_pc": "0x9001D56D",
                "gdr_packet_session_model.firmware_ingress.trigger_pc": "0x90006608",
                "gdr_packet_session_model.firmware_ingress.frame_size": "0x0C",
                "gdr_packet_session_model.firmware_ingress.opcodes": ["0x28", "0xA8", "0xBE"],
                "gdr_packet_session_model.firmware_ingress.seed_memory": [
                    {"addr": "0x0728", "value": "0x07", "size": 1},
                    {"addr": "0x0A8D", "or": "0x08", "size": 1}
                ],
                "gdr_packet_session_model.firmware_ingress.emit_05ec_source_event": True,
                "gdr_packet_session_model.firmware_ingress.use_05ec_source_for_read": True,
                "gdr_packet_session_model.host_event_gate.d954_ready_read": {
                    "enabled": True, "addr": "0xD954", "pc": "0x9001CA1A", "mask": "0x04",
                    "max_reads": "1", "return_pc": "0x9001E4CA", "ack_clears": True, "opcodes": ["0x28", "0xA8", "0xBE"]
                },
                "gdr_packet_session_model.host_event_gate.df38_fifo": {
                    "enabled": True, "base": "0xDF38", "width": "8", "cdb_offset": "0",
                    "opcodes": ["0x28", "0xA8", "0xBE"]
                }
            },
        },
        {
            "name": "natural_readonly_0606b_0728_07_d954read_df38fifo",
            "description": "v189 natural READ-only FIFO diagnostic: same READ-only natural dispatcher route as v188, but D954 and DF38 are no longer SRAM seeds. D954 is exposed as a consumed host ready read at 0x9001CA1A, and DF38..DF3F is supplied from the active CDB when 0x9001CA42 copies the service FIFO.",
            "patch": {
                "gdr_packet_session_model.main_loop_pc": "0x9000606B",
                "gdr_packet_session_model.host_event_gate.seed_dfa0_after_packet_tail": True,
                "gdr_packet_session_model.host_event_gate.seed_dfa0_after_packet_tail_opcodes": ["0x28", "0xA8", "0xBE"],
                "gdr_packet_session_model.host_event_gate.seed_memory_after_latch": [
                    {"addr": "0x0A8D", "or": "0x08", "size": 1, "opcodes": ["0x28", "0xA8", "0xBE"]},
                    {"addr": "0x0728", "value": "0x07", "size": 1, "opcodes": ["0x28", "0xA8", "0xBE"]},
                    {"addr": "0x06F1", "value": "0x04", "size": 1, "opcodes": ["0x28", "0xA8", "0xBE"]}
                ],
                "gdr_packet_session_model.host_event_gate.d954_ready_read": {
                    "enabled": True, "addr": "0xD954", "pc": "0x9001CA1A", "mask": "0x04",
                    "max_reads": "1", "return_pc": "0x9001E4CA", "ack_clears": True, "opcodes": ["0x28", "0xA8", "0xBE"]
                },
                "gdr_packet_session_model.host_event_gate.df38_fifo": {
                    "enabled": True, "base": "0xDF38", "width": "8", "cdb_offset": "0",
                    "opcodes": ["0x28", "0xA8", "0xBE"]
                },
                "gdr_packet_session_model.firmware_ingress.enabled": False
            },
        },
        {
            "name": "natural_readonly_0606b_0728_07_002c02_d954read_df38fifo",
            "description": "v189 natural READ-only FIFO diagnostic plus 0x002C bit 0x0002, preserving v188's E303 branch condition while replacing D954/DF38 SRAM seeding with consumed MMIO/FIFO reads.",
            "patch": {
                "gdr_packet_session_model.main_loop_pc": "0x9000606B",
                "gdr_packet_session_model.host_event_gate.seed_dfa0_after_packet_tail": True,
                "gdr_packet_session_model.host_event_gate.seed_dfa0_after_packet_tail_opcodes": ["0x28", "0xA8", "0xBE"],
                "gdr_packet_session_model.host_event_gate.seed_memory_after_latch": [
                    {"addr": "0x0A8D", "or": "0x08", "size": 1, "opcodes": ["0x28", "0xA8", "0xBE"]},
                    {"addr": "0x0728", "value": "0x07", "size": 1, "opcodes": ["0x28", "0xA8", "0xBE"]},
                    {"addr": "0x06F1", "value": "0x04", "size": 1, "opcodes": ["0x28", "0xA8", "0xBE"]},
                    {"addr": "0x002C", "or": "0x0002", "size": 2, "opcodes": ["0x28", "0xA8", "0xBE"]}
                ],
                "gdr_packet_session_model.host_event_gate.d954_ready_read": {
                    "enabled": True, "addr": "0xD954", "pc": "0x9001CA1A", "mask": "0x04",
                    "max_reads": "1", "return_pc": "0x9001E4CA", "ack_clears": True, "opcodes": ["0x28", "0xA8", "0xBE"]
                },
                "gdr_packet_session_model.host_event_gate.df38_fifo": {
                    "enabled": True, "base": "0xDF38", "width": "8", "cdb_offset": "0",
                    "opcodes": ["0x28", "0xA8", "0xBE"]
                },
                "gdr_packet_session_model.firmware_ingress.enabled": False
            },
        },
        {
            "name": "natural_readonly_0606b_0728_07_002c02_0774_cdb_d954read_df38fifo",
            "description": "v189 natural READ-only FIFO diagnostic plus 0x0774..0x077B CDB mirror. Use this if the FIFO route reaches CA42/E303 but the dispatcher still lacks the 0x0774 command mirror expected by the later READ branch.",
            "patch": {
                "gdr_packet_session_model.main_loop_pc": "0x9000606B",
                "gdr_packet_session_model.host_event_gate.seed_dfa0_after_packet_tail": True,
                "gdr_packet_session_model.host_event_gate.seed_dfa0_after_packet_tail_opcodes": ["0x28", "0xA8", "0xBE"],
                "gdr_packet_session_model.host_event_gate.seed_memory_after_latch": [
                    {"addr": "0x0A8D", "or": "0x08", "size": 1, "opcodes": ["0x28", "0xA8", "0xBE"]},
                    {"addr": "0x0728", "value": "0x07", "size": 1, "opcodes": ["0x28", "0xA8", "0xBE"]},
                    {"addr": "0x06F1", "value": "0x04", "size": 1, "opcodes": ["0x28", "0xA8", "0xBE"]},
                    {"addr": "0x002C", "or": "0x0002", "size": 2, "opcodes": ["0x28", "0xA8", "0xBE"]},
                    {"addr": "0x0774", "from_cdb": True, "offset": 0, "length": 8, "size": 1, "opcodes": ["0x28", "0xA8", "0xBE"]}
                ],
                "gdr_packet_session_model.host_event_gate.d954_ready_read": {
                    "enabled": True, "addr": "0xD954", "pc": "0x9001CA1A", "mask": "0x04",
                    "max_reads": "1", "return_pc": "0x9001E4CA", "ack_clears": True, "opcodes": ["0x28", "0xA8", "0xBE"]
                },
                "gdr_packet_session_model.host_event_gate.df38_fifo": {
                    "enabled": True, "base": "0xDF38", "width": "8", "cdb_offset": "0",
                    "opcodes": ["0x28", "0xA8", "0xBE"]
                },
                "gdr_packet_session_model.firmware_ingress.enabled": False
            },
        },
        {
            "name": "natural_readonly_0606b_0728_07_002c02_0536_mask02_d954read_df38fifo",
            "description": "v189 natural READ-only FIFO diagnostic plus scheduler byte 0x0536 bit 0x02. This is the broadest READ-only v189 probe and should be run after the narrower FIFO variants.",
            "patch": {
                "gdr_packet_session_model.main_loop_pc": "0x9000606B",
                "gdr_packet_session_model.host_event_gate.seed_dfa0_after_packet_tail": True,
                "gdr_packet_session_model.host_event_gate.seed_dfa0_after_packet_tail_opcodes": ["0x28", "0xA8", "0xBE"],
                "gdr_packet_session_model.host_event_gate.seed_memory_after_latch": [
                    {"addr": "0x0A8D", "or": "0x08", "size": 1, "opcodes": ["0x28", "0xA8", "0xBE"]},
                    {"addr": "0x0728", "value": "0x07", "size": 1, "opcodes": ["0x28", "0xA8", "0xBE"]},
                    {"addr": "0x06F1", "value": "0x04", "size": 1, "opcodes": ["0x28", "0xA8", "0xBE"]},
                    {"addr": "0x002C", "or": "0x0002", "size": 2, "opcodes": ["0x28", "0xA8", "0xBE"]},
                    {"addr": "0x0536", "or": "0x02", "size": 1, "opcodes": ["0x28", "0xA8", "0xBE"]}
                ],
                "gdr_packet_session_model.host_event_gate.seed_memory_on_pc": [
                    {"pc": "0x90016E4C", "addr": "0x0536", "or": "0x02", "size": 1, "once": False, "opcodes": ["0x28", "0xA8", "0xBE"]}
                ],
                "gdr_packet_session_model.host_event_gate.d954_ready_read": {
                    "enabled": True, "addr": "0xD954", "pc": "0x9001CA1A", "mask": "0x04",
                    "max_reads": "1", "return_pc": "0x9001E4CA", "ack_clears": True, "opcodes": ["0x28", "0xA8", "0xBE"]
                },
                "gdr_packet_session_model.host_event_gate.df38_fifo": {
                    "enabled": True, "base": "0xDF38", "width": "8", "cdb_offset": "0",
                    "opcodes": ["0x28", "0xA8", "0xBE"]
                },
                "gdr_packet_session_model.firmware_ingress.enabled": False
            },
        },
        {
            "name": "natural_readonly_0606b_0728_07",
            "description": "v188 natural dispatcher diagnostic: keep the 0x9000606B loop and corrected dispatcher index 0x0728=0x07, but apply the DFA0 latch/dispatcher seeds only for READ-family opcodes. v187 forced the E303 path during MODE SENSE (0x5A) and halted before the READ10 commands could run.",
            "patch": {
                "gdr_packet_session_model.main_loop_pc": "0x9000606B",
                "gdr_packet_session_model.host_event_gate.seed_dfa0_after_packet_tail": True,
                "gdr_packet_session_model.host_event_gate.seed_dfa0_after_packet_tail_opcodes": ["0x28", "0xA8", "0xBE"],
                "gdr_packet_session_model.host_event_gate.seed_memory_after_latch": [
                    {"addr": "0x0A8D", "or": "0x08", "size": 1, "opcodes": ["0x28", "0xA8", "0xBE"]},
                    {"addr": "0x0728", "value": "0x07", "size": 1, "opcodes": ["0x28", "0xA8", "0xBE"]},
                    {"addr": "0x06F1", "value": "0x04", "size": 1, "opcodes": ["0x28", "0xA8", "0xBE"]}
                ],
                "gdr_packet_session_model.firmware_ingress.enabled": False,
                "gdr_packet_session_model.host_event_gate.seed_memory_on_pc": [
                    {"pc": "0x9001CA1A", "addr": "0xD954", "or": "0x04", "size": 1, "once": True, "opcodes": ["0x28", "0xA8", "0xBE"]},
                    {"pc": "0x9001CA2F", "addr": "0xD954", "clear": "0x04", "size": 1, "once": True, "opcodes": ["0x28", "0xA8", "0xBE"]},
                    {"pc": "0x9001E4CF", "addr": "0xD954", "clear": "0x04", "size": 1, "once": True, "opcodes": ["0x28", "0xA8", "0xBE"]}
                ]
            },
        },
        {
            "name": "natural_readonly_0606b_0728_07_002c02",
            "description": "v188 natural READ-only dispatcher diagnostic plus the 0x002C bit mask 0x0002 required by the 0x9001E330/0x9001E36E READ branch checks.",
            "patch": {
                "gdr_packet_session_model.main_loop_pc": "0x9000606B",
                "gdr_packet_session_model.host_event_gate.seed_dfa0_after_packet_tail": True,
                "gdr_packet_session_model.host_event_gate.seed_dfa0_after_packet_tail_opcodes": ["0x28", "0xA8", "0xBE"],
                "gdr_packet_session_model.host_event_gate.seed_memory_after_latch": [
                    {"addr": "0x0A8D", "or": "0x08", "size": 1, "opcodes": ["0x28", "0xA8", "0xBE"]},
                    {"addr": "0x0728", "value": "0x07", "size": 1, "opcodes": ["0x28", "0xA8", "0xBE"]},
                    {"addr": "0x06F1", "value": "0x04", "size": 1, "opcodes": ["0x28", "0xA8", "0xBE"]},
                    {"addr": "0x002C", "or": "0x0002", "size": 2, "opcodes": ["0x28", "0xA8", "0xBE"]}
                ],
                "gdr_packet_session_model.firmware_ingress.enabled": False,
                "gdr_packet_session_model.host_event_gate.seed_memory_on_pc": [
                    {"pc": "0x9001CA1A", "addr": "0xD954", "or": "0x04", "size": 1, "once": True, "opcodes": ["0x28", "0xA8", "0xBE"]},
                    {"pc": "0x9001CA2F", "addr": "0xD954", "clear": "0x04", "size": 1, "once": True, "opcodes": ["0x28", "0xA8", "0xBE"]},
                    {"pc": "0x9001E4CF", "addr": "0xD954", "clear": "0x04", "size": 1, "once": True, "opcodes": ["0x28", "0xA8", "0xBE"]}
                ]
            },
        },
        {
            "name": "natural_readonly_0606b_0728_07_002c02_0774_cdb",
            "description": "v188 natural READ-only dispatcher diagnostic with the E303 0x0774..0x077B CDB mirror explicitly seeded from the active READ CDB. This distinguishes missing dispatcher timing from missing command mirror state.",
            "patch": {
                "gdr_packet_session_model.main_loop_pc": "0x9000606B",
                "gdr_packet_session_model.host_event_gate.seed_dfa0_after_packet_tail": True,
                "gdr_packet_session_model.host_event_gate.seed_dfa0_after_packet_tail_opcodes": ["0x28", "0xA8", "0xBE"],
                "gdr_packet_session_model.host_event_gate.seed_memory_after_latch": [
                    {"addr": "0x0A8D", "or": "0x08", "size": 1, "opcodes": ["0x28", "0xA8", "0xBE"]},
                    {"addr": "0x0728", "value": "0x07", "size": 1, "opcodes": ["0x28", "0xA8", "0xBE"]},
                    {"addr": "0x06F1", "value": "0x04", "size": 1, "opcodes": ["0x28", "0xA8", "0xBE"]},
                    {"addr": "0x002C", "or": "0x0002", "size": 2, "opcodes": ["0x28", "0xA8", "0xBE"]},
                    {"addr": "0x0774", "from_cdb": True, "offset": 0, "length": 8, "size": 1, "opcodes": ["0x28", "0xA8", "0xBE"]}
                ],
                "gdr_packet_session_model.firmware_ingress.enabled": False,
                "gdr_packet_session_model.host_event_gate.seed_memory_on_pc": [
                    {"pc": "0x9001CA1A", "addr": "0xD954", "or": "0x04", "size": 1, "once": True, "opcodes": ["0x28", "0xA8", "0xBE"]},
                    {"pc": "0x9001CA2F", "addr": "0xD954", "clear": "0x04", "size": 1, "once": True, "opcodes": ["0x28", "0xA8", "0xBE"]},
                    {"pc": "0x9001E4CF", "addr": "0xD954", "clear": "0x04", "size": 1, "once": True, "opcodes": ["0x28", "0xA8", "0xBE"]}
                ]
            },
        },
        {
            "name": "natural_readonly_0606b_0728_07_002c02_0536_mask02",
            "description": "v188 natural READ-only dispatcher diagnostic with corrected 0x0536 mask 0x02 in case the READ route still needs the scheduler event branch while avoiding non-READ dispatcher forcing.",
            "patch": {
                "gdr_packet_session_model.main_loop_pc": "0x9000606B",
                "gdr_packet_session_model.host_event_gate.seed_dfa0_after_packet_tail": True,
                "gdr_packet_session_model.host_event_gate.seed_dfa0_after_packet_tail_opcodes": ["0x28", "0xA8", "0xBE"],
                "gdr_packet_session_model.host_event_gate.seed_memory_after_latch": [
                    {"addr": "0x0A8D", "or": "0x08", "size": 1, "opcodes": ["0x28", "0xA8", "0xBE"]},
                    {"addr": "0x0728", "value": "0x07", "size": 1, "opcodes": ["0x28", "0xA8", "0xBE"]},
                    {"addr": "0x06F1", "value": "0x04", "size": 1, "opcodes": ["0x28", "0xA8", "0xBE"]},
                    {"addr": "0x002C", "or": "0x0002", "size": 2, "opcodes": ["0x28", "0xA8", "0xBE"]},
                    {"addr": "0x0536", "or": "0x02", "size": 1, "opcodes": ["0x28", "0xA8", "0xBE"]}
                ],
                "gdr_packet_session_model.firmware_ingress.enabled": False,
                "gdr_packet_session_model.host_event_gate.seed_memory_on_pc": [
                    {"pc": "0x9001CA1A", "addr": "0xD954", "or": "0x04", "size": 1, "once": True, "opcodes": ["0x28", "0xA8", "0xBE"]},
                    {"pc": "0x9001CA2F", "addr": "0xD954", "clear": "0x04", "size": 1, "once": True, "opcodes": ["0x28", "0xA8", "0xBE"]},
                    {"pc": "0x9001E4CF", "addr": "0xD954", "clear": "0x04", "size": 1, "once": True, "opcodes": ["0x28", "0xA8", "0xBE"]},
                    {"pc": "0x90016E4C", "addr": "0x0536", "or": "0x02", "size": 1, "once": False, "opcodes": ["0x28", "0xA8", "0xBE"]}
                ]
            },
        },

        {
            "name": "firmware_caller_9001e55e_df4c80",
            "description": "v179 caller-chain diagnostic: enter 0x9001E55E and force DF4C bit 0x80 so helper 0x900238A4 returns service-ready instead of the observed zero path.",
            "patch": {
                "gdr_packet_session_model.firmware_ingress.enabled": True,
                "gdr_packet_session_model.firmware_ingress.entry_pc": "0x9001E55E",
                "gdr_packet_session_model.firmware_ingress.trigger_pc": "0x90006608",
                "gdr_packet_session_model.firmware_ingress.frame_size": "0x04",
                "gdr_packet_session_model.firmware_ingress.opcodes": ["0x28", "0xA8", "0xBE"],
                "gdr_packet_session_model.firmware_ingress.seed_memory": [
                    {"addr": "0xDF4C", "or": "0x80", "size": 1}
                ],
                "gdr_packet_session_model.firmware_ingress.emit_05ec_source_event": True,
                "gdr_packet_session_model.firmware_ingress.use_05ec_source_for_read": True
            },
        },
        {
            "name": "firmware_caller_9001e55e",
            "description": "v178 caller-chain probe: splice at 0x9001E55E, above the packet-service dispatcher calls, instead of directly at the proven 0x9001E4E5 ingress body.",
            "patch": {
                "gdr_packet_session_model.firmware_ingress.enabled": True,
                "gdr_packet_session_model.firmware_ingress.entry_pc": "0x9001E55E",
                "gdr_packet_session_model.firmware_ingress.trigger_pc": "0x90006608",
                "gdr_packet_session_model.firmware_ingress.frame_size": "0x04",
                "gdr_packet_session_model.firmware_ingress.opcodes": ["0x28", "0xA8", "0xBE"],
                "gdr_packet_session_model.firmware_ingress.emit_05ec_source_event": True,
                "gdr_packet_session_model.firmware_ingress.use_05ec_source_for_read": True
            },
        },
        {
            "name": "no_d108_autocomplete",
            "description": "Disable D108 data-phase auto-completion. Tests whether firmware continues into the transfer helper/dispatcher instead of synthetic completion.",
            "patch": {"gdr_packet_session_model.host_event_gate.complete_data_phase_on_d108": False},
        },
        {
            "name": "no_d108_no_irq_after",
            "description": "Disable D108 auto-completion and the synthetic data-phase IRQ edge.",
            "patch": {
                "gdr_packet_session_model.host_event_gate.complete_data_phase_on_d108": False,
                "gdr_packet_session_model.host_event_gate.irq_after_d108_data_phase": False,
            },
        },
        {
            "name": "hold_d950_40",
            "description": "Keep post-frame D950 at 0x40 longer instead of decaying after four reads.",
            "patch": {
                "gdr_packet_session_model.host_event_gate.d950_zero_after_post_frame_reads": "-1",
                "gdr_packet_session_model.host_event_gate.clear_d950_after_tail": False,
            },
        },
        {
            "name": "d950_zero_immediate",
            "description": "Decay D950 immediately after FE frame. Tests whether lingering 0x40 holds us on the current bypass path.",
            "patch": {"gdr_packet_session_model.host_event_gate.d950_zero_after_post_frame_reads": "0"},
        },
        {
            "name": "post_frame_d950_10",
            "description": "Use 0x10 as post-frame D950 status instead of 0x40.",
            "patch": {"gdr_packet_session_model.host_event_gate.post_frame_d950_value": "0x10"},
        },
        {
            "name": "post_frame_d950_80",
            "description": "Use 0x80 as post-frame D950 status; useful because older traces saw 0x80/0x40 transitions around packet copy.",
            "patch": {"gdr_packet_session_model.host_event_gate.post_frame_d950_value": "0x80"},
        },
        {
            "name": "no_clear_08a8_frame",
            "description": "Do not clear 0x08A8 after FE frame. Tests whether 0x08A8 edge timing gates the READ dispatcher.",
            "patch": {"gdr_packet_session_model.host_event_gate.clear_08a8_after_frame": False},
        },
        {
            "name": "no_clear_08a8_tail",
            "description": "Do not clear 0x08A8 after packet tail.",
            "patch": {"gdr_packet_session_model.host_event_gate.clear_08a8_after_tail": False},
        },
        {
            "name": "packet_flags_00",
            "description": "Present FE packet with flags byte 0x00 instead of 0x20.",
            "patch": {"gdr_packet_session_model.packet_flags": "0x00"},
        },
        {
            "name": "packet_flags_40",
            "description": "Present FE packet with flags byte 0x40 instead of 0x20.",
            "patch": {"gdr_packet_session_model.packet_flags": "0x40"},
        },
        {
            "name": "raw_cdb_frame_no_fe",
            "description": "Experimental: packet flags 0 and D108 autocomplete disabled; still uses current frame source but removes the most aggressive helper.",
            "patch": {
                "gdr_packet_session_model.packet_flags": "0x00",
                "gdr_packet_session_model.host_event_gate.complete_data_phase_on_d108": False,
                "gdr_packet_session_model.host_event_gate.irq_after_d108_data_phase": False,
                "gdr_packet_session_model.host_event_gate.d950_zero_after_post_frame_reads": "0",
            },
        },
    ]


def apply_patch(cfg: dict[str, Any], patch: dict[str, Any]) -> dict[str, Any]:
    out = deepcopy(cfg)
    for path, val in patch.items():
        deep_set(out, path, val)
    # Keep mirrored trace-local session model synchronized for older emulator versions.
    if isinstance(out.get("trace"), dict) and isinstance(out.get("gdr_packet_session_model"), dict):
        out["trace"]["gdr_packet_session_model"] = deepcopy(out["gdr_packet_session_model"])
    return out


def run_cmd(cmd: list[str], cwd: Path | None, log_path: Path) -> int:
    print("[RUN]", " ".join(str(x) for x in cmd))
    proc = subprocess.run(cmd, cwd=str(cwd) if cwd else None, text=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
    log_path = ensure_parent(log_path)
    log_path.write_text(proc.stdout, encoding="utf-8", errors="ignore")
    sys.stdout.write(proc.stdout)
    return int(proc.returncode)


def summarize_proof(proof: dict[str, Any]) -> dict[str, Any]:
    out = {
        "outcome": "NOT_RUN",
        "trace_rows": proof.get("trace_rows", 0),
        "command_windows": proof.get("command_windows", 0),
        "read_handler_hit_count": 0,
        "lba_byte_pc_hit_count": 0,
        "assembled_lba_pc_hit_count": 0,
        "plus_150_pc_hit_count": 0,
        "cdb_lba_seen_count": 0,
        "lba_state_seen_count": 0,
        "packet_state_seen_count": 0,
        "firmware_lba_source_seen_count": 0,
        "proven_probe_count": 0,
        "partial_probe_count": 0,
        "blocked_probe_count": 0,
        "notable_hits": [],
    }
    findings = proof.get("findings") or []
    if findings:
        out["outcome"] = findings[0].get("outcome", "UNKNOWN")
    notable: set[str] = set()
    for f in findings:
        for p in f.get("probes") or []:
            out["read_handler_hit_count"] += len(p.get("read_handler_hits") or [])
            out["lba_byte_pc_hit_count"] += len(p.get("lba_byte_read_pc_hits") or [])
            out["assembled_lba_pc_hit_count"] += len(p.get("assembled_lba_pc_hits") or [])
            out["plus_150_pc_hit_count"] += len(p.get("plus_150_pc_hits") or [])
            if p.get("cdb_lba_bytes_observed"):
                out["cdb_lba_seen_count"] += 1
            checks = p.get("checks") or {}
            if checks.get("lba_state_seen"):
                out["lba_state_seen_count"] += 1
            if checks.get("packet_state_seen"):
                out["packet_state_seen_count"] += 1
            if checks.get("firmware_lba_source_seen"):
                out["firmware_lba_source_seen_count"] += 1
            po = str(p.get("outcome") or "").upper()
            if po == "PROVEN":
                out["proven_probe_count"] += 1
            elif po == "PARTIAL":
                out["partial_probe_count"] += 1
            elif po == "BLOCKED":
                out["blocked_probe_count"] += 1
            for key in ("read_handler_hits", "lba_byte_read_pc_hits", "assembled_lba_pc_hits", "plus_150_pc_hits"):
                for h in p.get(key) or []:
                    notable.add(str(h))
    out["notable_hits"] = sorted(notable)
    return out


def write_summary_md(path: Path, report: dict[str, Any]) -> None:
    lines: list[str] = []
    lines.append("# HLDS v191 LBA route scan report")
    lines.append("")
    lines.append("This is an offline route-discovery run. Its purpose is to find a host-interface gate configuration that reaches the firmware READ/LBA dispatcher candidates from the v169 proof pack. v191 keeps the proven v189/v190 natural FIFO route and adds convenient multi-LBA sweep presets. It does not start iSCSI and does not use Windows.")
    lines.append("")
    lines.append(f"stem={report.get('stem')}")
    lines.append(f"variants={len(report.get('variants') or [])}")
    lines.append("")
    lines.append("| variant | rc | outcome | handler | cdb-byte PCs | assembled | +150 | cdb seen | lba state | packet state | fw source | notable hits |")
    lines.append("|---|---:|---|---:|---:|---:|---:|---:|---:|---:|---:|---|")
    for v in report.get("variants") or []:
        s = v.get("summary") or {}
        hits = ", ".join((s.get("notable_hits") or [])[:8])
        lines.append(
            f"| `{v.get('name')}` | {v.get('returncode')} | {s.get('outcome')} | "
            f"{s.get('read_handler_hit_count')} | {s.get('lba_byte_pc_hit_count')} | "
            f"{s.get('assembled_lba_pc_hit_count')} | {s.get('plus_150_pc_hit_count')} | "
            f"{s.get('cdb_lba_seen_count')} | {s.get('lba_state_seen_count')} | "
            f"{s.get('packet_state_seen_count')} | {s.get('firmware_lba_source_seen_count')} | {hits} |"
        )
    lines.append("")
    lines.append("Interpretation: if all variants have zero handler/CDB-byte/assembled-LBA hits, the current packet presentation still bypasses the firmware READ dispatcher. The next fix is not another sidecar mapping; it is a still-lower-level packet FIFO/MMIO route model that presents both CDB bytes and the scheduler/status state needed for firmware to reach the candidate path without the controlled microcall.")
    p = ensure_parent(path)
    p.write_text("\n".join(lines) + "\n", encoding="utf-8")


def _parse_hex_bytes(text: Any) -> list[int]:
    if text is None:
        return []
    if isinstance(text, (bytes, bytearray)):
        return [int(x) & 0xFF for x in text]
    if isinstance(text, list):
        return [parse_int(x) & 0xFF for x in text]
    out: list[int] = []
    for tok in str(text).replace(",", " ").split():
        tok = tok.strip()
        if not tok:
            continue
        try:
            out.append(int(tok, 16) & 0xFF)
        except Exception:
            out.append(parse_int(tok) & 0xFF)
    return out


def _bytes_to_hex(data: bytes | bytearray | list[int]) -> str:
    return " ".join(f"{int(b) & 0xFF:02X}" for b in data)


def _read_cdb_lba_blocks(cdb: list[int]) -> tuple[int | None, int | None, str | None]:
    if not cdb:
        return None, None, None
    op = cdb[0] & 0xFF
    if op in (0x28, 0xBE) and len(cdb) >= 10:
        lba = ((cdb[2] << 24) | (cdb[3] << 16) | (cdb[4] << 8) | cdb[5]) & 0xFFFFFFFF
        blocks = ((cdb[7] << 8) | cdb[8]) & 0xFFFF
        if blocks == 0 and op == 0x28:
            blocks = 65536
        return lba, blocks, "READ10" if op == 0x28 else "READ_CD"
    if op == 0xA8 and len(cdb) >= 12:
        lba = ((cdb[2] << 24) | (cdb[3] << 16) | (cdb[4] << 8) | cdb[5]) & 0xFFFFFFFF
        blocks = ((cdb[6] << 24) | (cdb[7] << 16) | (cdb[8] << 8) | cdb[9]) & 0xFFFFFFFF
        return lba, blocks, "READ12"
    return None, None, None


def _resolve_existing_path(path_text: str | None, *, base_dir: Path | None = None) -> Path | None:
    if not path_text:
        return None
    p = Path(str(path_text))
    candidates = [p]
    if base_dir is not None and not p.is_absolute():
        candidates.append(base_dir / p)
    for c in candidates:
        try:
            if c.exists():
                return c
        except Exception:
            pass
    # On Windows, a drive-qualified path will resolve normally.  Return the raw
    # path so the caller can still open it on the user's host.
    return p


def apply_media_read_source(cfg: dict[str, Any], *, base_config_path: Path, media_path: str | None, delta: int, sector_size: int | None, sample_bytes: int, strict: bool) -> dict[str, Any]:
    """Replace synthetic READ data_in_hex with bytes read from the selected ISO.

    v191 intentionally used deterministic pattern bytes for READ payloads.  v192
    makes the media/source-window layer explicit: for READ-family commands, use
    host_lba + delta as the ISO/source LBA and record that mapping in each command
    as media_source metadata.  The emulator emits this metadata during D108 data
    phase completion and hlds_gdr_media_window_report.py verifies it.
    """
    out = deepcopy(cfg)
    media = out.get("media") if isinstance(out.get("media"), dict) else {}
    iso_text = media_path or media.get("iso_path") or media.get("image") or media.get("path")
    iso_path = _resolve_existing_path(iso_text, base_dir=base_config_path.parent)
    if sector_size is None:
        try:
            sector_size = parse_int(media.get("sector_size"), 2048)
        except Exception:
            sector_size = 2048
    sector_size = max(1, int(sector_size or 2048))
    if iso_path is None:
        raise SystemExit("--read-data-source media requested but no ISO path was found in --media-path or base config media.iso_path")
    if strict and not iso_path.exists():
        raise SystemExit(f"--read-data-source media requested but ISO path does not exist: {iso_path}")
    sess = out.get("gdr_packet_session_model") if isinstance(out.get("gdr_packet_session_model"), dict) else {}
    commands = sess.get("commands") if isinstance(sess.get("commands"), list) else []
    replaced = 0
    with open(iso_path, "rb") as f:
        for cmd in commands:
            if not isinstance(cmd, dict):
                continue
            cdb = _parse_hex_bytes(cmd.get("cdb", cmd.get("cdb_hex", cmd.get("packet_cdb"))))
            host_lba, blocks, opname = _read_cdb_lba_blocks(cdb)
            if host_lba is None or blocks is None:
                continue
            if str(cmd.get("direction", "in")).strip().lower() != "in":
                continue
            media_lba = (int(host_lba) + int(delta)) & 0xFFFFFFFF
            byte_count = int(blocks) * sector_size
            offset = media_lba * sector_size
            f.seek(offset)
            data = f.read(byte_count)
            if len(data) != byte_count:
                raise SystemExit(f"short ISO read for {cmd.get('tag')}: media_lba={media_lba} blocks={blocks} bytes={len(data)}/{byte_count} path={iso_path}")
            cmd.pop("data_in", None)
            cmd["data_in_hex"] = _bytes_to_hex(data)
            cmd["data_source"] = "media"
            cmd["media_source"] = {
                "enabled": True,
                "formula": "host_lba_plus_delta",
                "host_lba": int(host_lba),
                "host_lba_hex": f"0x{int(host_lba) & 0xFFFFFFFF:08X}",
                "media_lba": int(media_lba),
                "media_lba_hex": f"0x{int(media_lba) & 0xFFFFFFFF:08X}",
                "delta": int(delta),
                "blocks": int(blocks),
                "op_name": opname,
                "sector_size": sector_size,
                "byte_count": byte_count,
                "source": str(iso_path),
                "sample_first_hex": _bytes_to_hex(data[:max(0, sample_bytes)]),
            }
            replaced += 1
    out.setdefault("v192_media_window", {})
    out["v192_media_window"].update({
        "enabled": True,
        "read_commands_replaced": replaced,
        "formula": "host_lba_plus_delta",
        "delta": int(delta),
        "sector_size": sector_size,
        "source": str(iso_path),
    })
    print(f"[MEDIA] read_data_source=media replaced_read_commands={replaced} delta={delta} sector_size={sector_size} source={iso_path}")
    return out


def parse_probe_read_override(text: str) -> dict[str, Any]:
    """Parse --probe-read LBA[:BLOCKS] for quick focused route proofs."""
    parts = str(text).strip().split(":", 1)
    lba = parse_int(parts[0])
    blocks = parse_int(parts[1], 1) if len(parts) > 1 and parts[1] else 1
    return {"host_lba": lba, "blocks": max(1, blocks)}




def preset_probe_reads(name: str) -> list[dict[str, Any]]:
    """Small focused READ(10) sets for v191-v193 route/media proof sweeps.

    These are intentionally modest: data-in source tracing emits one row per
    payload byte, so very large block counts create huge CSVs.
    """
    key = (name or "").strip().lower().replace("_", "-")
    presets = {
        # v191/v192 route proof sets.
        "standard": ["0", "1", "0x1234", "306111"],
        "adjacent": ["306109", "306110", "306111", "306112"],
        "blocks": ["306111:1", "306111:2", "306112:4"],
        "tiny": ["0", "306111"],

        # v193 media-window stress sets.  Keep these modest because trace.csv
        # still records one data-source row per byte.  These are intended to
        # catch off-by-one, block-count, and early Windows probe behavior before
        # live iSCSI bridge integration.
        "media-boundary": ["0:1", "1:1", "31:1", "32:1", "33:1", "0x1234:1", "306079:1", "306080:1", "306111:1", "306112:1"],
        "media-stress": ["0:1", "1:2", "32:4", "0x1234:2", "306111:4", "306112:4"],
        "windows-lite": ["0:1", "16:1", "32:1", "64:2", "0x800:2", "0x1234:2", "306111:4"],
    }
    if key not in presets:
        raise SystemExit(f"unknown --probe-read-sweep preset {name!r}; choose one of: {', '.join(sorted(presets))}")
    return [parse_probe_read_override(x) for x in presets[key]]


def override_proof_pack_probe_reads(proof_pack: dict[str, Any], probes: list[dict[str, Any]]) -> dict[str, Any]:
    if not probes:
        return proof_pack
    out = deepcopy(proof_pack)
    changed = False
    for f in out.get("findings") or out.get("proofs") or []:
        dyn = f.get("dynamic_expectation") or f.get("dynamic_test")
        if isinstance(dyn, dict):
            dyn["probe_reads"] = [dict(p) for p in probes]
            changed = True
    if not changed:
        out.setdefault("findings", []).append({
            "type": "lba_probe_override",
            "dynamic_expectation": {"run_mode": "offline_cpu_trace", "probe_reads": [dict(p) for p in probes]},
        })
    return out


def main() -> int:
    ap = argparse.ArgumentParser(description="Run offline GDR-8050L LBA route-scan proof variants")
    ap.add_argument("--base-config", required=True)
    ap.add_argument("--proof-pack", required=True)
    ap.add_argument("--stem", default="gdr8050l_0l23_v174_lba_route_scan")
    ap.add_argument("--out-dir", default=None)
    ap.add_argument("--run", action="store_true")
    ap.add_argument("--validate", action="store_true")
    ap.add_argument("--python", default=sys.executable)
    ap.add_argument("--emulator", default="mn103s_emu.py")
    ap.add_argument("--cwd", default=None)
    ap.add_argument("--max-cycles", type=int, default=600000)
    ap.add_argument("--firmware-image", default=None)
    ap.add_argument("--keep-all-base-commands", action="store_true")
    ap.add_argument("--probe-read", action="append", default=[], metavar="LBA[:BLOCKS]", help="override proof-pack probe_reads for focused quick runs, e.g. --probe-read 306111 or --probe-read 0x4ABBF:1")
    ap.add_argument("--probe-read-sweep", action="append", default=[], metavar="PRESET", help="append a focused READ sweep preset: tiny, standard, adjacent, blocks, media-boundary, media-stress, or windows-lite")
    ap.add_argument("--read-data-source", choices=["pattern", "media"], default="pattern", help="v192: keep synthetic pattern READ payloads, or replace READ data_in with bytes read from the configured ISO/media image")
    ap.add_argument("--media-path", default=None, help="v192: ISO/media image path override for --read-data-source media; defaults to base config media.iso_path")
    ap.add_argument("--media-lba-delta", default="306080", help="v192: source/media LBA delta. media_lba = host_lba + delta. Default preserves the previous bridge-window assumption for proof testing.")
    ap.add_argument("--media-sector-size", default=None, help="v192: sector size for media reads; defaults to base config media.sector_size or 2048")
    ap.add_argument("--media-sample-bytes", type=int, default=32, help="v192: number of first bytes saved in per-command media_source metadata")
    ap.add_argument("--no-strict-media-path", action="store_true", help="v192: do not pre-check media path existence before opening; useful only for unusual Windows path handling")
    ap.add_argument("--variant", action="append", default=[], help="specific variant name(s) to run; default is all")
    args = ap.parse_args()

    base = load_json_or_jsonc(args.base_config)
    proof_pack = load_json_or_jsonc(args.proof_pack)
    probe_reads: list[dict[str, Any]] = []
    for preset in args.probe_read_sweep or []:
        probe_reads.extend(preset_probe_reads(preset))
    probe_reads.extend(parse_probe_read_override(x) for x in (args.probe_read or []))
    if probe_reads:
        proof_pack = override_proof_pack_probe_reads(proof_pack, probe_reads)
    out_dir = (Path(args.out_dir) if args.out_dir else Path("emutests") / safe_name(args.stem)).resolve()
    out_dir.mkdir(parents=True, exist_ok=True)

    all_specs = variant_specs()
    wanted = set(args.variant or [])
    if wanted:
        specs = [v for v in all_specs if v["name"] in wanted]
        missing = sorted(wanted - {v["name"] for v in specs})
        if missing:
            raise SystemExit(f"unknown variant(s): {', '.join(missing)}")
    else:
        specs = all_specs

    report: dict[str, Any] = {
        "format": "hlds-lba-route-scan-report",
        "version": 1,
        "stem": args.stem,
        "base_config": str(Path(args.base_config)),
        "proof_pack": str(Path(args.proof_pack)),
        "variants": [],
    }

    cwd = Path(args.cwd) if args.cwd else None
    for idx, spec in enumerate(specs, start=1):
        name = safe_name(spec["name"])
        # v179d: keep artifact paths short.  The previous long
        # emutests/<stem>/<variant>/<stem>.<variant>.* layout hit Windows
        # MAX_PATH and was reported as FileNotFoundError.  The report still
        # records the full variant name; the filesystem gets short names.
        vdir = out_dir / short_variant_dir(idx, name)
        vdir.mkdir(parents=True, exist_ok=True)
        trace_csv = (vdir / "trace.csv").resolve()
        cfg = build_config(
            base,
            proof_pack,
            f"{args.stem}.{name}",
            trace_csv,
            max_cycles=args.max_cycles,
            keep_all_base_commands=args.keep_all_base_commands,
            firmware_image=args.firmware_image,
        )
        cfg = apply_patch(cfg, spec.get("patch") or {})
        if args.read_data_source == "media":
            cfg = apply_media_read_source(
                cfg,
                base_config_path=Path(args.base_config),
                media_path=args.media_path,
                delta=parse_int(args.media_lba_delta),
                sector_size=(parse_int(args.media_sector_size) if args.media_sector_size is not None else None),
                sample_bytes=max(0, int(args.media_sample_bytes)),
                strict=not bool(args.no_strict_media_path),
            )
        cfg_path = ensure_parent(vdir / "config.json")
        write_json_artifact(cfg_path, cfg)
        run_log = ensure_parent(vdir / "run.log")
        proof_json_path = ensure_parent(vdir / "proof.json")
        proof_md_path = ensure_parent(vdir / "proof.md")
        rc: int | None = None
        summary: dict[str, Any] = {"outcome": "GENERATED"}
        if args.run:
            rc = run_cmd([args.python, args.emulator, str(cfg_path)], cwd=cwd, log_path=run_log)
        if args.validate and trace_csv.exists():
            proof = validate_lba_trace(trace_csv, proof_pack)
            proof_json_path = write_json_artifact(proof_json_path, proof)
            write_lba_md(proof_md_path, proof)
            summary = summarize_proof(proof)
            print(f"[SCAN] {name}: outcome={summary.get('outcome')} handler={summary.get('read_handler_hit_count')} cdb_seen={summary.get('cdb_lba_seen_count')} assembled={summary.get('assembled_lba_pc_hit_count')} plus150={summary.get('plus_150_pc_hit_count')}")
        elif args.validate:
            print(f"[WARN] trace missing for {name}: {trace_csv}")
            summary = {"outcome": "NO_TRACE"}
        report["variants"].append({
            "name": name,
            "artifact_dir": str(vdir),
            "description": spec.get("description", ""),
            "patch": spec.get("patch") or {},
            "config": str(cfg_path),
            "trace_csv": str(trace_csv),
            "run_log": str(run_log),
            "proof_json": str(proof_json_path) if proof_json_path.exists() else None,
            "proof_md": str(proof_md_path) if proof_md_path.exists() else None,
            "returncode": rc,
            "summary": summary,
        })

    report_json = out_dir / "route_scan_report.json"
    report_md = out_dir / "route_scan_report.md"
    report_json = write_json_artifact(report_json, report)
    write_summary_md(report_md, report)
    print(f"[OK] wrote route-scan report JSON: {report_json}")
    print(f"[OK] wrote route-scan report MD: {report_md}")
    return 0


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