SetuHaul — dock scheduling for late freight
When a truck runs late, the new plan should reach the warehouse, the carrier and the customer without a single phone call — and no two trucks should ever be promised the same dock.
Client
FDE Academy hackathon — simulated 3PL client: two facilities, nine docks, a day of inbound freight
Role
Six-person academy team (Group 6). I owned the system architecture and built the services, the assistant and the deployment.
Timeframe
August 2026 — four-day build, demoed to an evaluator panel
Stack
Python · FastAPI · LangChain · Gemini 2.5 Flash · PostgreSQL · OR-Tools CP-SAT · Vanilla-JS PWAs · Vercel
Sign in as driver 90000 10006 — the one-time code is shown on screen in demo mode. Warehouse and operations sign-ins are listed on their own screens.
Screens



01 — Situation
Inbound freight runs on appointments. When a truck is late — traffic, a tyre, a slow unload upstream — the appointment is already wrong, and today the correction travels by phone: the driver calls a coordinator, the coordinator calls the warehouse, somebody forgets the customer, and two people occasionally promise the same dock to two different trucks.
The brief was a day in the life of a 3PL: 21 shipments, two facilities, nine docks, a dataset of real-shaped exceptions — a breakdown mid-morning, a reefer that needs a refrigerated bay, an over-weight load, a dock that fails while trucks are already committed to it. Build the system that keeps that day coherent.
02 — Constraints
- !Drivers do not fill in forms. They type Hinglish on a phone in a moving cab — so the entry point had to be conversational, and bilingual without ever mistranslating a dock code.
- !A wrong promise is worse than a slow one: the system had to be correct under races, retries and direct database access, not merely correct when used politely.
- !Four days, and a live demo with evaluators allowed to ask for anything — every claim had to be reproducible on command.
- !Zero-budget hosting, which later became a design input in its own right: the free-tier database paused itself after a week of quiet.
03 — Stakeholders
Driver
Where do I go now, in my own language, without calling anyone
Warehouse planner
Feasible arrivals, and no dock promised twice
Operations coordinator
The whole day at a glance, and the exceptions that need a human
Carrier dispatcher
Their own fleet's revised plan — and structurally nobody else's
Consignee
A reliable revised arrival time, with no driver's personal data attached
04 — Architecture
- 01Five stakeholder apps on one origin, three services behind them: an agent that understands the driver, a core that owns the rules and the truth, and a scheduler that optimises the facility's day. One Postgres underneath.
- 02Language lives only at the edges. The model's single job is to turn a message into a typed intent object — one LangChain chain, schema-bound so it cannot return prose. Times are then resolved by deterministic code, and thirteen feasibility predicates decide what is offerable. The driver's reply is a hand-written bilingual template with canonical values slotted in: the model reads language, it never writes it.
- 03A guardrail validates every reply against the tool outputs of that turn, so a slot no tool returned cannot reach a driver. The live violation counter reads zero.
- 04Double-booking is prevented where the race actually happens: a partial unique index in Postgres over live appointments. Holds with a 120-second TTL answer the user-experience question, idempotency keys absorb retries, and the planner's confirm re-validates every rule — but the index is the guarantee, and it refuses a second booking even from direct SQL with full write access.
- 05The scheduler models the day as jobs on machines — release times, due times, priority weights, dock outages as unavailable intervals — and solves it with CP-SAT against a greedy dispatch baseline, publishing a result only when it genuinely beats that baseline. Confirmed appointments are frozen: a commitment is renegotiated with people, not by a solver.
- 06Booking is a handshake, and every screen says so: shown reserves nothing, held lasts two minutes, requested reads 'awaiting warehouse', and only a human planner turns a request into a promise.
05 — What shipped
- ✓Five live apps — driver PWA with voice input, warehouse planner, operations dashboard, carrier dispatch, and public customer tracking that leaks no personal data.
- ✓A bilingual assistant (English and Hindi, including Hinglish) that clarifies when a message is ambiguous, disambiguates between a driver's loads, and escalates to a human instead of inventing an answer.
- ✓An evaluation harness that is the project's spine, not an afterthought: 150 automated tests, all 30 cases from the dataset guide asserted by name, the brief's 11 stress scenarios, 27 stakeholder objectives checked over real HTTP, and a trace harness that records provider, intent, tools and guardrail outcome for every conversation turn.
- ✓A demo that survives its own infrastructure: when the hosted database is unreachable, the app builds the seeded dataset into an embedded SQLite and says so in its health endpoint — same schema, same unique indexes, so behaviour is identical and only scale degrades.
06 — Outcomes
0
double bookings — enforced by the database, not the code
218
checks green: tests, dataset cases, scenarios, objectives
61%
better plan than the greedy baseline on the same objective
07 — Retro: what I'd do differently
Designing the guarantee before the feature was the decision that paid. 'One live appointment per slot' written as a partial unique index meant every later layer — holds, retries, the confirm step — became a courtesy on top of something that could not break, instead of another place to get the logic right.
Giving the language model less to do made the product better. It extracts a typed intent and nothing else; rules decide, templates speak. That is why a Hindi reply cannot mistranslate a dock code, and why the guardrail can check structure instead of trying to fact-check prose.
I over-trusted the happy path on hosting. A free-tier database that pauses after a week of inactivity is not an outage you get to be surprised by twice — the embedded fallback exists because the demo went dark, and it should have been there from the first deploy.
What I would build next, in order: the planner's dock-issue report (shipped after the demo, once I saw planners are the facility's real sensor), predictive ETAs from telematics so the system opens the conversation before the driver does, and WhatsApp as the driver channel — the agent is already channel-agnostic, so it is an adapter, not a redesign.
Questions about this engagement?
Ask the copilot →