feat(carwash): entry-stream sampling for the review outbox; park-2 wired to the collector
Build & push images / images (push) Successful in 4m22s

The wash stream is small; the entry camera photographs every car in exactly the view the
classifier is trained on. The booth can now queue entry vehicle reads as pure training
material — crop + the camera's class, no order, no operator, no category.

- Core announces every vehicle read (deviceEvents.emitVehicleRead from snapshot.ts); the
  Car Wash module listens, samples entry reads in-process (sampleEntry: exactly one in N)
  and queues them (enqueueEntry). CARWASH_REVIEW_ENTRY_SAMPLE=N; 1 = every entry (storage
  and bandwidth are not the limit — user); 0/unset = off. Forwarded by compose.
- Packages carry kind: "wash" | "entry". Collector: kind column, entry meta validated
  without the operator fields, review screen shows an entry sample as such, export has a
  kind column, operator agreement computed from wash items only. Setup line shows
  "1 in N entries sampled"; status carries entrySample.
- komodo: park-2's four review lines enabled (collector URL by Netbird DNS name, booth-2,
  the shared per-booth secret, every entry sampled) — the collector is up on the overlay.
- Tests on both sides. Wiki: vision-review-outbox (entry stream + the internet-feed
  assessment), log.

Claude-Session: https://claude.ai/code/session_01FWncR69HgGPuei1dLrW3cU
This commit is contained in:
2026-09-07 09:38:55 +02:00
parent 3e57af5abc
commit dbbb051ebd
18 changed files with 242 additions and 64 deletions
+1
View File
@@ -143,6 +143,7 @@ export const en: Catalog = {
reviewTitle: "Remote review",
reviewOff: "off — no collector configured for this booth",
reviewCounts: "{{queued}} waiting · {{sent}} delivered · {{failed}} abandoned",
reviewEntrySample: "1 in {{n}} entries sampled",
reviewHint: "Each wash order sends the vehicle crop (plate blurred) and the chosen category to a trusted reviewer over the private network. One-way; nothing that names this site leaves.",
visionClassesHint: "The camera's fixed vocabulary (set in code, not here). Tick the classes this category covers.",
visionThreshold: "Camera confidence to flag a downgrade",
+1
View File
@@ -145,6 +145,7 @@ export const sq = {
reviewTitle: "Shqyrtim në distancë",
reviewOff: "joaktiv — asnjë mbledhës i konfiguruar për këtë kabinë",
reviewCounts: "{{queued}} në pritje · {{sent}} të dërguara · {{failed}} të braktisura",
reviewEntrySample: "1 në {{n}} hyrje merret mostër",
reviewHint: "Çdo porosi lavazhi dërgon prerjen e mjetit (targa e turbulluar) dhe kategorinë e zgjedhur te një shqyrtues i besuar përmes rrjetit privat. Njëkahësh; asgjë që emërton këtë vend nuk del.",
visionClassesHint: "Fjalori i fiksuar i kamerës (vendoset në kod, jo këtu). Shëno klasat që mbulon kjo kategori.",
visionThreshold: "Siguria e kamerës për të shënuar një ulje kategorie",
+2
View File
@@ -40,6 +40,8 @@ export interface CarwashReviewStatus {
failed: number;
lastSentAt: string | null;
lastError: string | null;
/** 0 = entry sampling off; N = one in N entry reads is queued as training material. */
entrySample: number;
}
export function fetchCarwashReviewStatus(): Promise<CarwashReviewStatus> {
return apiFetch("/api/carwash/review/status");