This is the normative specification for the driver-lab host tooling at //tools/driver-lab. The companion proxy driver specification lives at //src/devices/driver-lab/SPEC.md.
Phase 1 -- the unclaimed-node proxy workflow, which requires no Driver Manager changes -- is delivered first. Phase 2 -- in-situ driver debugging via an embedded library -- extends host tooling to active, bound drivers without managed takeover or Driver Manager changes, requiring no major contract change. Phase 3 -- software state, runtime knobs, and adaptive bug verification -- extends host plans, the Python API, and agent skills to drive single-build software state and concurrency experiments over StateBank resources.
Changeset numbers (CS) are global across this specification and //src/devices/driver-lab/SPEC.md, whose changesets interleave with these; gaps in the numbering below are proxy driver changesets. Every changeset marks itself complete in this list when it lands, so at any commit this list shows which changes precede and which follow that commit.
[driver-lab] Host tooling specification -- this document.[driver-lab] Transport-neutral host tooling core -- frozen data models (18), grant resolution with the TOML store (10.3-10.5), and plan validation, canonicalization, and digests carrying the reserved takeover keys (11).[driver-lab] Evidence recording + proxy transport abstraction -- hashed manifest-last evidence bundles (16) and the wire-contract-shaped transport seam with the fake proxy target (12, 20.3).[driver-lab] Consent resolution and the plan-run API -- interactive consent with exact-rule persistence and fail-closed unattended behavior (10.2), and DriverLab.run_plan composing prepare/execute/finalize with mode and guarantee resolution before any connection (6.2, 7.3, 14).[driver-lab] fuchsia-controller FIDL transport adapter -- ProxyTransport over the generated bindings, exercised through real FIDL encoding on the build host (12, 20.3).[driver-lab] Add host CLI tools -- driver-lab.pyz with run, permissions list/explain/add/revoke, plan digest/validate, and spec exit categories (10.5, 12, 13).[driver-lab] Add live-target conformance runbook -- ephemeral registration and test-node activation exercising the real driver end to end (14, 20.4), verified on the emulator.[driver-lab] Host node discovery via fuchsia.driver.development -- typed node enumeration, bound driver inspection, unclaimed-node detection over FIDL, and expected_unclaimed plan verification (7.1, 8.3, 14.2, 15; milestones H0, H1).[driver-lab] Direct mode client and published-protocol workflow -- direct-mode capability validation, typed protocol discovery, protocol transactions, and target audit exclusion (7.1, 8.2, 15; milestone H1).[driver-lab] Target ceiling policy manifests and verification -- immutable target ceiling manifests, canonical JSON and deterministic SHA-256 digests, non-widening narrowing validation, and one-shot vs poll enforcement (9.2, 9.6, 22, 23.1; milestone P1 remainder).Remaining phase 1 work:
[driver-lab] Bounded sequences and mutation in proxy driver (11.4, 11.5, 12.2-12.4, 13; milestone P3).[driver-lab] Host tooling support for mutation and sequences (11, 14; milestone P3 / H4).[driver-lab] Driver-shaped public Python API -- connect/attach, HardwareSession, MmioRegion, and representative protocol resources (6.1, 9; milestone H4).[driver-lab] Stop-path hardening and cancellation (19; milestone P2).[driver-lab] Host CLI subtool expansion and verified teardown (8.3, 12, 14.2; milestone H3 remainder).[driver-lab] Protocol resources, typed endpoints, and heterogeneous sequence (8.1, 8.4, 9.5, 11.5, 11.6, 12, 14.2; milestone P4).[driver-lab] Interrupt observation and local acknowledgement (11.7, 17; milestone P5).[driver-lab] Structured configuration and per-class rate limits (in //src/devices/driver-lab/SPEC.md).[driver-lab] Serial capture and independent recovery integration (3, 14.1; milestone H5).Phase: 1 -- new-driver development. Covers direct mode and proxy access on unclaimed nodes. In-situ debugging of existing drivers via an embedded library is specified in the Phase 2 section of this document and in //src/devices/driver-lab/SPEC.md, and requires no changes to this contract.
Scope: host Python API, ffx integration, operator authorization, orchestration, and evidence
Companion specifications: the proxy driver specification at //src/devices/driver-lab/SPEC.md, and the Phase 2 section of this document
The host tooling provides a single, driver-shaped Python programming model for interactive hardware exploration and agent-assisted Fuchsia driver development. The same entry point supports two target access modes:
In phase 1 the proxy binds directly to an otherwise unclaimed development node. In-situ debugging of existing, active drivers through an embedded library is specified in phase 2; the wire and transport contracts here support both deployment paths without breaking changes.
The tooling also exposes an ffx interface, coordinates out-of-band recovery, enforces operator-consent policy, freezes executable plans, and preserves the raw evidence needed to reproduce and audit an experiment.
This specification deliberately does not define how the proxy maps MMIO, handles interrupts, or enforces its target-side policy. Those requirements are owned by the proxy driver specification at //src/devices/driver-lab/SPEC.md.
The terms must, must not, should, and may describe requirements, recommendations, and optional behavior respectively.
The following terms have specific meanings:
The host tooling must:
core realm for the target-side endpoint.ffx target transports without precluding a serial-capable transport; serial support itself is separate ffx-team-owned work and does not gate phase 1.fuchsia.driver.development surface rather than a parallel discovery protocol.The host tooling must not:
Driver Agent / Python script / human CLI | v Driver-shaped Python API and plan/evidence layer | v FFX transport adapter network / USB / serial-capable target transport | v Driver Manager control plane discovery: fuchsia.driver.development (takeover protocol arrives in phase 2) | | | direct | proxy v v Published driver protocol Isolated proxy driver running driver remains bound bound to an unclaimed node Paniolo or equivalent --- serial / liveness / reset / power recovery
The Driver Manager integration is a control plane. Hardware operations run in the production driver in direct mode or in the isolated proxy driver in proxy mode. Driver Manager must not become a generic MMIO executor.
The target endpoint must remain available on engineering systems whose normal component topology or networking is unavailable. Discovery and channel routing therefore belong at the Driver Framework/Driver Manager layer rather than at a stable moniker under core. This placement was suggested by driver-framework engineering during early design review.
Two requirements are distinct here. The target-side control plane must not live under core; serving it from Driver Manager in the bootstrap realm satisfies that. End-to-end operation without core additionally requires a transport that terminates outside core; no such ffx transport exists today, so this design must not preclude one, and serial transport is tracked as separate work with the ffx team. The public Python API must not expose the transport choice.
The standalone proxy (lab_proxy) is the Phase 1 mechanism for private hardware access on unclaimed nodes. For nodes with an active, bound driver, Phase 2 introduces the embedded library (driver_lab_rust / driver_lab_cpp) for in-situ debugging. Rather than unbinding the driver, the embedded library preserves live hardware state and solves clock/power gating by running inside the active driver component, using cooperative quiesce locks and interrupt event taps to prevent concurrent interference.
The public Python API must preserve, as closely as practical:
Discovery, transport, consent, target policy, batching, and evidence may be implemented underneath or alongside this API. They must not flatten every hardware resource into an unrelated generic remote-control interface.
For protocol-backed resources, use generated Python FIDL types directly or a thin shape-preserving adapter. For MMIO, expose a remote MmioRegion object whose scalar operations resemble the MMIO operations used by an on-device driver. Target-local batches are an explicit timing and safety facility, not the only programming model.
The host must report:
Unsupported operations fail before hardware access. A direct-mode connection must not pretend that private MMIO, private interrupts, target audit, or target-local batch timing is available.
Raw evidence is finalized before an agent may label an observation verified, contradicted, or inconclusive. Interpretation is a derived artifact and must refer back to exact operation results.
The experiment channel is not the recovery channel. Serial capture, target liveness, reset, and power control remain usable after loss of the FIDL or ffx path.
Direct mode connects to a protocol already published by the active driver. Discovery may use an aggregated service, devfs compatibility path, or another Driver Manager-provided route, but those details are hidden below the public API.
Direct mode is appropriate for:
fuchsia.hardware.registers endpoint; andDirect mode does not provide private-register reflection. It provides only what the running driver publishes.
Proxy mode is appropriate when the caller needs:
In phase 1 the node must be unclaimed: the proxy binds through engineering-only node metadata and existing registration and bind mechanisms. If a normal driver is bound, the node requires managed takeover, which is phase 2 and is reported as unsupported. Either way the proxy backend must be compatible with the resources offered by the parent.
The caller may request direct, proxy, or auto. A proxy request may also require a specific activation policy: bind-unclaimed (phase 1) or takeover (reserved; rejected as unsupported until phase 2).
auto may select direct mode only when every plan requirement is satisfied by direct mode. It must never:
The selected mode and its guarantees are included in the canonical plan and evidence.
Discovery reuses the existing fuchsia.driver.development and fuchsia.driver.registrar protocols; this specification adds no parallel discovery surface and phase 1 requires no Driver Manager changes. The engineering-only takeover protocol is defined in the Phase 2 section of this document and in //src/devices/driver-lab/SPEC.md. The exact FIDL syntax is revision-dependent, but the semantics below are normative.
Discovery uses fuchsia.driver.development (GetNodeInfo, GetDriverInfo, and related methods). Work package 1 produces a gap matrix mapping each required field to that surface in the selected revision. Discovery must provide:
fuchsia.driver.development does not report them directly.Boot identity, proxy generation, resource descriptions, and their digests are reported by the bound proxy's Describe, not by Driver Manager. Takeover eligibility, active proxy-access state, and pending restoration failure are reported by the phase 2 takeover protocol, not by node discovery.
Physical addresses and raw handles are never returned.
Direct mode connects only to a protocol that discovery reported. Phase 1 uses existing devfs or aggregated-service routing; the host tooling validates the typed protocol/service selector itself and never accepts an arbitrary path or component moniker from plan JSON. A typed Driver Manager route may be added later if selector validation proves insufficient.
Phase 1 activates the proxy only on nodes with no bound driver:
fuchsia.driver.registrar);The caller cannot select an arbitrary driver package. The managed-takeover activation path (BeginProxyAccess, EndProxyAccess, exclusive leases, and restoration) is specified in phase 2; phase 1 plans, sessions, and evidence reserve the fields those flows require.
The illustrative API below fixes responsibilities, not final spelling:
class DriverLab: @classmethod async def connect( cls, target: str, *, transport: str = "auto", timeout_s: float = 10.0, ) -> "DriverLab": ... async def list_nodes(self) -> list[NodeSummary]: ... async def describe_node(self, node_id: str) -> NodeDescription: ... async def attach( self, node_id: str, *, mode: Literal["auto", "direct", "proxy"], requirements: AccessRequirements, context: RunContext, ) -> "HardwareSession": ... class HardwareSession: @property def capabilities(self) -> SessionCapabilities: ... async def mmio(self, resource: str) -> "MmioRegion": ... async def gpio(self, resource: str) -> "Gpio": ... async def spi(self, resource: str) -> "Spi": ... async def i2c(self, resource: str) -> "I2c": ... async def serial(self, resource: str) -> "Serial": ... async def clock(self, resource: str) -> "Clock": ... async def reset(self, resource: str) -> "Reset": ... async def interrupt(self, resource: str) -> "Interrupt": ... async def sequence(self, operations: list[Operation]) -> ExecutionReport: ... class MmioRegion: async def read32(self, offset: int) -> int: ... async def write32( self, offset: int, value: int, *, mask: int, expected_before: int | None = None, expected_mask: int | None = None, require_readback: bool = True, ) -> WriteResult: ... async def poll32( self, offset: int, *, expected: int, mask: int, interval_s: float, timeout_s: float, ) -> PollResult: ...
sequence is available only when the selected backend can preserve the requested ordering and timing. Resource-specific methods remain the canonical programming model.
Each public hardware method should document:
A future translation checker may reject or flag scripts that use experiment-only facilities. Automatic translation is not required in the initial implementation.
Use one asyncio event loop per process. All waits have host deadlines. Target deadlines must expire slightly before host deadlines so a normal target timeout can be distinguished from transport loss.
Do not call blocking sleep from asynchronous code. Hold strong references to serial, health, evidence, and execution tasks until each has completed or been explicitly canceled and joined.
Authorization has two layers:
A host grant never widens the target ceiling. A target accepting a session-specific allowlist does not prove who approved it; approval provenance is evidence, not cryptographic authorization.
An MMIO read that matches neither an active plan approval nor a persistent grant must pause for operator consent. In unattended operation it fails closed. For an unknown register, the active plan approval must itself contain an explicit human read decision; classifying a plan as read-only is not sufficient authorization.
The interactive choices are:
The UI must warn that reads may clear status, consume FIFO data, acknowledge events, or fault when hardware dependencies are disabled.
“Read all registers” is not a primitive permission:
The UI may approve an exact displayed set in one interaction, but the stored and target-enforced result remains a set of exact access rules.
Consent gates register-class access regardless of transport. A direct-mode read through a published raw-register endpoint such as fuchsia.hardware.registers resolves against the same plan approvals and persistent grants as a proxy MMIO read. Semantic protocol operations (GPIO, SPI, I2C, serial, clock, reset) are governed by plan approval and target policy, not per-offset read consent.
Register metadata is a first-class deliverable, not an ambient assumption: its schema, provenance, and review process must be specified before consent expansion depends on it (see open decisions).
In V1 the proxy session allowlist is fixed when the session opens. Consent granted mid-session takes effect by closing the session and reopening it with the expanded allowlist; a narrowing-only ExtendAllowlist operation is a deferred open decision.
Permission-resolution evidence records the source of every decision -- persistent rule, allow-once, always-allow, deny-once, always-deny, or fail-closed -- so a run's authorization basis is auditable. A persistent deny short-circuits prompting for the rest of the plan.
A persistent read grant must include:
It must not match solely by nodename, boot ID, devfs path, or dynamic service instance.
The resource-description digest covers the resource kind, logical size, provider identity, and any stable metadata needed to prevent an old grant from matching a changed mapping. A boot change alone need not invalidate a persistent read grant; a resource-description change does.
At minimum distinguish:
A one-shot read grant does not implicitly authorize polling. Poll authorization includes maximum interval frequency and timeout. All target-local execution remains subject to proxy limits.
The persistent store is a checked, human-readable TOML file. For example:
schema_version = 1 [[read_grants]] target_scope = "example-engineering-target" node_id = "example-device" resource_digest = "sha256:..." resource = "control" offset = 0x3c width = 32 access = "read_once" decision = "allow" approved_at = "2026-07-29T23:10:00Z" reason = "Reviewed against the device register specification"
Interactive persistence must:
The CLI provides list, explain, add, and revoke operations. Manual config edits are allowed but undergo the same validation on load.
Persistent read permission never authorizes a write. A mutating plan requires:
The first implementation should not offer “always allow arbitrary write.”
Plans are data, not executable code. A representative plan is:
{ "schema_version": 1, "run_id": "2026-07-29T23-10-00Z-status-read", "case_id": "device-status-observation", "target": { "selector": "lab-target", "expected_boot_id": "..." }, "node": { "id": "example-device", "expected_unclaimed": true, "expected_resource_digest": "..." }, "access": { "mode": "proxy", "activation": "bind-unclaimed", "requires_target_policy": true, "requires_target_audit": true, "requires_target_local_timing": false }, "operations": [ { "kind": "mmio_read32", "resource": "control", "offset": "0x3c" } ] }
The plan schema reserves the managed-takeover fields (expected bound driver, expected topology generation, expected proxy identity, restoration policy) for phase 2. For mutation the canonical plan records target policy digest, proxy generation, exact operation fields, and recovery intent.
Plans must not support:
Canonicalization normalizes numeric representations, key order, and operation order before hashing.
The supported command surface should be available as an ffx subtool so that target discovery and transport selection remain in the Fuchsia tooling layer. Proposed commands:
ffx driver-lab list ffx driver-lab describe --node <id> ffx driver-lab direct --node <id> --protocol <typed-selector> ffx driver-lab bind-proxy --node <id> ffx driver-lab end-proxy --node <id> ffx driver-lab run --plan <json> --evidence-dir <dir> ffx driver-lab permissions list ffx driver-lab permissions explain --plan <json> ffx driver-lab permissions revoke --grant <id>
ffx driver-lab takeover arrives in phase 2.
The Python package links fuchsia-controller in process and uses the generated fuchsia.driver.lab bindings, behind a transport-neutral ProxyTransport interface so tests run against a wire-contract-faithful fake and future transports slot in without public API changes. Transport implementation must not leak into hardware scripts.
Until the ffx subtool exists, the same operations ship as a standalone driver-lab.pyz host tool with JSON on stdout, diagnostics on stderr, and the exit categories below.
A serial-capable path, once the separately tracked transport work lands, must be tested without the normal core topology before it is considered supported. It does not gate phase 1.
Structured results go to stdout and diagnostics/prompts go to stderr or the dedicated interactive UI channel.
Recommended exit categories:
0 success 2 local argument, plan, or permission error 3 stale target/node/resource expectation 4 operation failure or partial execution 5 transport/channel failure 6 target reboot or liveness failure 7 evidence persistence failure 8 proxy activation failure 9 restoration failure (reserved; used by phase 2) 10 unsupported capability or unsafe downgrade
Ctrl-C cancels bounded execution, finalizes available evidence, and ends proxy access when one is active. A second interrupt may request out-of-band escalation but must not abandon proxy-access state silently.
The managed-takeover workflow is specified in phase 2. The phase 1 unclaimed-node workflow is:
Preparation performs no hardware operations.
Target proxy policy and audit are absent unless the published protocol itself provides equivalent guarantees.
Each run gets a new directory:
evidence/<run_id>/ manifest.json plan.requested.json plan.canonical.json approval.json permission-resolution.json target.description.json node.before.json access.capabilities.json proxy-access.jsonl execution.response.json operations.jsonl target-audit.jsonl node.after.json restoration.json serial.log host-events.jsonl interpretation.json
Files that do not apply to a run are marked not applicable in the manifest rather than fabricated. restoration.json and the takeover entries of proxy-access.jsonl are reserved for phase 2 and marked not applicable in phase 1 runs.
manifest.json contains:
Write each artifact to a unique temporary file, flush, close, atomically rename, and hash the final bytes. Write the manifest last. A run whose evidence cannot be finalized is not successful.
The workflow remains:
proposed -> permissions-resolved -> approved -> running -> observed -> interpreted -> verified | contradicted | inconclusive
Driver Agent:
Agent interpretation cites the operation, resource, offset or protocol method, raw values, target timestamp, driver/proxy generation, and relevant source.
Use frozen dataclasses or equivalent validated models for:
Conversion among JSON, internal models, generated FIDL, and subprocess messages occurs only in explicit functions. Do not pass unvalidated dictionaries through the system.
The host consent file protects the workflow from accidental or unapproved agent behavior. It is not a target security boundary: a malicious host with engineering access may bypass it.
The target ceiling must remain safe enough for the intended engineering environment even if host approval is bypassed. The plan digest proves stable intent within the workflow; it is not bearer authorization.
Permission files:
Test:
Test:
Use fake and physical transports to test:
core (applicable once the serial transport lands);Test:
Managed-takeover state-transition tests are specified in phase 2.
Test:
Test:
The ffx subtool, target bindings, and transport integration belong in the Fuchsia tree. The Python package is built there, at tools/driver-lab, because it links fuchsia-controller and the generated bindings.
Driver Agent adapters, workflow approval objects, and repository-specific orchestration remain in the external Driver Agent repository.
Every run records exact revisions and dirty-state artifacts for both repositories. Directory layout is not part of the evidence identity.
ffx team roadmap);fuchsia.driver.development and produce the discovery gap matrix;ffx adapter.Exit: no architecture-critical dependency is assumed rather than demonstrated.
Exit: direct mode makes no claim to private resources or target audit.
Exit: an unapproved register read cannot reach the target.
Exit: the proxy activates, executes, and deactivates on a synthetic unclaimed node without manual target intervention.
Exit: the same overlapping Python operations work through direct and proxy backends without hiding semantic differences.
Exit: an agent can complete an approved read-only investigation and safely stop for missing authority.
The phase 1 host tooling is ready when:
core component dependency;Before implementation, resolve:
ffx-team-owned) The serial-capable target transport; phase 1 must not preclude it.fuchsia.driver.lab bindings behind the transport-neutral ProxyTransport seam.ExtendAllowlist session operation replaces V1's reopen-per-grant behavior.Takeover-related decisions (force-bind, node persistence, restoration policy, takeover-protocol packaging) live in the Phase 2 section of this document and in //src/devices/driver-lab/SPEC.md.
Changesets continue the global numbering:
[driver-lab] Core library extraction & pre-mapped MMIO adapter (in //src/devices/driver-lab/SPEC.md).[driver-lab] Embedded Rust driver library with ServiceFs integration (in //src/devices/driver-lab/SPEC.md).[driver-lab] Cooperative locking, quiesce hooks, and interrupt tap support (in //src/devices/driver-lab/SPEC.md).[driver-lab] Assembly gating, CML debug shard, and production-absence verification[driver-lab] Reference integration: synthetic platform driver in testing/[driver-lab] Host tooling discovery of embedded debug endpoints and in-situ session workflow[driver-lab] In-situ live-target conformance test suite[driver-lab] Expose FIDL and debug shard to Bazel (in //src/devices/driver-lab/SPEC.md).[driver-lab] Native Bazel build for driver_lab_rust (in //src/devices/driver-lab/SPEC.md).[driver-lab] Add C FFI staticlib shim (driver_lab_c) (in //src/devices/driver-lab/SPEC.md).[driver-lab] Add C/C++ wrapper and StateVmoBank (in //src/devices/driver-lab/SPEC.md).[driver-lab] Add C++ embedded server unit tests (in //src/devices/driver-lab/SPEC.md).Phase: 2 -- in-situ driver debugging workflow. Extends: the Phase 1 section of this document. The wire client, transport adapters, CLI parsing, and evidence models apply unchanged.
Companion specification: the proxy driver specification at //src/devices/driver-lab/SPEC.md.
Phase 2 enables host tooling (ffx driver-lab, the public Python API, and Driver Agent) to connect directly to active, bound drivers equipped with the embedded driver-lab library. This eliminates the need for managed takeover, avoids disturbing live hardware state, and provides real-time in-situ inspection of wedged or misbehaving devices.
| Stage | Phase 1 (Standalone Proxy) | Phase 2 (Embedded In-Situ Library) |
|---|---|---|
| Node State | Unclaimed dev-node (no driver bound) | Active driver bound and running |
| Target Moniker | lab_proxy.cm on synthetic or test node | Bound driver moniker (e.g. /bootstrap/boot-drivers:sample-driver) |
| Transport Connection | connect_device_proxy(proxy_moniker, ...) | connect_device_proxy(driver_moniker, ...) |
| Hardware State | Freshly initialized / unclaimed | Live in-flight state (preserves bugs) |
| Teardown / Restore | Stop lab_proxy; discard node | Close session; release mutation lease; driver resumes normal work |
In-situ debugging connects to the live driver component. It does not unbind the driver, does not drop power domains or clock votes, and does not reset hardware registers.
Scalar reads and snapshots default to non-destructive inspection. Known clear-on-read or FIFO data registers must be marked hard_denied in the target ceiling manifest unless explicitly authorized in an exact session allowlist.
Opening a mutating session acquires an exclusive mutation lease. If registered by the driver, the library invokes an optional driver-registered synchronous quiesce_hook(bool paused) callback to pause driver-side background polling loops while host mutations and sequences execute. If complex driver dispatchers require asynchronous coordination in the future, this interface may be extended to support an async stream/channel notification.
Host discovery via fuchsia.driver.development.Manager remains the discovery mechanism:
ffx driver-lab list --debug-capable: Queries active drivers on the target and filters for components exposing fuchsia.driver.lab.Service.ffx driver-lab describe --moniker <driver-moniker>: Fetches ProxyDescription directly from the live driver's embedded endpoint, inspecting its registered MMIO banks, protocol resources, and ceiling policy digest.fuchsia.driver.lab.Service is exposed.describe to fetch registered resources, logical sizes, and the target policy digest.attach and connect accept a driver component moniker directly without requiring an unclaimed node.ffx driver-lab inspect --moniker <id> as the primary entry point for live driver diagnostics.manifest.json.Changesets continue the global numbering:
[driver-lab] Software state banks, runtime knobs, and diagnostic triggers (in //src/devices/driver-lab/SPEC.md).[driver-lab] Host plan aliases and StateHandle API for state and knob experiments[driver-lab] Update driver-lab and autoda-fix skills for Mode A/B/C verificationPhase: 3 -- software state, runtime knobs, and adaptive verification workflow. Extends: Phases 1 and 2 of this document.
Companion specification: the proxy driver specification at //src/devices/driver-lab/SPEC.md.
Phase 3 enables host probe plans, the asynchronous Python API, and the autoda-fix skill to interact ergonomically with target-side StateBank resources (CS31) for single-build software state and concurrency experimentation, while selecting the lightest-weight verification mode for any driver bug.
StateHandle API (CS32)driver_lab/plans.py, api.py, cli.py)Probe plans support semantic operation kinds for StateBank resources both at top-level operations and inside sequence.items:
state_read32:offset.resource defaults to "state0" when omitted.AccessClass.READ_ONCE (or AccessClass.SEQUENCE inside a sequence) and executes via session.read32(resource_id, offset).state_poll32:(value & mask) == (expected & mask) or timeout (timeout_ns defaults to 100_000_000 when omitted).resource defaults to "state0" when omitted.AccessClass.POLL and executes via session.poll32(...).knob_write32:offset.resource defaults to "state0" when omitted; readback defaults to True.is_mutating_plan() == True), maps to AccessClass.WRITE (requiring one-time operator consent), and executes via session.write32(...).trigger_write32:offset.resource defaults to "state0" when omitted; accepts arg or value (defaulting to 1); readback defaults to False.is_mutating_plan() == True), maps to AccessClass.WRITE (requiring one-time operator consent), and executes via session.write32(...).Evidence bundles (operations.jsonl) preserve the exact semantic operation kind ("state_read32", "state_poll32", "knob_write32", "trigger_write32") for audit clarity.
StateHandle (driver_lab/session.py)HardwareSession.state(name: str = "state0") -> StateHandle wraps the named StateBank resource with a driver-shaped asynchronous API:
await state.read32(offset) -> intawait state.poll32(offset, expected=0, mask=0xFFFF_FFFF, interval_s=0.001, timeout_s=1.0) -> PollOutcomeawait state.write_knob(offset, value, ..., require_readback=True) -> WriteOutcomeawait state.trigger(offset, arg=1, ..., require_readback=False) -> WriteOutcomeawait state.snapshot32(offsets) -> list[int]autoda-fix, CS33)The autoda-fix skill (//src/devices/skills/autoda-fix) and companion driver-lab skill (//src/devices/skills/driver-lab) classify driver bugs during Phase 0 reconnaissance into one of three verification modes:
in-situ MMIO/IRQ):mmio_read32, mmio_write32, mmio_poll32, and wait_for_interrupt with hashed evidence bundles.in-situ StateBank):"state0" StateBank (define_state_slot, define_knob for fault and fix toggles, define_trigger) and deploys once (preferring ffx driver restart <driver_url> for reloadable non-bootfs drivers).knob_write32 with fix knob = 0, trigger_write32, state_read32) followed in the same boot by a Fix Proof Plan (knob_write32 with fix knob = 1, trigger_write32, state_read32 / state_poll32).driver-lab Required):fx test (unit/realm tests) or ffx log.driver-lab into Mode C bugs when a unit test or log assertion is faster and more direct.