Case Study · 2026
NextJune
A quote-to-cash workspace for air freight forwarding — where the system carries the memory, and the person makes the call.
Operational state modeling · Decision support · Reconciliation feedback loops
The problem
Pricing a shipment draws on three things, and they live in three different places. One of them is somebody's memory.
A freight forwarder receives an enquiry, asks several carriers what they would charge, picks one, adds a margin, sends a quote, and — weeks later — reconciles against whatever the carrier actually invoiced. To do that well you need three things at once: the prices carriers are quoting today, which arrive by email; what comparable shipments were priced at before, which sits in folders; and what margin this particular client gets, which is usually carried in someone's head.
The consequence is that the work is gated on recall. Experience becomes the bottleneck: a new person cannot quote without it, a stored default silently drifts from what is actually being charged, and mistakes surface long after they were made — a senior colleague catches a junior's error the same week, while the junior catches the senior's a year later, during reconciliation.
NextJune is built on one idea about that: the person makes the decisions; the system remembers, and points at what deserves attention. It never chooses a carrier or sets a margin. It makes sure that whatever somebody would have needed to remember is already on screen at the moment the decision is being made.
The rule I set for scope
Solve the general problem, or say out loud that it is not one.
The obvious failure mode for a tool built out of domain experience is that it encodes one company's habits and calls them an industry. So the brief carried an explicit instruction: build for what small and mid-size forwarders share, and if a requirement turns out to apply to only one operation, say so and stop rather than building it.
That rule is why the data is synthetic end to end, written from scratch rather than derived from anywhere. It is also why the demo below is open: there is nothing in it that needs protecting, so the whole system can be looked at and argued with.
The shape of the work
Quoting is not a form. It is a form with a wait in the middle.
Carriers usually come back within a few hours. That makes the sequence gather → ask → wait → return → price, and it makes the interesting moment the one that is easiest to lose: the enquiry that has not come back. Past half a day, somebody has to notice and chase, and the reason they usually do not is that nothing is keeping track.
A system that models quoting as a single form is modelling a different job. This one models the wait.
Decisions
Each of these exists to take something out of someone's head and put it on the screen.
A quote exists before its prices do
You have asked three carriers and none has answered. A spreadsheet records that as nothing at all — the rows simply are not there, which is indistinguishable from never having asked.
Here an enquiry exists from the moment it is sent, with no rate and an explicit status: pending, responded, no response, declined. Four different facts, where a blank cell would have been one. Only some of them mean somebody should chase.
Who to ask is worked out, not recalled
Knowing which carriers to approach for a given lane is exactly the knowledge that takes years to accumulate and is never written down. It is also derivable: it is in the history of who was actually booked.
Carriers are suggested per route from that history, scored on how often they were selected, less a penalty when they did not reply or when their invoice later came in disputed. The weights are deliberately crude and not fitted to anything — a dispute costs more than a single silence, but neither can fully cancel out a carrier who has actually been booked on that lane repeatedly. The score is a retrieval aid, not an automated carrier selection model.
One detail only showed up in the data. Matching a trucking lane on both the pickup and delivery postcode almost never matched anything, because the customer side changes every shipment. Only the airport end is a stable anchor. That was a discovery, not a design.
Recent margin outweighs average margin
Every client has a stored default markup. It goes stale quietly: rates move, the person quoting adjusts, and nobody goes back to update the record. The average across all history hides that — the recent quotes are the ones that say what is actually being charged.
So the markup shown is weighted toward the last few quotes, and when it has drifted from the stored default by more than a couple of points, the form says so, with both corrections one click away. It does not pick one.
Reconciliation is a pricing input, not an accounting chore
The carrier's invoice arrives weeks after the shipment. Reconciling it means comparing against what they quoted — or, when no enquiry was sent because the cargo was ordinary, against the contract rate on file. That is the baseline; enquiries are for the shipments that fall outside it.
Which means a variance is rarely just a billing error. It can be a re-weigh on arrival. It can be a rate card nobody has refreshed, quietly mispricing every quote on that lane until someone notices. Reconciliation is where those surface, so it is wired back into quoting rather than parked in accounting.
Estimated cargo and actual cargo are different facts
Customers quote from estimates. After packing, the weights and dimensions change — not occasionally, but as a matter of course. A system that lets you overwrite the estimate loses the thing reconciliation needs, which is what you were quoting against when you set the price.
So actual cargo is confirmed as a separate step rather than edited over the top, and the quote carries both. Special handling works the same way: hazmat, temperature control, non-stackable and oversize are recorded on the quote because they are what makes a contract rate unreliable and an email enquiry necessary.
Deliberately out of scope
The first version of this was ten modules. That version was not finishable, and an unfinishable project is indistinguishable from an abandoned one.
The original sketch covered everything a forwarder does, and was meant to be reusable across industries besides. It was cut, on purpose, to the spine that has a beginning and an end: an enquiry arrives, an invoice goes out. Customer relationships, quoting, invoicing and vendor reconciliation stayed. Document generation, role-based permissions, a service catalogue and a sharing hub did not — those belong to the general platform idea, which is a different product from a freight quoting tool and should be built as one if it is ever built.
Reconciliation and payments remain listed as planned, because they extend this same spine: what a supplier quoted, what they later billed, and the money that actually moved.
How this was built
I defined the operational model, product rules, state transitions, pricing behavior, reconciliation logic, edge cases, and acceptance criteria based on freight-forwarding domain experience. An AI coding agent produced the implementation; I reviewed system behavior, tested edge cases, and directed revisions. All demo data is synthetic and was created specifically for the project.
Where it is now
Deployed on Cloudflare Workers with Supabase behind it. Money arithmetic is rounded to cents at every step so the application and the database never hold different numbers for the same quote, with seventeen tests that import the real module rather than restating it. Lint, types, tests and a build run on every push.
The demo below is a real account with its own seeded history — invented clients, invented carriers, invented shipments — isolated by the same row-level security that separates any two users, and rebuilt nightly. You can change anything in it.