docs(wiki): USB printer cover-open field bug writeup; add art-docker-station lab box
Build desktop / desktop (push) Successful in 5m14s
Build & push images / images (push) Successful in 3m1s
CI / check (push) Successful in 43s

Printer investigation (park-buzi): cover-open on the USB thermal
printer wedges its status offline/faulty, surviving a full reboot,
recoverable only via `docker restart server`. Traced sendRawUsb/
probeUsb end-to-end — no persistent handle in the app layer, so the
leading theory is the container's /dev/usb directory bind-mount
retaining a stale view across the printer's physical re-enumeration.
Not yet confirmed on hardware; documented with repro/confirmation
commands and ranked candidate fixes.

Also registers a new lab bench box, "art-docker-station", as a Komodo
Stack (dev tier, same shape as park-lab, its own isolated secret refs).

Claude-Session: https://claude.ai/code/session_01FWncR69HgGPuei1dLrW3cU
This commit is contained in:
2026-08-30 18:11:34 +02:00
parent 2910672b5a
commit ea8fe22969
4 changed files with 138 additions and 3 deletions
+37
View File
@@ -2673,3 +2673,40 @@ the DS-2CD1047G3H-LIU units rather than carry an RTSP/ffmpeg workaround dependen
DS-2CD1043G2-LIU (no such bug, ISAPI main-stream snapshot works natively) is the reference model
going forward. RTSP main-stream capture remains documented as a proven, viable fallback if a G3H
camera is ever unavoidable, but is not being built. Full sweep table + reasoning on [[lpr-camera]].
## [2026-08-30] update | Booth USB printer cover-open bug: leading theory is a stale container bind-mount, not a stale app-layer handle
Live troubleshooting request (park-buzi): opening the printer's paper-roll cover reliably wedges its
status to offline/faulty, surviving a full appliance reboot; only `docker restart server` clears it.
Traced `sendRawUsb`/`probeUsb` end-to-end in `printer-escpos.ts` plus both poll loops
(`device-monitor.ts`, `printer-monitor.ts`): every print AND every poll does a fresh
open→write/probe→close with no persistent fd/socket/driver instance anywhere — ruling out a naive
"stale Node handle" explanation. Leading hypothesis instead: the cover-open microswitch cuts power
to the printer's USB interface board, causing a real bus re-enumeration; the container's directory
bind-mount of `/dev/usb` (chosen specifically to survive `lpN` renumbering) can retain a stale view
of the old device node until the container's mount namespace is recreated — which `docker restart`
does and a policy-driven reboot-time restart may not (boot-order race). Not yet confirmed on
hardware (host-vs-container `stat`/inode comparison at the next occurrence is the next step); lab
repro is blocked because the lab has a RONGTA, not the park-buzi unit's actual (still unidentified,
"Generic (unknown)") model. Full writeup, confirmation commands, and candidate fixes on
[[printer-usb-transport]].
## [2026-08-30] update | Backup status "Never" despite valid rotating backups — restart amnesia in BackupService, fixed
Admin noticed park-buzi's Backup screen showed "last successful backup: Never" despite 7 real,
correctly-rotating encrypted backup files on disk, plus a 2-day gap since the last file. Traced
both symptoms to the same cause: `BackupService` tracked last-success/last-error as PLAIN
IN-PROCESS FIELDS (never written to the DB), and the daily schedule was a `setInterval(...,24h)`
measured from PROCESS START, not wall-clock time since the last real backup — so any server
restart (routine under `restart: always`: deploy/crash/OOM/host reboot) simultaneously wiped the
visible status back to "Never" and reset the 24h countdown, independent of the actual
file-writing/retention engine (`backup.ts`), which was working correctly the whole time and
explains why files existed on disk despite the UI's contradictory-seeming status. Fix: four new
nullable `site_config` columns (migration `0025_backup_last_status.sql`) persist last-success/
error there instead of in memory; `BackupService.status()` reads them fresh each call so a new
instance (= a restart) sees the prior instance's outcome; a new `isDue()` method computes
schedule-due-ness from the persisted last-success timestamp; `server.ts`'s scheduler is now a
15-minute poll gated by `isDue()` instead of a 24h `setInterval`, making the real cadence immune
to restart timing. New test file `backup-service.test.ts` (6 tests) covers restart-durability and
`isDue()` directly; full existing suite (319 tests) still green. No API/UI contract change. Not
yet committed (holding per instruction). Full writeup on [[backup-recovery]].