Justin Smith

The operating platform

Adventure Idaho · 2024–present

The company dispatched trips off a whiteboard. A whiteboard has no memory, no visibility, and no way to tell you that the van you just assigned is already 90 miles away.

A wall-mounted whiteboard holding one week of rafting operations in hand-written marker: columns for Monday through Saturday, launch times with guest counts, crew assigned per trip, driver and van assignments, and colored lines tracking multi-day trips across days.
What it replaced. One week of trips, crew, drivers, vehicles, and shuttles, in dry-erase marker, with no memory and no way to tell you a van was already committed.
The dispatch board in the operating platform: a multi-week grid of rafting trips with launch times, guest counts, and crew status, above a row of live counters for guests out today, guests in the next seven days, trips still needing a lead, and rentals awaiting a decision.
The same operation, in the system. Every trip carries a status the whole crew can read: red for a hard conflict, yellow for something a human still needs to settle, green for cleared. The counters across the top are the questions a manager used to answer by counting the board by hand.
The same dispatch board on a phone: a week of trips as stacked cards, each showing launch time, guest count, day number of a multi-day trip, and a cleared or open status, with a movements row underneath for driver runs.
The same board on a phone, which is where the crew actually reads it. Nobody logs in, and nobody waits for one person to send the plan out.
The crew schedule: one row per guide with role badges beside each name, and one column per day showing the trip that person is leading or guiding, driver runs with the assigned vehicle, travel and training days, and time off.
One row per guide, one column per day. The badges beside each name are the roles that person can be assigned: lead, guide, food, driver, store. Trips, driver runs with the vehicle attached, travel and training, and time off all live in the same grid, which is the part a whiteboard can hold for about a week before it stops being true.

Why off-the-shelf scheduling couldn't do it

Every scheduling product assumes people and rooms. A person is busy or free, a room is booked or open, and the day is the unit. This operation schedules a dependent graph instead: a trip needs a crew, the crew needs qualifications specific to a river section, the trip needs boats and a vehicle and often a trailer, the vehicle needs a driver with the right license, and the whole thing has to be somewhere else the next morning. Change one assignment and the consequences ripple through shuttle logistics two days out.

The part that breaks every product I looked at is that resources here aren't consumed by the day. A guide can run a day trip in the morning and launch on a five-day expedition that afternoon. A trailer might serve three or four different trips in a single day, moving between put-ins and takeouts on different rivers. Booking software wants to pin one vehicle and one trailer to one trip for one day, which is tidy and wrong. What actually governs the schedule is drive time between points, how far one trip's window overlaps the next, and how long a rig takes to reset between uses. None of those are a calendar.

The constraints aren't static either. Trip types have different crew ratios, different rigs, different put-ins. Capacity is gated by permits. Off-the-shelf tools can express "this person is busy." They can't express "this trailer is committed until it clears the takeout and drives back," or "this guide isn't licensed for this section during this flow range."

Configuration as data, not code

The single most useful architectural decision was pushing business rules out of the code and into tables: trip type rules, conflict rules, capacity tiers, and a config table of thresholds. Adding a trip type is a row, not a deploy.

That decision is what makes the system portable. The rules are the part that's specific to one company. The engine that reads them isn't.

The conflict engine

The largest single component. It walks the schedule and flags three classes of problem: hard conflicts where a person or asset is committed in two places, soft conflicts that need a human look, and missing data where it can't judge safely. That third category matters more than it sounds. A system that silently passes incomplete records teaches people to distrust it.

It runs two ways: event-driven when a record changes, and on a schedule as a safety net, because event triggers eventually miss something. It went through 27 versions, and most of those versions exist because real operations kept producing edge cases the model hadn't imagined.

A week of trips with status flags: a legend defining red as a hard conflict, yellow as needing attention, and green as all clear, above trip cards showing cleared trips, trips still open on crew, and guest counts that have changed since the trip was set up.
Hard, soft, and clear flags across the week. Red is a real collision, yellow needs a human, green is cleared. The pink badges are guest counts that moved after the trip was built, which is the change most likely to quietly break a rig plan.

Integrations

Reservations sync from the booking platform through a purpose-built API adapter, hourly, with a nightly deep reconcile and a de-duplication pass. Cancellations cascade: cancel a reservation and the day's operation, its driving movements, and its trip log entries all stand down together. Alerts route to Slack. Field communications from satellite messengers land in a team channel instead of one person's phone. Texts from satellite messengers route through a company number into a team Slack channel, so a message from a canyon with no cell service reaches everyone who needs it rather than whoever happened to be holding the phone.

The adapter pattern is deliberate. Everything downstream of a standard intake table is portable; only the connector is vendor-specific.

The team field-comms channel: a satellite text relayed in from the river through the company number, alongside automated messages telling a driver when to leave for a pickup and when boats are expected to land, in a workspace whose channel list is organised by base and by trip.
Satellite texts from dead-zone canyons relayed into the team channel, plus automated shuttle-timing pings. Names and numbers redacted. The point is that field communication lands somewhere the whole team can see it, instead of on one person's phone.

The money the booking system can't see

Because this layer sits on top of the booking data rather than inside it, it can see things the booking system has no view into. A reservation records what someone agreed to pay. It does not record whether the money actually arrived, and those are not the same thing.

Deposits get collected and remainders never do. A card on file fails on its scheduled run and nobody sees the failure. A guest changes a reservation and the balance moves with it, quietly. In every one of those cases the trip still launches, and the gap only surfaces afterward, when chasing it means calling someone who has already gone home.

So the system reads the booking data every morning and posts what is still owed on trips going out in the next six days: which reservations changed, which cards failed, what is outstanding, and a link to contact the guest or run the charge. It is a plain report with working links, which is the whole trick. We estimate it has moved more than $20,000 of reservation balances into collected before launch, rather than chased afterward. The daily digest is covered in detail here.

Collecting money you are already owed, before the guest is on the water, isn't new revenue. It's the same revenue arriving on time instead of late or never. That is the argument for an operations layer in one sentence: the booking platform isn't wrong, it just isn't looking.

Where AI sits

It's the interface, not the decision maker. The speed difference is the whole point: instead of opening records and hand-editing every name, contact, and field one at a time, I say what changed and the system works out what that touches. Updates get made by voice from a truck. Questions get asked in plain language against live operational data. Reports that used to take days get generated in minutes. An operation moves faster than booking software was built to move, and typing was the bottleneck.

A spoken request being dictated into the operations system on a phone, waveform showing, asking it to check river miles per guide for the year, who is over and under utilized, who has not been on the bigger multi-day trips, and when vehicles could go to the shop. The same request transcribed in full, ending with an instruction to pull recent guide messages about scheduling conflicts, build them into the board, and return an overview layer for a person to verify.
A spoken request becomes real changes, with a human verifying. Note how it ends: build it into the board, then give me an overview layer I can check. The asking is the fast part. The verifying is the part that stays human.

The other half is reading what the team already says. Operations don't announce themselves as structured records. A guide mentions a doctor's appointment in a channel. Someone reports a strap that tore on the last trip. A vehicle problem comes in on the ops issue form. Every one of those is a future conflict wearing plain clothes, and on a whiteboard it stays invisible until the morning it isn't. The system reads them, matches them against the schedule, and surfaces the collision while there is still time to solve it. The alternative isn't a tidier database, it's endless texts and emails and whoever happened to read them.

But anything touching money, compliance, or safety requires a human confirmation, and the system reports back exactly what it changed so a person can verify it. That constraint isn't a limitation, it's what makes the crew willing to use it.

How it was built

I'm not a career software engineer. I specify, direct, review, and ship using AI coding tools, and I've been doing it long enough to know where that approach breaks: it will confidently produce something that works on the happy path and fails on the operation's actual edge cases, which is why the version history is long and why the specifications matter more than the code.

What I'd argue this demonstrates isn't that I can write JavaScript. It's that I can hold a complex domain in my head, decompose it into something a machine can execute, and keep it running in production while 44 people depend on it during a season where mistakes hurt people.

This page is the working proof of an argument I make separately: booking software solved the transaction and left the operation unbuilt.