Files
tools-legacy/openspec/changes/archive/2026-10-02-fpagent-device-availability/design.md
T

73 lines
3.3 KiB
Markdown
Raw Normal View History

# 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
1. **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.
2. **Availability is tri-state, not boolean.** `agent.available` starts `null`
(unknown) and becomes `true`/`false` on the first status. An agent that never
emits the event leaves it `null`, so captures are not blocked — the feature
degrades to today's behavior instead of deadlocking older field laptops.
3. **The status tag has four labels.** Offline (socket down), "Scanner not
connected" (`available: false`), "Agent ready" (`available: true`), and
"Agent connected" (unknown). The `title` carries the version and the device
name when known.
4. **Capture is gated at both layers.** The buttons are disabled when
`available === false`, and `capture()` 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.
5. **The gating logic gets a small vm-sandbox test.** `app.js` is a browser
IIFE, so `test/fp-wizard.test.js` loads it in a `node:vm` sandbox with
stubbed `Vue`/`io`/`document` and 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
`/fingerprint` requires 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; the `null` state shows "Agent connected" rather than a false
"Scanner not connected".
- [`available` read 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.