driver_server.py is its own ASGI app on its own
App Service at driver.air-gourmet.com — the same separation portal_server.py
already gives the client portal. It never imports server.py, so there is no URL a
tablet in a van can be made to reach that shows an invoice, a customer or another day's board.
Sign-in is name → PIN → van on a tablet enrolled once from ops Settings: no Microsoft
licence per driver, and nobody has to remember a password.
Taking a van somebody else is in? You can — it asks why and tells the supervisor.
Deliveries start at 4–6am. Every screen here is the ops app's own dark rail
(--sidebar-bg #23211C) opened out into a whole app — same terracotta, same type, no
second brand. A cream screen at 04:00 in a van blows the driver's night vision and then they
cannot read a tail number on a dark ramp. The only lifted values are the status colours,
because --ok #1F9D6B genuinely fails contrast on that background.
Winter mornings mean gloves, and gloves do not work on a capacitive screen at all — so the answer is not "bigger buttons", it is fewer touches. A stop is four taps. Nothing needs a swipe, a long-press, a pinch or a drag. The one fine-motor interaction, the signature, is made by the person receiving the order, who is not wearing driving gloves.
Sixteen people on the board today — eleven drivers and five supervisors — and it will grow.
A native <select> pull-down is the wrong control here: it hides the list behind a
tap, renders as a tiny OS overlay with row heights the platform picks, and gives no room for the
van and stop count that tell a driver they picked the right row. The list is the screen, with
a search field at the top that filters as you type.
Two letters in — matches anywhere in the name, so her finds Herbert and lup finds Lupe S.:
On today comes first — usually three or four people, straight off the runs ops built that morning, with van and stop count so a driver sees their own run before they touch anything. Then everyone else alphabetically, then supervisors. Nobody has to search on a normal day; search is there for the day somebody is covering.
It was worth checking against the gloves rule — but a PIN has to be tapped bare-handed anyway, so the search field costs nothing that was not already being paid. It stays optional: scrolling to your name always works.
The van is a claim about today, so it happens once the app knows who is making it. It is pre-selected from the run ops built, because on a normal morning it is already right and confirming beats choosing. Changing it is one tap, for the 04:00 reality where #5 would not start and Porfirio took #6.
Vans already signed into are shown as such rather than hidden, because sometimes it is genuinely
right. Taking one asks why and tells the supervisor. This is the fix for VEHICLE # on
the board being a dropdown somebody types from memory.
"Out at 03:44 · Porfirio · Van #5" is DATE · DRIVER · OUT BY · AM/PM on the CREDIT
CARD & VEHICLE LOG sheet, written without anybody typing it. Signing out at the end is the
IN BY time Elmer says is always missing. Still v2 — but the van step is what makes it nearly
free.
One-time, from Settings → Tablets in ops: name the device ("Van #5 tablet") and it takes a long-lived device token. Lose a tablet and you revoke that one device. An unenrolled device gets the enrolment screen and nothing else — no order, no client name, no list of drivers. The tablet lives in a van but does not decide which van the run is on; the driver does.
Every photo, signature and delivery time is stamped with a person and a vehicle, not a device — that is what makes it evidence when a client disputes a drop, and what fills two columns of the board by itself.
A pickup sits in the run in route position, dashed instead of solid so it reads as a different thing at a glance. Today they live in a Google Keep note titled PICK UP ORDERS shared with seventeen people — the driver is expected to have read it. Here it is stop 5.
Signal on a ramp at VNY or SNA is not reliable, and a driver who taps Complete and sees a spinner will tap it again. Completions, photos and signatures are written locally first and pushed when there is signal, with a plain count of what is still queued. Nothing is lost, nothing double-posts, and the driver is never blocked by the network.
You build routes strategically every morning and you are better at it than a solver that has never heard of Signature North vs South. Tapping the FBO opens the driver's own maps app, which is what they already do and do well.
DELIVERY TIME is not a drop-off target — Wednesday's real rows show a 10:00 AM order dropped at 02:00 and an 11:00 AM at 05:45. So the column appears as a fact with a neutral label and drives nothing: no countdown, no red, no "late". Question 1 for Elmer is what it actually means; the app does not guess in the meantime.
One button, always there. It sends the supervisor on duty the driver, the van, the stop and a note. Without it, the first thing a driver does when reality diverges from the tablet is stop using the tablet.
delivery_proof row and the same
order_photo records that ops already writes today — same storage, photos to the
order-photos blob container, never Postgres.
ACI Jet North — matches where this order is going.
Elmer: "these three orders out of your five have to go to Signature North, these two out of five have to go to Signature South… it happens all the time. Two days in a row." The tablet knows where it is and the order knows where it should be, so confirming the FBO on screen catches it — no QR codes, no label printer, no whole workflow to build first.
12540396 goes to ACI Jet North, 1.8 miles away.
The override exists, but it asks for a reason and records it in
override_reason — a genuine exception survives, a mistake gets caught at the tailgate.
Joe: "you have to hold still for a split second, which sucks… and then the whole thing is glitchy" and "you can't zoom in to see if the blueberries are rotten." Two checks on the device before a photo is accepted — sharpness, and whether the subject fills enough of the frame. Two seconds, and it saves the deviation nobody can answer. Uploads use the same client-side downscale ops already uses (1600px / 0.78), so it is small over cellular and still zooms.
The aircraft left, the FBO is closed, nobody will sign. If the only button is Complete, drivers press it anyway and the record is worse than nothing. This asks a reason, takes a photo, and puts the stop back in front of the supervisor while the driver keeps moving.
The tablet writes through the existing delivery.save() and
photos.save(kind="handover"). Same blob container, same managed identity, same
5-minute user-delegation SAS on read. Step 1 was built office-side precisely so this step is a new
surface, not a new record.
ORDER OR TAIL # · PICK UP LOCATION · ORDER STATUS · P/U TIME · DRIVER · P/U STATUS) plus a
Google Keep note called PICK UP ORDERS is the whole system today. It is third-party
collections — Cafe Ficelle, True Foods, See's Candy — and it is non-food: pillows and
pillowcases, a double duvet, ceramic soup bowls, pyjamas, and newspapers tagged per order and per tail.
Every line in that Keep note already carries its order number ("2 CANS DIET COKE - 12506356"),
which is exactly what makes this modellable.
No FBO to confirm, nobody to sign, no printed name — and a per-line outcome instead of one outcome for the stop, because the real failure is "they had four of the six". Reusing the delivery screen would make drivers tick things they did not get.
The Keep note already tags each line ("4 PILLOWS & PILLOWCASES – 12541253"), so the collected item lands against the right order in ops the moment it is ticked. That is what turns a shared note into a record: the office sees the duvet arrive on order 12541253, not "Porfirio said he got it".
Elmer uses that phrase in Keep as an ad-hoc sub-status. Here it makes the line uncollectable and says why, so a driver does not stand at a counter asking for soup bowls nobody bought. It is the office's problem, shown to the driver as a fact.
"3 NY TIMES – 12532463, N1454H" is bought, not collected from a named vendor, and the driver buys them en route. Ops already has a shopping rollup, so this is a boundary question rather than a design one — see question 5.
The PICK UPS tab has a P/U STATUS dropdown someone types after a phone call. Six of
seven ticked at 07:14 by Porfirio in Van #5 is that column, without the call.
| AG-4423AG-direct | Signature Aviation · SAN | board time 3:30 PM | Van #3 Van #5 Van #6 |
| Pick upTrue Foods | Ventura · for 12516663 | ready 8:00 AM · 1 item not ordered | Van #3 Van #5 Van #6 |
| # | Order | Where | Board time | Type | Status |
|---|---|---|---|---|---|
| 1 | 12540850NetJets | NetJets VNY | 6:29 AM | Delivery | Dropped 05:15 |
| 2 | 12540084NetJets | NetJets VNY | 6:00 AM | Delivery | Dropped 05:15 |
| 3 | 12536237NetJets | NetJets VNY | 10:00 AM | Delivery | Dropped 05:16 |
| 4 | 12540396NetJets | ACI Jet North SNA | 7:36 AM | Delivery | In the van |
| 5 | Pick upCafe Ficelle · N1454H | Camarillo CMA | ready 7:00 AM | Pickup | 7 items |
| 6 | 12536425NetJets | ACI Jet North SNA | 7:00 AM | Delivery | — |
built plus every pickup for the day. It should
reach zero before the first van leaves, and if it does not, that is visible rather than remembered.DRIVER and VEHICLE # come from the run, not a dropdown.ORDER STATUS comes from the stop.ORDER DROP OFF TIME — the column Elmer actually cares about and the one that is
blank most often — is written by the tablet at the tailgate.P/U STATUS on the PICK UPS tab comes from the ticked lines.tpp-sage-importdelivery_proof, order_photo, the blob
container, managed identity, the SAS read path, the client-side downscale and the 22-check
test_delivery.py gate are all live and verified on prod. The driver app writes
through those same functions. What is genuinely new is a surface, a run, and pickups.
delivery.save() and delivery.get(), unchanged. The
tablet fills driver, device, fbo_confirmed,
override_reason, lat/lon — columns that already exist and are mostly
empty because only the office writes today.photos.save(kind="handover"), blob + thumbnail, never Postgres.
Pickups add one kind: pickup.order_event / events.py already renders the
timeline. Delivery and pickup events just write to it.portal_server.py already proves the separate-ASGI-app pattern,
right down to its own App Service and its own custom domain.order_link already does multi-use expiring tokens. Device
enrolment is the same shape with a longer life and a revoke.DATE · DRIVER · OUT BY · AM/PM on the
CREDIT CARD & VEHICLE LOG sheet, and signing out at the end is the IN BY time Elmer
complains is always missing. It is the obvious v2 and it costs almost nothing once v1 exists.driver_server.py, sign-in, read-only run. Nothing writes. Real drivers,
real stops, on a real tablet, for a week. Cheap to abandon if the shape is wrong.