Upload entry gates — customer-number retirement (2026-08-07)

JES/Groundworks retired customer numbers, dead-ending the self-service upload entry flows at unlikely.pl (pier logs, QR on the JES pier log sheet) and unlikely.pics (job photos) — both hard-required permit # + customer #.

New gate (entirely-api eac14f1, 5e6c37e, 4c329e1)

Identity = any TWO of {permit #, job #, address} agreeing on one project. One publicly visible fact alone still never mints a token (spam gate kept).

  • Step 1: permit and/or “Job number (customer # or BC job #)” — fields individually optional, server enforces at-least-one. Two-field hit mints directly; one-field entries go to Step 2.
  • Step 2: address is the second fact — permit+address tried first, then job+address. Job # matches customer_number OR bc_job_number (migration 029).
  • Step 3 “call the office” unchanged.

Matcher notes (services/permit_lookup.py)

  • Permit comparison is now separator-insensitive (spaces/hyphens/#/slashes stripped both sides) — entry gates only; cert_public keeps its stricter rule.
  • Permit-first candidate sweep = PostgREST imatch regex requiring the permit’s digits as an in-order subsequence (0[^0-9]*5[^0-9]*8…). A contains-ilike on the merged digit run does NOT work — hyphen-stripping merges digit groups and the substring never matches stored hyphenated permits (caught live on RES-05877-2026 / MAN-9167).
  • Token display slot falls back to bc_job_number; upload pages label it “Job #” now.

Tests: tests/test_permit_lookup.py + tests/test_upload_entry_flow.py (29). The per-stop tokenized link (one-upload-path) is untouched and remains primary.