Product card shell: injected board interaction acceptance
Completed 2026-09-23 at 16:07:09 UTC using tools 11ffecb and the existing
product package /nix/store/ii5g7635wldnk7jsrfp18ism634w5y70-k230-card-shell,
source 935e02155cac7374de10b5db6e642d9479c500b3 (landed as f514478).
The deliberately unchanged package isolates the acceptance-tool correction
from the separate compositor telemetry fix. Task 5.2 interaction evidence
passes; no cost, default-image or real-finger acceptance is claimed.
Method and provenance
Follow docs/research/card-shell-board-tools.md: a coordinator-reserved,
temporary non-root service includes the actual normal configuration, with
DSI-1 at 568x1232, scale one, Pixman RGB565. Native input-event packets go only
to the distinctly named, sysfs-verified virtual touchscreen. The compositor’s
headless test-input hook is not used. Root recovery is armed before stopping
the normal shell, and all trial processes belong to bounded service cgroups.
Actual invocation after the owner declared the reservation active:
python3 /run/card-tools/card-shell-acceptance.py --execute \
--provenance injected-touch --output /run/card-evidence/acceptance
The runner exits successfully with all thirteen checks OBSERVED. The raw
report deliberately remains CAPTURED_REQUIRES_REVIEW; this document records
the subsequent coordinator visual review rather than rewriting raw results.
The transfer was verified against the board archive’s SHA256 over Wi-Fi after
a serial download failed gzip verification. No failed download was published.
The offline collector verified each retained screenshot against the report’s
hash and exported only allowlisted telemetry and client records.
Reviewed observations
| Interaction | Evidence and reviewed outcome |
|---|---|
| Native card entry | one-live.png shows the blue parent and orange child shrunken inside the selected card. Native renderer/format metadata matches the panel. |
| Two live apps and horizontal movement | two-live.png was captured while holding the injected drag halfway between cards. Both parents and children are visible; both clients’ counters advance. The selected card moved from its centered position to the left, exposing its neighbor. |
| Tap expansion and Cards Back | expanded.png shows the purple app at normal size; cards-back.png shows the blue app restored after close handling. |
| Privacy and unavailable state | private.png and unavailable.png replace the selected app’s pixels and title with the corresponding non-live placeholder. |
| Upward close and refusal | The native throw generates close_requested followed by the client’s explicit close_refused. The client remains alive. |
| Timeout and accepted close | Fixed telemetry independently records the compositor timeout; close-timeout.png retains the app and explains recovery. The second client accepts Close and exits; close-exit.png shows the remaining card and source-gone feedback. |
| Apps and Help/Back | apps.png, help.png and help-back.png show the expected installed-app and help surfaces and return path. |
| Terminal and Monitor | terminal.png shows the usable terminal; monitor.png shows htop. IPC verifies the corresponding app focus. |
| Windows/Home | windows.png shows the Windows controls; home.png returns to Terminal with the normal bar. |
| Keyboard | keyboard.png visibly shows the keyboard, while workspace geometry confirms reserved content space. |
| System/Back | system.png shows System controls; system-back.png restores the main bar and Terminal. No reboot or power-off was invoked. |
The refusing synthetic floating client is terminated only after its protocol test, with executable and owner verified, so it cannot obscure normal tiled apps. All user interaction routes use native injected touches; marks used to exercise privacy and fixture lifecycle setup use ordinary compositor IPC. Synthetic titles label refusal behavior and do not correspond to app-ID order; client identity in the logs is authoritative.
Limits retained
during-drag.png caught a settled selected card, so its filename alone is not
movement evidence; the deliberately held two-live.png supplies that evidence.
closing.png was captured after the 1.5-second refusal timeout, not during the
short pending-close state. The client protocol log and separate timeout state
provide the refusal/timeout distinction. Neither capture establishes animation
smoothness, gesture latency, physical-glass behavior or optical presentation.
The old compositor’s session-start timestamp still follows the first input.
The unchanged cost parser rejects this run with exit 2 (benchmark-rejection.txt).
The telemetry is retained unmodified; no passing cost report is fabricated.
Compositor-observed motion gaps have medians of 2.037 ms (one client) and
2.271 ms (two), but maxima of 67.489 ms and 62.903 ms. These bursty callbacks
are not proof of uniform 10 ms device delivery or a frame-budget pass. A repeat
with the corrected producer must evaluate all declared budgets independently.
The original failed run remains in ../board-first-trial/; successful testing
with corrected instrumentation does not erase it. Product task 5.1 still needs
system-image integration and its QEMU proof. Task 5.3 still needs a focused
real-finger trial. The current card appearance is functional and remains part
of the planned UX refinement, not a claim of finished webOS-like polish.
Normal-shell recovery
restoration.json records the independent 16:07:47 UTC injected regression:
normal shell/seat/network services active, Apps Next/Previous, keyboard toggle
retaining Apps, Terminal, and Apps Back all passed. The product service was
inactive. The installed image stayed
/nix/store/nz82q373yj1hp2k4qm85xa3c328jli8j-nixos-system-nixos-26.11.20260919.20b1ddd,
with unchanged boot ID 9407bae0-fe1a-4a05-8062-edbe8a110859. No flash or reboot
was required, and no Wi-Fi credentials or private addresses are included.