v0.1.0b2 — beta 2: the reliability + evidence-harness release
Pre-releaseBeta 2 — the reliability + evidence-harness release. Two hardening
batches in one: the transports can no longer double-fire a destructive action,
MCP approvals became signed single-use receipts with an audit trail, GLKVM
snapshots work headless at native resolution, evidence learned to carry the
conditions it was observed under — and kvm-pilot test-report turns the whole
per-device intake into one command. Everything in this release is
unit/emulator-verified; the first live fleet run of the new paths is the next
milestone (the harness exists precisely to record it honestly). 18 issues
closed.
Added
-
kvm-pilot test-report(#99) — the live-test harness: probes the
device's capabilities and appends one evidence row to the run ledger
(--ledger>$KVM_PILOT_TEST_LEDGER>~/.config/kvm-pilot/test_runs.jsonl;
never the installed package data). Read-only probes (info, snapshot —
stamped with the #156 conditions on pass AND fail — healthcheck, logs,
power_state) run every invocation; destructive probes run only via
--include virtual_media,power,firmware_updatewith a recorded--attest
operator statement, still routed through the normal safety gates. Pass =
assertion + observed effect (power must flip the read-back and restore;
media must report online and the eject must land; a flash passes only via
the driver's #94 verified-state contract) — an unobserved effect records an
honest FAIL. Fake-driver runs (and--synthetic) record
source="synthetic", which never promotes maturity; there is no flag to
forcereal. -
The generated wiki Hardware-Compatibility page now carries a Maturity
column — the #98-derived level per (device, firmware) read from the shipped
registry (—when no live-derived rating exists), completing #103. -
New GLKVM quirk
firmware-flash-webui-only(#177): the GL web console is
the only known-good RM1PE upgrade path (V1.5.1→V1.9.1 live-verified); the
/api/upgrade/*flash was observed to no-op (#94/#95) and no API-driven
flash has ever been verified on any release.firmware-updateoutput now
steers to the web console on affected devices (it never blocks — the driver
already reports a no-op flash honestly), and
firmware-update/driver-features/firmware-registry docs carry the guidance. -
GLKVM headless JPEG snapshot at native resolution (#187): when the
snapshot bytes fail the JPEG guard (H.264 at native/high res, #107) and the
firmware exposesparams.video_format(V1.9.1+), the driver flips the
encoder to MJPEG (POST /api/streamer/set_params?video_format=1), retries,
and restores the prior format — no EDID change, no H.264 decoder, no
browser. Insidestreamer_warm()the flip is held for the whole block and
restored once at exit. Composes with the #142 offline-streamer recovery
(offline → warm → H.264 → flip). V1.5.1 doesn't expose the switch and keeps
the honestSnapshotFormatError(upgrade via the web UI, #177). Mechanism
live-proven on V1.9.1; the auto-path is emulator-verified and awaits its
first live ledger row. New quirksnapshot-h264-at-native-res. -
A firmware change now invalidates the device assessment:
preflight
remembers the firmware each device was last assessed at, forces the stable
checks to re-run live when it differs (out-of-band web-UI flashes included),
and emits afirmware-deltafinding diffing what cleared / regressed /
stayed open (#180). -
Run-ledger capability rows can now record the conditions a result was
observed under (conditions: {resolution, encoder_format, snapshot_cached, jpeg_sink_clients}) — the axes that actually decide a GL snapshot outcome —
and the support-matrix rollup surfaces them aspass_conditions/
fail_conditions, so field reports at different operating points reconcile
from data instead of reading as contradictions. Optional; pre-existing rows
and derived maturity are unaffected (#156). -
CI docs-parity guard:
build_wiki.py --checkfails when adocs/*.mdpage is
not registered in the wikiPAGESallowlist, so a new page can no longer
silently go unpublished; the MCP README tool table gained the missing
access_pathsrow (#175).
Added (MCP)
-
Signed, expiring, single-use approval receipts + audit trail for the
destructive act tools (#72): every approval mints an HMAC-signed receipt
bound to the exact invocation (host, tool, effect, args-hash, dry-run,
approver), re-verified immediately before dispatch and consumed on use —
a bound field changing after approval, an expired receipt
(KVM_PILOT_MCP_RECEIPT_TTL, default 60 s), or a replay fails closed as a
denial-shaped result. Approved results carryreceipt: {id, state}and a
realapproval.expires. Every destructive-invocation terminal (approved,
denied, consumed, expired, mismatched, replayed, dispatch-exception) emits
one JSON audit record on thekvm_pilot.mcp.auditlogger. -
New
file_firmware_reporttool — the MCP twin of CLIfirmware-check's
auto-filing (#189), completing the registry telemetry loop for agent
sessions. Filing a GitHub issue is a new external-write effect class
with its own operator gate (KVM_PILOT_MCP_ALLOW_EXTERNAL_WRITE, off by
default) plus the usual per-invocation approval; the shared helper moved to
firmware_registry.file_firmware_report(#190).
Changed
- The healthcheck's support-evidence finding now labels ledger history as
recorded (vs this run's own tested-now probes), renders the #156
conditions each result was observed under, and flags condition-blind
snapshot passes ("verified only under unknown resolution/encoder") instead
of overclaiming a bareverified (n=N)— the honesty regression that gave
#180 its false confidence (#180).
Changed (MCP)
- Act results now carry a typed
outcomefield (approved/denied/
cancelled/not_confirmed/gate_closed/invalidated) so agents
branch on data instead of matching the human-facingdenied_reasonstrings —
a cancelled elicitation (benign, retryable) is now typed apart from an
explicit denial. TheKVM_PILOT_MCP_ELICIT=offescape-hatch hint moved from
every client-side denial to a one-time hint after ≥2 consecutive
client-side approval kills on the same host — a security trade-off shouldn't
be advertised on a one-off mis-click (#149). - The
powertool now returns a structured result instead of a sentence: it
bumps the frame generation (a stale mouse click can no longer anchor to a
pre-reboot snapshot within the reboot window) and verifies the effect via
the driver's trustworthy signal — Redfish PowerState or a wired ATX LED —
reportingverified: true/falsewith the observed state, or an honest
verified: null+ the reason and remedy when no trustworthy signal exists
(GL units: ATX sensing lies; verify visually) (#168).
Fixed
-
The transports no longer auto-retry a 409/503 that answers a state-changing
request — re-firing a POST whose 409/503 arrived after the device began
acting (e.g. a BMC perturbed by theComputerSystem.Resetit just accepted)
could double-fire a destructive action. Reads (GET/HEAD) keep the bounded
retry; the #164 breaker semantics are unchanged; the Redfish driver's own
at-target reconciliation now sees the surfaced error instead of being
pre-empted by a transport re-POST (#167). -
A JSON endpoint answering with a non-JSON body (e.g. truncated at the
content boundary) now raises a typedProtocolErrorwith a redacted preview
instead of leaking raw bytes into dict-expecting callers as an opaque
AttributeError; an empty 2xx body returnsNone(#170). -
Redfish
mount_isonow verifies the medium actually landed (polls the
VirtualMedia slot forInserted=true, raisingMediaOfflineErroron a
silent no-op — the #78 trap, Redfish edition;verify=Falseopts out), and
a mid-flight 401 re-auth DELETEs the old session before re-login so a
spurious 401 can't strand a live session slot on session-capped BMCs; a
login that yields no session URI now logs the future leak (#169). -
The MCP effect gate now fails closed for an effect class with no
registered enable-flag instead of silently borrowing the CONFIG gate;
APPLIANCE_RESETis now explicitly mapped to
KVM_PILOT_MCP_ALLOW_APPLIANCE(#190).
Install: pip install --pre kvm-pilot
Full diff: v0.1.0b1...v0.1.0b2