docs(wiki): activity-log explainability, dates/i18n, KP-300H barcode fix

Record this session's work across the affected pages + three log entries.

- ticket-encoding: id 13→11 digits (guess-resistance rationale, legacy-safe
  validation) + a barcode-geometry rule (symbol dots must fit the narrowest
  deployed printer's line — the KP-300H 72mm overflow).
- rongta-printer: KP-300H raster-garbage root cause (line overflow, not
  corruption), sendRaw graceful-close fix, Albanian human dates (formatStampSq).
- i18n: localized ledger reason codes, relative/human dates + the
  "browser ICU lacks Albanian" gotcha, toggle stale-router-context fix.
- shift: Albanian Z-report, shift-history UI + permission scoping.
- booth-console: explainable activity log (inline reasons/badges, event-detail
  modal with snapshots + audit disclosure, subscriber names, failed-snapshot
  tiles).
- index/log updated; all added wikilinks resolve.

Claude-Session: https://claude.ai/code/session_01Xcm6ikLgGoCxxHrxtjkk5V
This commit is contained in:
2026-06-19 11:41:27 +02:00
parent bbf61c48df
commit 0074e82a2a
7 changed files with 154 additions and 20 deletions
+28 -6
View File
@@ -2,7 +2,7 @@
type: concept
tags: [parking, domain, business, devices, entry-flow]
sources: []
updated: 2026-06-15
updated: 2026-06-19
status: open
---
@@ -23,11 +23,19 @@ must have:
reconciliation aid ([[reconciliation]] pre-numbered stock), not the scan key.
- **All-numeric** (as-built 2026-06-17) — so ANY legacy 1D barcode scanner reads it and an operator
can hand-key it. Random (not sequential), so "all-numeric" does not weaken the unguessable
property. Format: **13 digits = 12 cryptographically-random digits + 1 Luhn check digit**
(10^12 space → negligible collisions at lot scale; the Luhn digit lets manual entry reject a typo
rather than fail as "session not found"). `newTicketId()` in `apps/server/src/entry-flow.ts`;
validate with `validateTicketCode()` (gate MANUAL entry only — a scanned/looked-up id already in
the ledger is authoritative regardless of format).
property. Format (**shortened 13→11 on 2026-06-19**): **11 digits = 10 cryptographically-random
digits + 1 Luhn check digit**. **Length is driven by guess-resistance, not volume** — with 10^10
valid ids and the Luhn digit rejecting 9/10 malformed guesses, a blind attempt at a currently-OPEN
ticket lands at ~1-in-10^7 even with thousands parked (the [[threat-model|booth-operator]] threat),
which is the property that matters; raw count (a billion) was never the constraint. Collisions stay
negligible at lot scale. `newTicketId()` in `apps/server/src/entry-flow.ts`; validate with
`validateTicketCode()` — now **length-agnostic** (`\d{10,14}` + Luhn) so legacy 13-digit tickets in
circulation keep validating. Gate MANUAL entry only — a scanned/looked-up id already in the ledger
is authoritative regardless of format.
> **Why 11, not the requested 9 (2026-06-19):** 9 digits (10^8 space) against ~1000 live tickets
> gives ~1-in-10^5 per blind guess — an operator scripting guesses could find a valid open ticket
> in minutes. 11 keeps a strong anti-forgery margin AND was the actual fix for a printer overflow
> (below). The shorter id is *why* the width-3 barcode now fits the 72mm Cashino line.
- **Format is a property of minting, not the schema** — `identity` / `sessions.id` are free-form
`text`, so changing the id format is a code change with **no migration**. Legacy `T-<uuid>` ids
(pre-2026-06-17) remain valid keys and coexist with numeric ones.
@@ -50,6 +58,20 @@ must have:
> uncertain, so the id is carried in two independently-readable forms (1D barcode / printed digits).
> The "operator scans with a phone" path reuses the
> existing dispatch flow ([[entry-exit-readers]]) and is tracked separately (not yet built).
> **Barcode GEOMETRY must fit the paper width — root cause of a real garbage-print incident
> (2026-06-19).** The Cashino [[rongta-printer|KP-300H]] entry dispenser printed tickets as **raster
> garbage** (solid black bars / banding) while the [[rongta-printer|Rongta]] printed the *identical*
> byte stream fine. Not data corruption: the **Code128 symbol overflowed the print line.** Math: a
> Code128-B symbol is `(11·chars + 35) · moduleWidth` dots. At 203 dpi the KP-300H prints **72mm =
> 512 usable dots** (the Rongta runs 80mm = 576). The old **13-digit** id at `GS w 3` = ~534 dots
> **overran 72mm**, and the firmware rendered the overflow as pixels; the Rongta's 80mm had just
> enough room — which is why only the Cashino failed. The **11-digit** id at width 3 = **~468 dots**,
> fits both widths, and scanned the full value at the exit reader (verified on hardware). Lessons:
> (1) keep `(11·len+35)·moduleWidth` under the **narrowest** deployed printer's usable dots; (2) a
> too-NARROW module (`GS w 2`) is also bad — it scanned but returned **truncated** values (partial
> reads logged as `exit.refused.noSession` anomalies). Width 3 + 11 digits is the verified sweet
> spot. `code128()` in `packages/devices/src/drivers/printer-escpos.ts`.
- **Scan points** (both host-side reads — [[entry-exit-readers]]):
- **Pay station** — customer scans the ticket → host finds the session → shows fee → takes
payment ([[tariff]], pay-on-foot) → appends `payment`.