Consume fpagent's fprint-device-status {available, device, captureAgentId}
in the /fingerprint wizard. Availability is tracked tri-state so an agent
that does not report it leaves capture working as before; the header tag
shows offline / no-scanner / ready, the capture panel warns, and capture is
disabled until a supported reader is attached.
Adds a vm-sandbox test for the wizard logic and the OpenSpec change
fpagent-device-availability, archived with the fingerprint-wizard spec.
3.3 KiB
Design
Context
See proposal.md — Why. fpagent pushes fprint-device-status
{available, device, captureAgentId} to each connection on connect and to all
connections when availability changes (2 s watcher; probes skipped while an
enrollment runs). available means "a supported reader is attached", not that a
capture will succeed. The wizard currently learns only agent-hello and the
enrollment events, and its header tag reflects only the socket connection.
Goals / Non-Goals
Goals: surface scanner availability in the wizard; stop a capture from starting when no reader is attached; keep working against agents that do not emit the event.
Non-Goals: changing the agent or the event contract; blocking capture when the agent is merely unreachable (pre-existing behavior); a proactive device picker or wiring guidance.
Decisions
-
The client is a pure listener. The agent already emits on connect and on change, so the wizard adds one handler and no polling or request event.
-
Availability is tri-state, not boolean.
agent.availablestartsnull(unknown) and becomestrue/falseon the first status. An agent that never emits the event leaves itnull, so captures are not blocked — the feature degrades to today's behavior instead of deadlocking older field laptops. -
The status tag has four labels. Offline (socket down), "Scanner not connected" (
available: false), "Agent ready" (available: true), and "Agent connected" (unknown). Thetitlecarries the version and the device name when known. -
Capture is gated at both layers. The buttons are disabled when
available === false, andcapture()returns early on the same condition so a race (availability changing between click and handler) cannot start an enrollment. The agent remains authoritative and still rejects a capture with no reader. -
The gating logic gets a small vm-sandbox test.
app.jsis a browser IIFE, sotest/fp-wizard.test.jsloads it in anode:vmsandbox with stubbedVue/io/documentand exercises the availability state, the tag labels, and the capture refusal directly. No new dependency and no browser runtime; the remaining end-to-end pass against a real agent is manual, since/fingerprintrequires a session.
Risks / Trade-offs
- [Old agent never emits the event] → availability stays
null, captures are allowed, behavior matches today. The status tag shows "Agent connected". - [Brief wrong tag between socket connect and first status] → the agent emits
the status in the same connection handler as
agent-hello, so the window is negligible; thenullstate shows "Agent connected" rather than a false "Scanner not connected". - [
availableread as "ready to capture"] → the agent's contract says it means "attached"; enrollment remains authoritative and a capture can still fail. - [Watcher is skipped during a capture] → availability is not updated mid-scan, which is correct: the reader is in use by that capture.
Migration Plan
Ship the wizard; it is backward compatible with agents that lack the event.
No agent change is required to deploy the wizard, and the full benefit appears
once the field laptops run the agent that emits fprint-device-status. Rollback
= revert the wizard.
Open Questions
None.