Internal reference for client and firmware maintainers. It records transport protocols, physical-device test procedures, benchmarks and dated verification results for the plugin media, HID and rendering paths. Dated results describe the recorded milestone only. Plugin authors should use the plugin contract and the plugin media guide; nothing here adds SDK, CLI or MCP capabilities.
Compare the same package and state across the browser preview, Host SDL and the physical Dock. An installed plugin and an uninstalled Vibe draft can have different layouts; use the installed package directory to reproduce a device discrepancy.
From apps/desktop, with the desktop client closed to release its CDC port:
apps/desktop
BOLT_HOST_BINARY=/absolute/path/to/firmware/build/host/bolt \ BOLT_DEVICE_SERIAL=101D10124011100013 \ node scripts/test-preview-rendering.mjs /absolute/path/to/plugin
Omit the package argument to run the synthetic regression cases: empty bindings, literal colors, missing/null/object text, image variants, inline artwork and clearing artwork in an existing physical view. Omit BOLT_DEVICE_SERIAL for a preview/SDL-only run. The device serial must identify exactly one USB CDC port.
BOLT_DEVICE_SERIAL
Captures are written under .cache/plugin-rendering/<plugin-id>/. current.png is the physical display, simulator.png is the native content area, and preview.png is the browser preview. Comparison excludes the system header, whose clock is device-owned. The physical/SDL check rejects more than 1% of content pixels differing by over 32/255; synthetic image and rectangle pixels are also checked individually with a tolerance of 2/255. Browser text antialiasing can differ from FreeType; text geometry is checked separately.
.cache/plugin-rendering/<plugin-id>/
current.png
simulator.png
preview.png
The physical adapter must preserve empty bindings as literals and use the same text fallback as the browser/SDL adapter. Images contain and center within their declared viewport. Inline PNG artwork takes precedence over the selected asset variant, then the default asset. Clearing artwork or receiving an unknown variant must restore that fallback. Image stream pixels take precedence while present.
These physical fixes live in the sibling pexarbolt-firmware-launcher repository and require deploying its rebuilt V821 binary as well as rebuilding Host SDL.
pexarbolt-firmware-launcher
Firmware owns media fullscreen; plugins continue normal state publication. Test continuous playback and a paused frame while repeatedly changing surrounding control text/options/disabled state. Fullscreen must avoid black regions/flicker and restore normal controls on exit. Hardware-only failures suggest a device renderer drawing-context issue; do not mask it by suppressing state or raising fps. This rendering fix changes no SDK/CLI/MCP methods, permissions or capabilities.
The existing USB JSON framing is extended without enlarging console.data. Image requests carry pluginId/instanceId/epoch/viewId/sequence. Sequence increases monotonically within the sender; old views/sequences are rejected.
Transport itself requires neither images.read nor video.read. Firmware checks active view, lease and declared stream dimensions; the host Broker enforces source-file grants. Canvas games may use the same channel without media-read permission.
Required capabilities:
{"dynamicImages":true,"imageStream":{"version":1,"format":"bgra8888","maxBytes":1393728,"maxChunk":16384,"volatile":true}}
BGRA8888 uses viewport dimensions; Desktop handles contain/cover and Dock never decodes the original photo. Maximum frame size is 1,393,728 bytes. Base64 adds overhead; CDC throughput is not a frame-rate guarantee.
Only one chunk acknowledgment is outstanding. Keep the old frame until full validation. A newer begin cancels incomplete uploads; never replay old-connection data. The receiver holds at most three frames for fading-out/current/incoming images; further allocation returns overloaded. Ten seconds of chunk inactivity or 30 seconds total releases incomplete frames. Image activity cannot extend the independently renewed five-second view lease.
Shared EphemeralImageReceiver owns validation and frames. USB routes through PluginDeviceService and UI snapshots. SDL's local adapter supplies descriptors in /api/state and chunks at /api/image?sequence=...&offset=..., using the same receiver and LVGL RAM image interface. HTTP caching is disabled and no photo files are created. Packaged fallbacks and legacy small Music artwork are separate compatibility paths.
pnpm --filter @pexarbolt/plugin-studio test:plugin-images exercises the real sandbox, picker grants, PNG/JPEG, contain/cover, preview pixels, wide/narrow SDL screenshots, clear-on-stop, USB PTY display and absence of saved device photos.
pnpm --filter @pexarbolt/plugin-studio test:plugin-images
Folder tests cover real-sandbox pagination and slideshows. pnpm --filter @pexarbolt/plugin-studio test:plugin-folders:ui requires Vite on 5173 and checks selection/cancellation, settings save, reload, revocation and responsive layouts. Unit tests cover restart, plugin isolation, replacement, symlinks, pagination refresh, cancellation and stale IDs with temporary files. Mac App Store bookmark restoration has not been verified in a MAS build.
pnpm --filter @pexarbolt/plugin-studio test:plugin-folders:ui
Firmware tests ephemeral_image_test, plugin_device_service_test and usb_plugin_route_test cover offsets, integrity, expiry, sequence, cross-session three-frame bounds, grants, stale views, leases, catalog and USB routing. make -C tests plugin-media-ui checks real LVGL normal/fullscreen transitions and release. Client node scripts/test-plugin-photo-transitions.mjs checks delayed loads, dual-image pixels, animation cleanup and responsive screenshots. Host binary defaults to the adjacent firmware build/host/bolt; override with BOLT_HOST_BINARY.
make -C tests plugin-media-ui
node scripts/test-plugin-photo-transitions.mjs
At the recorded milestone, related SDK/protocol/client unit checks, type checks, Host SDL build, native tests and image checks passed. A local USB PTY transfer of 854×408 BGRA took about 2.2 seconds; this is not hardware throughput. Two baseline checks remained blocked: usb_cdc_host_client_test's historical bucket-count assertion, reproduced on unchanged firmware, and validate-host-ui.sh's existing AI Status animation expiry check. The new channel's standalone screenshots/pixel checks passed.
Compatible firmware is required. This channel alone does not provide a hostless album, arbitrary network image download, video decoding or device photo storage.
Firmware with bulkTransport negotiates a separate data interface for frames, image chunks, assets, catalog icons and native projections. Control, heartbeat and replies remain CDC. Compatible JSON/Base64 and integrity paths remain; plugins need no USB code.
The host probes with the current session token to avoid a different Dock. Old/unavailable interfaces use CDC. A failed Bulk write rejects the request without replaying potentially partial data; subsequent requests may fall back. Cancellation drops unsubmitted work; submitted frames drain before the next frame to preserve byte-stream boundaries.
The main-process message USB bulk OUT enabled; acknowledgements use CDC confirms enablement; device /tmp/bolt-bulk.log records received bytes. Physical verification used macOS + V821; Windows needs a WinUSB driver for the interface. See the adjacent firmware docs/protocols/plugin_usb_bulk.md.
USB bulk OUT enabled; acknowledgements use CDC
The 2026-09-19 fix breaks a native USB callback/Transfer reference cycle on completion, error and submit failure, releasing old frames, cancellation signals and error stacks. Submitted frames still drain; closing cancels active transfers and waits for callbacks. Unknown results are never replayed. Updating Desktop is sufficient; SDK calls, grants and protocol stay unchanged. Check long-running and post-stop memory, not just short previews.
The symbolicated crash was main-process V8 OOM. Before the fix, JS heap grew from 57.09 to 127.80 MB in 120 seconds. Afterward, a 300-second observation ranged 35.11–44.38 MB and ended at 39.29 MB. Post-GC snapshots reduced Transfer objects from 2,138 to one and JPEG strings from 2,087 to 11, including current frames and other enabled plugins. Bulk playback continued. Ten lifecycle/cancellation/drain/router regressions, Desktop type checks and Desktop/CLI/MCP builds passed. This validates the identified leak, not multi-hour endurance.
The V821 hardware decoder supports JPEG/MJPEG. H.264 encoding support does not imply H.264 decoding support. Official references:
Firmware advertises videoStream version 1, format: mjpeg, maxBytes: 32768, volatile: true, singleFrame: true. New clients request videoMaxFps: 55 in plugin.capabilities and receive maxFps: 55; clients without that opt-in receive 30 to remain compatible with their existing capability validators. Firmware without video capabilities remains compatible with photo plugins but is incompatible with plugins requesting video.read.
videoStream
format: mjpeg
maxBytes: 32768
volatile: true
singleFrame: true
videoMaxFps: 55
plugin.capabilities
maxFps: 55
video.read
Negotiated plugin.image.frame transfers a complete MJPEG frame in one acknowledged request when the encoded message fits within 16 KiB. Its dimensions, format, length and SHA-256 have the same validation as plugin.image.begin/chunk/commit, which remains the fallback for larger frames, older firmware or smaller wire limits. Video fallback chunks contain at most 8 KiB of JPEG data. There is one acknowledged transfer in flight and at most one replacement frame; slow devices receive the newest available frame without building a latency queue. Firmware validates view identity, lease, permission, exact viewport dimensions, bounded baseline JPEG headers, frame sequence and hash before decode. Video allows at most three received frames across decoder, snapshot and renderer ownership. Photos use the same bound to accommodate a fading previous image alongside the current image and the next transfer. Frames are cleansed on release.
plugin.image.frame
plugin.image.begin/chunk/commit
If a JPEG commit is explicitly rejected with invalid_image, the client sends its already decoded BGRA pixels using a new frame sequence. It retries JPEG on the next frame after a five-second cooldown, so temporary DMA memory pressure does not permanently slow the view. BGRA is capped at 10 fps and requires substantially more USB bandwidth, so actual playback may be slower. Timeouts and unrelated protocol errors do not trigger this fallback. Firmware startup also removes a stale framebuffer mirror left by an interrupted video process, preserving the current UI page and unrelated display layers.
invalid_image
The client continuously follows Chromium's decoded-frame callbacks and paces by media timestamps rather than window refresh timestamps. Only the newest encoded frame is retained between Electron reads. Adaptive JPEG quality targets 8 KiB above 30 requested fps, 10 KiB above 15 fps, and otherwise 32 KiB, retaining the 32 KiB hard limit for difficult content. Resolution is unchanged; detailed or noisy sources can lose compression quality or frame rate. Every firmware's advertised frame-rate limit is respected.
V821 G2D supports rotation only, not RGB/YUV conversion (https://docs.aw-ol.com/docs/soc/v821/disp/g2d/). Even-sized NV21 frames use G2D rotation and the DE video channel when no later primitive, hit target or system overlay obscures the stream. The live UI stays on the UI channel. One YUV staging source and two scanout buffers share the inactive framebuffer page; UI updates use the remaining page while video is active. Stop and navigation restore ordinary double buffering. Unsupported layouts and formats retain full-range BT.601 CPU conversion and LVGL composition. Unchanged plugin state is not rebuilt per frame, UI snapshot reads skip a busy decoder, and V821 hardware telemetry sampling runs on a separate worker to keep I2C waits off the USB loop.
scripts/prepare-video-decoder.sh in the firmware repo builds only CedarC's MJPEG decoder libraries from the existing V821 SDK, including a patch that permits a 64 KiB compressed-input buffer for the protocol's maximum 32 KiB JPEG frames. The vendor's original 1 MiB minimum can fail under fragmented DMA memory and force very slow BGRA transfers. Deploy the rebuilt decoder libraries together with the firmware. Dependency collection includes six shared libraries (about 300 KB before deployment stripping). The buffer and one reusable YUV frame are allocated lazily and released after 500 ms without a completed frame. Paused pixels remain in RAM and on the video layer.
scripts/prepare-video-decoder.sh
Canvas plugins use the same MJPEG device path. ctx.frames.present retains the previous frame's working JPEG quality and adapts when scene detail changes, avoiding repeated full quality searches on every game frame. The requested FPS remains a ceiling; game-loop timing and device throughput determine the displayed rate.
ctx.frames.present
Generate an actual moving MP4 fixture and run from apps/desktop:
ffmpeg -f lavfi -i testsrc2=s=854x408:r=30 -t 6 -c:v libx264 -pix_fmt yuv420p -movflags +faststart /tmp/bolt-video.mp4 node scripts/test-plugin-video.mjs /tmp/bolt-video.mp4 BOLT_DEVICE_SERIAL=101D10124011100013 node scripts/test-plugin-video.mjs /tmp/bolt-video.mp4
The physical check requires the desktop client to release that device's CDC port. It verifies moving frames, bounded JPEG output, pause, seek, resume, stop, capability negotiation and acknowledged frame commits, and saves a matching paused client frame and physical screenshot under .cache/plugin-video.
.cache/plugin-video
Set BOLT_HOST_BINARY to also verify the native simulator's moving-frame transfer and paused pixels. Use a 55 fps fixture and BOLT_VIDEO_FPS=55 BOLT_VIDEO_SECONDS=60 for the high-rate loop, BOLT_VIDEO_HEIGHT=320 for a smaller viewport, or BOLT_VIDEO_OVERLAY=1 to verify software composition with an overlapping rectangle. Run node scripts/test-plugin-video-ui.mjs with the client dev server available to check the bundled plugin, permission controls, settings and desktop/narrow layouts in an isolated profile.
BOLT_HOST_BINARY
BOLT_VIDEO_FPS=55 BOLT_VIDEO_SECONDS=60
BOLT_VIDEO_HEIGHT=320
BOLT_VIDEO_OVERLAY=1
node scripts/test-plugin-video-ui.mjs
For enlarged fullscreen video, run the physical check with BOLT_VIDEO_WIDTH=640 BOLT_VIDEO_HEIGHT=360 BOLT_VIDEO_FULLSCREEN=1. It captures moving frames, then pauses and updates text behind the fullscreen image on its right and bottom edges. The resulting pixels must still match the paused frame, including those two regions. This catches partial redraws that clear the background but omit the enlarged image outside its original bounds. V821 routes transformed images directly through LVGL's software drawing callback because the vendor G2D adapter clips against untransformed bounds before reaching its fallback. Untransformed images and the direct video layer retain their existing paths.
BOLT_VIDEO_WIDTH=640 BOLT_VIDEO_HEIGHT=360 BOLT_VIDEO_FULLSCREEN=1
For device presentation measurements, restart the development firmware with BOLT_VIDEO_PROFILE=1 /etc/init.d/S90bolt restart through the explicitly selected ADB device. The monitor counts changed video sequences after LVGL display flush or direct layer submission plus vsync wait, separately from USB acknowledgements. Direct video screenshots use the DE capture interface because framebuffer readback excludes that layer. The smoke test saves the new measurements to .cache/plugin-video/device-performance.log; BOLT_VIDEO_MIN_FPS optionally enforces average delivered and displayed rates. These counters verify software presentation, not optical panel scanout. The normal restart clears the profiling environment.
BOLT_VIDEO_PROFILE=1 /etc/init.d/S90bolt restart
.cache/plugin-video/device-performance.log
BOLT_VIDEO_MIN_FPS
With a requested ceiling of 50 and an 854x320 viewport, two 15-second physical-device checks passed source-rate limits, colour/geometry and pause/seek/resume/stop:
Neither run reported device transfer/decode errors. The slower source was not upsampled; the faster source remained below the 50 fps ceiling. The 30 fps run had a maximum presentation gap of 287.25 ms, so these averages do not imply uniform frame delivery. Reproduce with BOLT_VIDEO_FPS=50 BOLT_VIDEO_HEIGHT=320 BOLT_VIDEO_SECONDS=15 and BOLT_VIDEO_MAX_FPS=50 or 30 matching the fixture's expected output ceiling. Logs are saved under .cache/plugin-video/benchmark-adaptive50-*.
BOLT_VIDEO_FPS=50 BOLT_VIDEO_HEIGHT=320 BOLT_VIDEO_SECONDS=15
BOLT_VIDEO_MAX_FPS=50
30
.cache/plugin-video/benchmark-adaptive50-*
Dock 101D10124011100013 was tested with a moving 55 fps local MP4, the full plugin protocol, live UI and asynchronous hardware telemetry. These measurements use the direct video layer and have no overlapping controls:
101D10124011100013
Both runs reported zero device transfer/decode errors and passed pause, seek, resume, stop, physical colour and geometry checks. The full-height run also passed native SDL pixel comparison. Full-height display windows ranged from 52.32 to 54.11 fps; JPEG frames averaged 6,499 bytes and acknowledged transfers averaged 11.54 ms (p95 17.59 ms). Display rates cover complete five-second measurement windows, independently of the receive-rate interval. Logs are saved locally under .cache/plugin-video/benchmark-55fps-*.
.cache/plugin-video/benchmark-55fps-*
The panel reports approximately 55-56 Hz. The production path approaches that limit but does not sustain exactly 55 distinct frames every second: some received frames are replaced before presentation, and occasional refresh deadlines are missed. Zero protocol errors does not mean zero skipped frames. Overlapping content uses the slower software composition path. A separate five-second overlap regression verified the overlay remained visible, the hardware video layer was disabled, playback controls worked and stop restored normal display; mean paused RGB error was 1.36/255. That short run did not yield a complete display-rate window. These results do not establish network-camera latency or performance for every source video.
Before direct-layer integration, the same Dock delivered 1,730 acknowledged frames in a 60-second 854x408 test with a 30 fps MP4 source (28.83 fps), with zero transfer errors. LVGL displayed 24.59 fps across eleven five-second windows. JPEG frames averaged 8,880 bytes; matching paused frames had mean RGB error 1.34/255. This is the software-path baseline, not the current direct-layer result.
A previous 15-second 854x320 software-path comparison delivered 443 frames (29.53 fps), with display windows of 29.44 and 29.24 fps and mean RGB error 1.66/255. The bundled player uses a 320-pixel video viewport with controls below it; the full-height benchmark uses 408 pixels.
The firmware repository's tools/video-overlay-probe verifies a separate path: dedicated USB reader, CedarC NV21 decode, G2D YUV rotation and a DE video layer. With Bolt stopped, a 60-second 854x408 USB test sent and acknowledged all 1800 frames at a 30 fps source rate, with zero drops or receive errors. Mean decode/rotation times were 1.024/0.715 ms; host send to layer submission plus vsync acknowledgement averaged 13.413 ms (p95 21.665 ms). DE composition readback matched the final JPEG with mean RGB error 1.644/255.
tools/video-overlay-probe
The probe uses a minimal binary transport and a stopped UI. Its 15-second 55 fps test subsequently presented all 825 frames, with zero drops or receive errors, mean acknowledgement latency 12.85 ms and maximum frame interval 35.92 ms. This establishes hardware feasibility only. Production measurements must use the full plugin protocol, live UI and telemetry; neither test measures optical scanout or camera-to-panel latency.
The firmware must accept frames for an active, dimension-matching stream: true viewport even when grants are empty or only contain store. Older firmware incorrectly required images.read or video.read for every frame, leaving Canvas games on their packaged fallback image despite a running host. Update that firmware; do not add unrelated media permissions to a drawing-only plugin. Hardware regression checks must include a plugin without media-read grants, since the full-permission showcase cannot detect this failure.
stream: true
store
images.read
Verified on 2026-09-14 with a drawing-only game (store grant) on Dock 101D10124011100013: the displayed MJPEG buffers changed while running, remained identical after pause completed, and changed again after resume. MJPEG can use a separate NV21 hardware display layer; /dev/fb0 screenshots of the UI page alone omit this layer. Inspect the active display layers when checking physical animation.
/dev/fb0
Validation commands (from apps/desktop):
node scripts/test-sdk-showcase.mjs node scripts/test-frame-native.mjs BOLT_DEVICE_SERIAL=101D10124011100013 node scripts/test-frame-native.mjs
Verified on 2026-09-12: real Chromium sandbox and browser preview, SDL renderer, and Dock 101D10124011100013. At 448 x 252, all Canvas/GIF/BGRA/JPEG modes rendered; SDL matched the host pixels exactly and the Dock mean RGB error stayed below 3/255. MJPEG completed about 40 transfers/sec and raw BGRA about 1 transfer/sec in the 2-second samples. These are completed transport frames, not measured panel refresh rates. The localhost HTTP snapshot path also passed on the Dock (9 transfers/sec with the showcase's 100 ms polling delay); it used generated moving JPEGs, not a connected security camera.
Live calls flow through the sandbox broker, the current PluginRouter view, USB CDC, and the firmware's existing consumer report queue. The Dock sends HID report ID 1 containing the consumer usage followed by a zero release report. This does not inject software media keys or use the player's volume API.
PluginRouter
The plugin must have an active physical view with an unexpired lease. Both the client and firmware check hid.consumer grants. Other plugin instances, disconnected devices and retired views cannot issue commands. The host SDL backend does not advertise physical HID. Vibe and fixture previews simulate the SDK result without calling the physical router.
hid.consumer
Firmware advertises this optional field in plugin.capabilities:
{"hidConsumer":{"version":1,"commands":["volumeUp","volumeDown","mute","playPause","nextTrack","previousTrack","stop"]}}
plugin.hid.consumer requests contain pluginId, instanceId, epoch, viewId, sequence and command. Sequence numbers increase within each view. The firmware caches 128 results, rejects retired sequences and mismatched duplicate commands, and returns cached results without replaying a key. Both ends limit commands to 20 per second. HID calls do not renew the view lease.
plugin.hid.consumer
pluginId
instanceId
epoch
viewId
sequence
command
{ok:true} confirms press and release reports were written. It does not confirm OS/player behavior or an absolute volume. Failures use offline, unsupported, unavailable, permission_denied, stale_event, invalid_event, or overloaded. Lost replies or partial writes return an unknown outcome and must never be retried automatically.
{ok:true}
offline
unsupported
unavailable
permission_denied
stale_event
invalid_event
overloaded
Run SDK/protocol tests, desktop broker/router/manager tests and pnpm --filter @pexarbolt/plugin-studio test:plugin-runtime for the real Electron sandbox bridge. Firmware tests cover the service authorization, deduplication, rate limits, lease expiry and HID press/release queue.
pnpm --filter @pexarbolt/plugin-studio test:plugin-runtime
For physical verification, build the desktop, quit its current instance to release the serial port, and run from apps/desktop:
node scripts/test-plugin-hid-device.mjs DOCK_USB_SERIAL
This explicitly selects one Dock by USB serial, uses a temporary plugin/profile, opens its view and sends one volume-down followed by one volume-up command through the real SDK sandbox. It can change the computer's volume. Use an HID report monitor to verify received reports, and record/restore the host volume when testing at a boundary or with a muted output. Restart the desktop after the test to restore its catalog and connection.
Desktop no longer starts a hidden sandbox for every enabled plugin at launch. Enabled plugins are catalogued as available; the host starts a runtime when the Dock opens one (plugin.open, a recovered pending selection, Open on device or the SDL simulator) and stops it after PLUGIN_IDLE_STOP_MS (60 seconds) hidden. Native-execution entries never start one. After activation, frame submissions from a hidden runtime wait until it is shown again. Unit tests use a fake transport and runtime; verify on a physical Dock with the user's normal App:
plugin.open
PLUGIN_IDLE_STOP_MS
capacity.running
bolt-plugin
device-data-card
Validated on 2026-09-12:
The repository-wide firmware scripts/validate-host-ui.sh remains blocked by pre-existing stale AI Status working animation source frames. Plugin-specific native and physical checks above passed independently.
scripts/validate-host-ui.sh
Desktop's CDC write watchdog remains 2,000 ms. It lets ready I/O callbacks run once before committing a timeout, so an overdue timer does not immediately discard a completed write. This does not extend the timeout for a genuinely stalled device or replay uncertain writes. Reset rejects new writes until reopening completes; callbacks from superseded opens cannot change the current connection and late-opened descriptors are closed.
The main process records [serial-link] transport error before sending connection status to windows. writeStall includes activeBytes, queue size, waitingFor, lastReceiveAgoMs, monotonic elapsedMs, and timerDelayMs (elapsed beyond the watchdog threshold, including the final I/O check). A large delay suggests host scheduling pressure; it is not proof of a USB or firmware fault. PexarLink.resetProtocol(error?) preserves the supplied transport error for pending requests; without a reason its existing Pexar link reset. message remains. Request rejection and reconnect semantics remain unchanged.
[serial-link] transport error
writeStall
activeBytes
waitingFor
lastReceiveAgoMs
elapsedMs
timerDelayMs
PexarLink.resetProtocol(error?)
Pexar link reset.
Concurrent full-device queries share one in-flight scan while retaining caller-specific port filters. Display polling, events and snapshot queries share in-flight collection; repeated events cannot spawn duplicate profiler commands while that collection is pending. Subsequent requests still perform a fresh scan.
Simultaneous ioreg sampling timeouts and CDC stalls warrant host/USB/device investigation even when the user is idle: background collectors remain active. Serialport's Unix reads/writes use asynchronous filesystem operations and can compete with other filesystem work. A storage timeout alone does not establish causation. Real-device endurance is required to determine whether the stall originates in the host driver, scheduling, or firmware.
ioreg
This is a Desktop transport repair. Plugin SDK permissions, capability declarations, CLI commands, standalone MCP and Studio MCP tool contracts are unaffected; no firmware capability or automatic mutation retry is added.