# 26 — risc-v-gsp-rm-debugger architecture: full picture, workflow and API surfaces
> Status: implemented, documented and tested (audit: docs/27-TOOL-AUDIT.md; value demos: docs/25-VALUE-VALIDATION.md).
> The architecture map for the risc-v-gsp-rm-debugger suite: how the pieces fit, which
> surface to call, and which document specifies each part. Every claim here is
> implemented in the tree or explicitly marked as a gated roadmap design.
> Development-only: never shipped, never a driver dependency, never in a guest.
## 1. What risc-v-gsp-rm-debugger is (and is not)
risc-v-gsp-rm-debugger runs GSP/uproc firmware in a controllable machine, observes it with
Frida-like primitives, records a durable session, changes the machine's inputs
(including injected code), and compares runs as evidence for `vgsp`
emulation changes.
| It is | It is not |
|---|---|
| A firmware runtime microscope (execution + memory, L0/L1) | A time, interrupt or device model (L2/L3 absent — [06](06-ACCURACY.md)) |
| A scriptable `Session` API with two backends | A GDB wrapper; there is no GDB dependency |
| A recorded-evidence system with a versioned format | A product component or a guest agent |
| A static/dynamic join with Ghidra | A decompiler or a CUDA-corpus replacement |
| An automation layer (harnesses, MCP, CI) | A fuzzing service against hardware; simulator/re-hosted code only |
## 2. The suite as a whole
```text
front doors control plane evidence
CLI/REPL/scripts ─┐
MCP (stdio) ──────┼─► GreenEyeAPI ─► Session ─┬─► QEMU-RSP backend ──┐
Ghidra bridge ────┤ (plugins) Memory │ qemu-system-riscv64│
harness/ ─────────┤ SymbolDB └─► native backend ────┼─► session dir
rmharness ────────┘ Recorder sim/machine.py │ events.jsonl
└─► analyze / compare /
export_hits / bundles
```
```mermaid
flowchart LR
CLI["CLI/REPL/scripts"] --> CORE
MCP["MCP server"] --> CORE
GH["Ghidra bridges"] --> CORE
HAR["harness/*.py"] --> CORE
RMH["rmharness (read-only)"] --> CORE
CORE["GreenEyeAPI + Session
hooks, watchpoints, call, run_until
Memory, SymbolDB, Recorder"]
CORE --> QB["QEMU-RSP backend"] --> SESS
CORE --> NB["native backend (M1/M1.5)"] --> SESS
SESS["session dir, greeneye-session/1"] --> REP["analyze / compare / export_hits / MCP / bundles"]
RMH --> SESS
```
Status: transport, core, recording, both backends (native M1 trace included),
injection ABI v1, both Ghidra bridges, traces, device map, harnesses, MCP (R3)
and `rmharness` are implemented and exercised. The `Backend` protocol
refactor (16 §5), the H2.2 QEMU device model, RM-core injection (H1.3) and the
H3 M2–M6 milestones are design/roadmap only ([16](16-CUSTOM-SIMULATOR.md)).
## 3. Session core: hooks, watchpoints and dispatch
`Session` ([greeneye/session.py](../greeneye/session.py)) owns all stop-driven
state and is backend-agnostic: every stop reads registers, resolves the PC,
records an event, invokes matching hooks/watchpoints, then resumes.
```mermaid
sequenceDiagram
S->>FW: set breakpoint / watchpoint
loop run(duration, max_stops)
FW-->>S: stop reply (T/S/W)
S->>S: resolve PC via SymbolDB
S->>R: bp_hit / watch_hit / stop / step
alt symbol entry
S->>H: on_enter(hit) with a0..a7
S->>S: arm return breakpoint from ra (LIFO)
S->>FW: continue
else return breakpoint
S->>H: on_leave(hit), a0 as retval
else watchpoint (Z2/Z3/Z4)
S->>FW: clear watch, step over store, re-arm
S->>H: callback + store PC
end
S->>R: on_event(kind, hit)
end
S->>R: session_end on close
```
| Primitive | Semantics / limits |
|---|---|
| `breakpoint(addr, kind=sw\|hw, temporary=)` | sw is stepped over transparently; hw uses the backend's `Z1` |
| `hook(target, on_enter, on_leave)` | leave is a return breakpoint on `ra`; tail calls/interrupt returns are not tracked |
| `watch(target, size, kind=w\|r\|a, callback)` | QEMU stops *at* the access; Session clears/steps/re-arms; store PC is recorded |
| `run(duration, max_stops)` | services stops until a budget or exit; the deadline path interrupts once |
| `run_until(target, timeout)` | temporary breakpoint; raises `RspTimeout` when not reached |
| `call(target, args, timeout)` | Frida-like remote call; restores `RESTORE_REGS` around it |
| `step(n)`, `note(text)`, `on_event(kind, fn)` | single-step with dispatch; annotate and observe the stream |
Instructions between stops are invisible unless the TCG plugin
(`qemu/greeneye_trace.c`) or the native trace (`sim/trace.py`) is on
(01 §11, 16 §1.1a).
## 4. Backend abstraction: QEMU-RSP and native
`Session` assumes a small RSP-shaped primitive set; `run_until` and `call`
live above it and work on every backend. M1 implements the same method
surface (`greeneye/sim_backend.py`) without the planned `Backend` protocol
refactor (16 §5); the table is the M2+ target.
```mermaid
flowchart LR
S["Session (backend-independent)"]
S -->|"read/write reg+mem, step, continue, Z0-Z4"| RC
S -->|"same surface"| ST
subgraph QEMU["QEMU-RSP backend (default)"]
QT["QemuTarget
qemu-system-riscv64 virt"] --- RC["RspClient gdbstub packets"]
end
subgraph NAT["Native backend (--backend native)"]
ST["SimTarget / SimClient"] --- MC["sim.machine.Machine
RAM + UART + decode loop"]
MC --- TR["sim.trace.Trace exec/access"]
MC --- DM["DeviceMap lenient MMIO"]
end
```
| Session need | QEMU-RSP | Native |
|---|---|---|
| registers/memory | `g`/`p`/`P`, `m`/`M`/`X` chunked to `PacketSize` | direct register file and memory map |
| breakpoints/watchpoints | `Z0`–`Z4` | dispatch-loop tables; same stop semantics |
| snapshots | region copies over RSP | direct map copy; machine moments are M2+ |
| virtual time | host wall clock | deterministic instruction count (`SIM_IPS`) |
| full-access trace | TCG plugin (`-plugin`) | in-process `sim/trace.py`, `--trace` |
| device environment | QEMU `virt` only (no TU104) | device map + lenient MMIO; real devices H2.2 |
| injection | debugger writes + `Session.call` | same SDK ABI; RM interception is M5 |
`--backend qemu|native` is accepted by `demo`, `run`, `attach`, `record`,
`inject` and `ghidra`; `attach --port` and QEMU `--trace` are QEMU-only;
`--trace` is native-only (16 §1.2).
## 5. Recording and the evidence format
A session directory is the durable artifact (`greeneye-session/1`,
[greeneye/record.py](../greeneye/record.py)). The event stream is the map; the
snapshot and blob files are the dump.
```text
dumps/-/
session.json # format, name, created, counts, snapshot inventory, meta
events.jsonl # append-only JSON lines: {"t": monotonic, "type": ...}
snapshots/*.bin.z # zlib-compressed region captures (sha256 recorded)
blobs/* # QEMU logs, trace.jsonl, rmharness stdout, plugin dumps
summary.md # rendered analysis
```
```mermaid
flowchart LR
E["Session stops, memory ops,
probe/plugin/injection events"] --> R["Recorder.event()"]
R --> J["events.jsonl (append-only)"]
M["Memory.snapshot()"] --> Z["snapshots/*.bin.z + sha256"]
B["Recorder.blob()"] --> BD["blobs/*: trace.jsonl, qemu.log"]
X["rmharness external runs"] --> J
J --> A["analyze: summary.md"]
J --> C["compare: A/B delta report"]
J --> G["export_hits: greeneye_hits.tsv"]
J --> S10["value_demos s10: manifest + M1-M6 skeleton"]
```
Event classes: core stop/memory events (`session_start`, `image_loaded`,
`hook`, `watch`, `bp_hit`, `leave`, `watch_hit`, `stop`, `step`, `mem_read`,
`mem_write`, `snapshot*`, `probe*`, `plugin*`, `note`, `exit`, `session_end` —
01 §6); injection (`service_injected`, `call_enter`, `call_return` — 12 §6);
traces (`exec`/`access`); M2/M3 native bus (`device`/`irq`/`schedule` — 16
§10); `rmharness` (`rm_harness_run`, `t=0.0` with epoch `start`/`end` — 17 §5).
Format invariants: append-only (partial sessions stay readable); additive
versioning with migration notes (V2); `compare` treats `t`, host paths and
backend identity as non-semantic; a claim without a session is not evidence
(06 §7, 25 §1).
## 6. Injection SDK lifecycle
The SDK (`sdk/greeneye_service.h`, `sdk/injected_demo.c`) builds
position-independent ELF64 `EM_RISCV` blobs; `greeneye/inject.py` loads,
registers and invokes them. ABI v1: `ge_u64 (*)(ge_u64 cmd, ge_u64 a0,
ge_u64 a1)`, magic `0x47455356`, entry `ge_demo_service_dispatch`.
```mermaid
sequenceDiagram
Dev->>Inj: load_service(elf, slot=6, load_addr)
Inj->>FW: write PT_LOAD segments into RAM
Inj->>FW: patch gsp_ctrl.handlers[slot] entry
Inj->>Rec: service_injected + mem_write events
Dev->>Inj: invoke_remote(cmd, a0, a1)
Inj->>FW: Session.call(entry, args)
Rec-->>Rec: call_enter / call_return
Dev->>FW: mailbox = 16 + slot (arg0=cmd, arg1, arg2)
FW->>FW: dispatch handlers[slot]
FW-->>Rec: hook/watch events + result field
```
Demo registration: mailbox commands 16..23 select slots 0..7; slot 6 is the
SDK default; control-block offsets (`mailbox +0x20`, `handlers +0x150`) are
demo-specific and must come from `greeneye/inject.py` constants (12 §4).
Cleanup is not automatic in v1; record the injection so runs start from a
known state. Roadmap: RM-core shim, service registry and `log`/`stub`/
`forward` interception at M5/H1.3 (12 §8, 16 §11).
## 7. Ghidra bridges (two directions)
The static/dynamic join uses three address spaces — file vaddr, Ghidra program
addr, runtime addr — with a bias per source (01 §5, 05 §1).
```mermaid
sequenceDiagram
G->>B: {"id":n,"cmd":"run_until","args":{"target":"0x..."}}
B->>S: run_until(addr) (temporary breakpoint)
S->>T: continue; service intervening stops
T-->>S: stop at addr
S-->>B: {addr, symbol, kind}
B-->>G: JSON response
G->>B: read_mem / read_reg / call / snapshot / hits / export_hits
B->>R: hits and notes recorded in the session
R-->>G: greeneye_hits.tsv (via MarkGreenEyeHits.java)
```
| Tool | Purpose | Doc |
|---|---|---|
| `symbols export` + `SymbolDB` loaders | Ghidra TSV/JSON → runtime symbol DB | [05](05-GHIDRA-BRIDGE.md) |
| `greeneye.ghidra.export_hits` | session hits → `addrhitssymbolannotation` | 05 |
| `ExportForGreenEye.java` | Ghidra → `addrnamesize` TSV | 05 |
| `MarkGreenEyeHits.java` | annotate functions with runtime hits + bookmarks | 05 |
| `GreenEyeControl.java` + `risc-v-gsp-rm-debugger.sh ghidra` | drive a live session from GhidraScripts over localhost JSON-lines | [13](13-GHIDRA-CONTROL.md) |
Bridge commands: `ping`, `info`, `resolve`, `bp_set`, `bp_clear`, `bp_list`,
`watch_set`, `run_until`, `run`, `step`, `read_mem`, `write_mem`, `read_reg`,
`write_reg`, `call`, `snapshot`, `note`, `hits`, `export_hits`, `close`
(13 §3). Unauthenticated by design; localhost only.
## 8. MCP server tool surface
`plugins/mcp_server.py` serves the active `Session` over JSON-RPC 2.0 on
stdio (or loopback TCP) with no MCP library; read/observe by default,
`write_mem`/`call_function` require `--allow-write`, `inject_service`
requires `--allow-inject` ([20](20-MCP-SERVER.md)).
```mermaid
flowchart LR
AG["AI agent (Claude Code, opencode)"] -->|"JSON-RPC 2.0 over stdio"| M["plugins/mcp_server.py"]
M --> READ["read: session_info, symbols_resolve,
read_mem, hexdump, snapshot, plugins_list"]
M --> OBS["observe: bp_set/clear, watch_set,
run, run_until, step (recorded)"]
M --> WR["write (gated): write_mem, call_function"]
M --> INJ["inject (gated): inject_service"]
M --> TR["trace: trace_start/stop"]
M --> AN["analysis: analyze_current, compare_sessions, export_hits"]
M --> HN["harness: probe_firmware, mutation_run,
portability_matrix_run"]
M --> CL["session: close"]
READ & OBS & WR & INJ & TR & AN & HN --> S["Session + Recorder"]
```
All results are one JSON text block; absent modules return a structured
`not_available` error; stdout carries protocol frames only (20 §4, §8). Full
argument schemas for all 24 tools are in 20 §5.
## 9. Automation harness
`harness/` turns the playbooks into repeatable, self-describing runs. Modules
bootstrap the tool root and use the QEMU backend by default (18, 19, 21, 28,
29); `common.py` provides flat-image relocation, session helpers and bounded
log reads.
```mermaid
flowchart TB
FW["firmware ELF / .fwimage"] --> PM & MU & CD & RE & VD & LV
SYM["Ghidra exports + symbol DBs"] --> PM & MU & CD & RE & VD & LV
STIM["stimulus / probe spec"] --> PM & MU & CD & RE & VD & LV
SEED["seed + budgets"] --> PM & MU & CD & RE & VD & LV
subgraph H["harness/"]
PM["portability_matrix.py R6/H4.1"]
MU["mutation_harness.py R5/H4.3"]
CD["control_discovery.py H4"]
RE["reach.py H4.1/H4.5"]
VD["version_diff.py H4.1"]
CR["ci_release.py"]
LV["value_demos/ s01..s10"]
end
VD --> CR
PM --> CR
MU --> CR
PM & MU & CD & RE & VD & LV & CR --> OUT["reports + sessions + findings.jsonl
+ compare/ + bundle-manifest.json"]
```
| Entry point | Purpose | Outputs | Doc |
|---|---|---|---|
| `portability_matrix.py` | probe + symbols + fixed scenario + compare across targets; M1–M6 skeleton | `portability-report.json/.md`, `symbols/`, `sessions/`, `compare/` | 18 §1 |
| `mutation_harness.py` | seeded one-input mutation, outcome classes, per-attempt session | `findings.jsonl`, `summary.md`, `sessions/` | 18 §2 |
| `control_discovery.py` | sweep command/call surfaces; classify handler/default/trap/fault | `findings.jsonl`, `discovery-report.json`, `control-discovery.md` | 19 §3 |
| `reach.py` | reachability, trigger discovery, required args and effects for one symbol | `reach-report.json/.md`, `probe.json`, sessions | 19 §4 |
| `version_diff.py` | old vs new image: shape, symbols, corpus hints, probe, behavior, M1–M6 | `version-diff.json/.md`, `corpus/`, `compare/` | 21 §2 |
| `ci_release.py` | version_diff → matrix → mutation smoke, then hash the bundle | `release-report.json`, `bundle-manifest.json` | 21 §4 |
| `value_demos/run_all.py` | ten fixed scenarios (reply, counters, discovery, reach, diff, inject, version, mutation, Ghidra, evidence) | `artifacts/results.json` + per-demo JSON | 25 |
| `port_driver_versions.py` | version-list sweep of version_diff + matrix + mutation (dry-run default; unadmitted pilot) | per-version bundles + `sweep-summary.json/.md` | 28 §2 |
| `port_new_device.py` | device-port scaffold: profile, probe, symbols, matrix, checklist (dry-run default; unadmitted pilot) | `device-port.json/.md`, `port-checklist.json/.md`, bundle | 28 §3 |
| `package_sim.py` | build/run a trial simulation package (unadmitted pilot) | package tree + `manifest.json`, `results.json` | 29 |
| `common.py` | shared setup (relocate to `0x80000000`, open session, bounded logs) | library | 18 §1.2 |
Harness limits are intentional: QEMU/re-hosted/demo code only; no hardware,
guest, kernel module or existing gdbstub; bounded budgets; deterministic
findings for a fixed seed (18 §5, 19 §8, 21 §7).
## 10. Device map and lenient MMIO data flow
`greeneye-device-map/1` is versioned JSON keyed by
`(address, width, direction)` with values, hit counts and firmware PCs
([24](24-DEVICE-MAP.md)). It records what the firmware touched first and lets
the native interpreter step past unmodelled registers to collect the next
ones; it feeds the H2.2 QEMU device model and the H3 profile DSL.
```mermaid
flowchart LR
P["risc-v-gsp-rm-debugger.sh probe (raw .fwimage in QEMU)"] -->|"blobs/qemu.log + qemu_first_fault"| H["device_map.py harvest"]
H --> MAP["greeneye-device-map/1
entries + meta (bias, sha256)"]
MAP --> LOOK["DeviceMap.lookup
zero / value / unimplemented"]
LOOK --> LEN["Native Machine(unknown_mmio=lenient)"]
LEN --> LOG["machine.mmio_log / mmio_unknown"]
LEN --> LEARN["learn_mmio=True: source trace entries"]
LEARN --> MAP
MAP --> PROF["docs/16 profile DSL
falcon/csb/ptimer/pmu stubs"]
PROF --> H22["H2.2 QEMU device model"]
LOG --> NEXT["next unmapped wall; promote to manual"]
NEXT --> MAP
```
Honest scope: lenient execution proves an unmodelled access *can* be served
deterministically; it does not model read-back, side effects, interrupts or
timing, and a zero that keeps the firmware running is a hypothesis, not a
silicon fact (24 §4).
## 11. Workflow architecture: scenario → evidence → emulation change
The canonical loop (H4.5, 06 §5, 07 P4, 25 §7). Each stage names its gate.
| Stage | Action / instrument | Success criterion | Failure signal |
|---|---|---|---|
| 1. Scenario | state the firmware question and the smallest stimulus (one command/control/boot phase) | question maps to a symbol or surface | no observable exists → add one (probe/plugin) |
| 2. Choose instrument | hooks/watchpoints for semantics; TCG/native trace for coverage; `run_until`/`call` for reachability; injection for new code | stop set answers the question without flooding | session too large/noisy → filter PCs, watch fields only |
| 3. Run | fresh bounded session; fixed stimulus, seed and budgets | run completes; UART/banner liveness holds | timeout, trap loop, wedged target → capture and retry clean |
| 4. Record | `greeneye-session/1` with snapshots and notes | events + snapshots + hashes written | missing `session.json`/empty analyzer → recording off |
| 5. Compare | `compare` against a reference (old firmware, hardware/VFIO capture, self-diff) | deltas explained symbolically; self-diff is zero | unexplained hit/watch/write delta → investigate first |
| 6. Decide | fill M1–M6 with the reference method (06 §5); mutation/triage for error paths | metric table improves or stays equal | metric claimed without a reference capture |
| 7. Implement | change `vgsp` (never the simulator for a product fix); keep the stimulus | physical confirmation for behaviour changes (06 §6, 14 §7) | product change from a simulator-only observation |
| 8. Regression | re-run step 3 with an unchanged stimulus; golden sessions/CI (`make test`, `risc-v-gsp-rm-debugger.yml`) | profile improves or stays equal; CI green | regression → revert and re-derive |
```mermaid
flowchart LR
Q["Question"] --> OBS["Observe
probe / hooks / watch / trace"]
OBS --> ORI["Orient
analyze + symbol decode + snapshots"]
ORI --> DEC["Decide
compare + M1-M6 + mutation"]
DEC --> ACT["Act
implement in vgsp"]
ACT --> REG["Regression
same stimulus, same budgets"]
REG -->|"improved / equal"| SHIP["Evidence pack
sessions + report + hashes"]
REG -->|"regressed"| Q
SHIP --> OBS
```
OODA compression evidence (analog, measured) is in 25 §7: hardware cycles are
minutes and campaigns hours/days; the risc-v-gsp-rm-debugger path for the same question
class is sub-second to seconds plus scripting.
## 12. API surfaces by tool
### 12.1 CLI (`risc-v-gsp-rm-debugger.sh`, `greeneye/cli.py`; usage in [02](02-USAGE.md))
| Command | Purpose | Not for |
|---|---|---|
| `info` | environment/tooling check | validating a firmware image |
| `demo` | canned boot + hooks + watch + record on the bundled uproc | booting real GSP (use `probe`) |
| `run ` | run a Python instrumentation session | shell pipelines (write Python) |
| `attach` / `launch` | interactive REPL / keep a target alive by port | injecting into a live product |
| `record` | boot and record with watches/snapshots/trace | post-processing an existing session |
| `probe ` | bounded run of a raw image; first divergence report | booting the image (no TU104 model) |
| `inject` | load/register/invoke an SDK service | shipping or hot-patching signed firmware |
| `ghidra` | serve a live session to GhidraScripts | network exposure (localhost only) |
| `analyze ` / `compare ` | markdown report / symbolic A/B delta | proving accuracy without a reference |
| `symbols export\|resolve` | build/inspect a symbol DB | recovering types/decompilation |
| `plugins` | list discoverable plugins | loading untrusted code into the core |
| `python3 -m greeneye.device_map harvest\|show\|merge\|demo` | MMIO first-touch map data | a device model or timing source |
### 12.2 Python entry points (recipes in [03](03-SCRIPTING.md))
| Entry point | Purpose | Not for |
|---|---|---|
| `ge.launch/attach`, `Session.hook/watch/run/run_until/call/step/note` | session lifecycle and instrumentation | in-process C instrumentation |
| `Memory.read/write/hexdump/search_*/snapshot/diff/render_diff` | memory inspection and region diff | full-machine snapshots (M2+) |
| `SymbolDB.load_elf/*_tsv/*_json`, `resolve/annotate` | address-space-aware symbols | type recovery from Ghidra |
| `structs.schema_from_json/decode/render` | declarative struct decoding | automatic struct discovery |
| `probes.load_spec/install` | JSON probe specs (no arbitrary code) | complex control flow (write a script) |
| `inject.load_service/invoke_remote/invoke_via_firmware` | ABI v1 service load/invoke | product features or persistence |
| `ghidra.build_symbol_db/export_hits/corpus_location` | Ghidra TSV in/out and corpus location | replacing Ghidra analysis |
| `analyze.render_summary/compare_sessions/parse_qemu_faults/load_trace` | offline reports and QEMU fault parsing | live control |
| `record.Recorder/load_events/load_meta` | session writing/reading | a database (files are the interface) |
| `device_map.harvest_*/DeviceMap.*` | MMIO map authoring/merge | device semantics |
| `sim_backend.launch_native`, `sim.trace.Trace/make_trace` | native backend + trace knobs | timing/concurrency claims |
| `errors.*` (`GreenEyeError`, `RspTimeout`, ...) | typed failure surface | retrying a wedged target blindly |
### 12.3 Plugin API (`register(ge)`, [04](04-PLUGINS.md))
`ge` exposes live objects (`session`, `memory`, `symbols`, `recorder`),
instrumentation (`hook`, `watch`, `on_event`, `resolve`), CLI extension
(`add_command(name, fn, help)`, `commands`) and audit logging (`log`). A
raising plugin is recorded as `plugin_error` and skipped. Not for: shipping
dependencies, assuming a specific firmware, or hiding unrecorded state.
## 13. Extensibility and invariants
Where extension lives:
* **Plugins** (`plugins/`, `GREENEYE_PLUGINS`) — runtime RE tools, harnesses,
environment updates, agent integrations (04).
* **Harness modules** (`harness/`) — new sweeps/reports over the public
`Session`/analysis primitives; a new call surface is a `Surface` subclass
(19 §5.1).
* **SDK services** (`sdk/`) — PIC blobs behind ABI v1; v2 adds an RM-core call
table (12 §8).
* **Device maps/profiles** — versioned data (`greeneye-device-map/1`, the
`greenmachine-profile/1` DSL in 16 §7), never core special cases.
* **MCP tools** (`plugins/mcp_server.py`) — add tools in the plugin, keep the
core stdlib-only (20 §10).
Invariants (enforced by tests or review):
1. **stdlib-only core**: `greeneye/` and `sim/` import only the standard
library; optional deps live in plugins/harnesses.
2. **Session format**: `greeneye-session/1` is append-only and evolves
additively with migration notes (V2).
3. **Backend parity**: every backend satisfies the same `Session` primitives,
so scripts, probes, plugins, recordings and comparisons survive backend
changes (16 §4, R0).
4. **ABI**: blobs declare an ABI version and capability metadata; breaking
changes need a shim and a doc note (12 §8, V3).
5. **Markers**: the in-development notices were removed on 2026-10-02 after
the tool audit (27) and value demos (25); gated items stay marked as such.
Never shipped, never in a guest, never a driver dependency.
6. **Evidence**: no accuracy, portability or security claim without a recorded
A/B session and an explicit L2/L3 fidelity statement (06 §5–6, 25 §10).
7. **Provenance**: tracked docs describe components and behaviour;
conclusions cite recorded evidence (V6).
## 14. Diagram and document index
Section diagrams and their backing docs: §2 suite (00, 01, README); §3
dispatch (01, 03); §4 backends (01 §8, 16 §4–5); §5 recording (01 §6, 09, 12
§6, 16 §10, 17 §5); §6 injection (11, 12); §7 Ghidra (05, 13); §8 MCP (20);
§9 harness (18, 19, 21, 28, 29); §10 device map (24, 16 §7); §11 OODA (06, 07 P4, 25
§7). Roadmap keys: R3 = MCP (08), R5 = mutation (18), R6 = multi-version (18,
21), H1.3 = RM service injection (RISC-V-GSP-RM-DEBUGGER-ROADMAP), H2.2 = QEMU device
model, H3/M1–M6 = native simulator (16), H4 = adversarial navigation (14),
H7 = agentic platform (15, 20).