The operating platform
Modern booking platforms can assign a guide and reserve a vehicle. That was never the hard part. The hard part is keeping the operation current as bookings, people, vehicles, geography, and field conditions change underneath it, all day. This is the layer I built to run at the speed the operation actually moves.
-
Not a whiteboard, and not off-the-shelf software either
At peak this operation puts 272 people on the river in a single day, across two bases and four river sections. When I started, a week of that ran on dry-erase marker: trips, crew, vans, and shuttles. A whiteboard has no memory, so it couldn't tell you the van you had just written down was already ninety miles away. Buying a scheduler doesn't fix it either, because a scheduler models a calendar, this person is busy until five, that room is taken Tuesday. A river operation is a chain of dependencies instead. The trip needs a crew, the crew needs qualifications for that specific river, the trip needs boats and a van and often a trailer, the van needs a driver licensed for it, and all of it has to be somewhere else tomorrow. Move one piece and the rest move with it.
-
A conflict engine that catches the collision before a guide does
I used to spend forty-five minutes late at night typing the next day's dispatch by hand. I'd hit send and a guide would reply in ten seconds: how am I on a five-day trip and a day trip at the same time, on two different rivers? The whiteboard never caught it. The engine catches that collision the moment it's created, and the section below explains how.
-
An AI layer built for speed, not for another feature list
Any competent database can hold names, certifications, and who drove which rig, and ours does. That part is table stakes. What used to eat whole evenings is everything that arrives unstructured: a guide messages that they have a doctor's appointment Thursday, a trip shifts, a group asks to be picked up an hour later, someone reports a trailer problem in passing. Each one quietly reshuffles the logistics, and acting on it meant opening records one at a time and working out by hand what else had to move. Now I say what changed, or the system reads it out of the team's messages, and it works out everything it touches. It answers the questions that used to cost a night, too: who is underutilized, who hasn't been out in weeks, whether one rig could cover two trips inside the same window. The bottleneck was never feature count. It was the cost of keeping operational state current.
- 696 commits across two repos
- ~20,000 lines in the current system
- 566 production deployments
Why off-the-shelf scheduling couldn't do it
Most scheduling products assume 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 cleared to lead big water."
Where AI sits
Everything described above this is a real operational database: constraints, qualifications, capacities, and dependencies, codified. That is the part that makes what follows possible. The AI layer has read and write access to it and knows the operation's rules, so it isn't guessing at English, it's acting on a model of how this company actually works. That is the difference between a chatbot bolted onto a booking product and an operations layer. The booking system is the system of record. This is the system of action on top of it.
And it's a read-only layer: it pulls from the booking system but never writes back into it, and that's a feature, not a limitation. The booking system records the transaction. The operation lives in the layer on top, which is where the reading and writing happen in real time. It's also why none of this is tied to one booking platform: swap the connector and the model comes with you.
The obvious version of this is an AI that drives your booking software for you, clicking through the screens. I built that first. It's too slow and too brittle, because that software was never designed for a machine to operate at speed, even with an agent driving a browser. The bottleneck isn't the model, it's the data layer. So the real work was the database: 44 interconnected tables and 207,000 connected data points modeling guides, certifications, rigs, trailers, permits, movements, and compliance, with the operation's rules encoded as data the engine reads. That depth isn't vanity. It's the only shape that can hold an operation where a guide runs a morning day-trip and launches on a five-day that afternoon, and it took a full season of real edge cases to get right. The AI is the interface. The operating model is the moat. It gets deeper every season, because every operation and exception it survives is context no competitor and no foundation model starts with.
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. When the schedule tightens, it helps me see moves that used to take an evening to work out, like when one rig can service two trips inside the same window instead of sending two. An operation moves faster than booking software was built to move, and typing was the bottleneck. A guide messages that a blinker is out, the VIN is already in the vehicles table, and a minute later the exact bulb part number and the steps to change it are posted to the channel and the repair is logged against that vehicle. Then the part lands on the shopping list, the same list the food run works from, so whoever is next in town picks up the bulb alongside the groceries and nobody has to remember to ask. That is the whole point of one model instead of four automations: a maintenance report in a channel turns into a purchasing task in a different workflow, because the system knows they are the same operation. No booking system is going to ship "which bulb fits this van," and it shouldn't have to. It took one sentence.
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, a boat tube tears and that boat can't go out again until it's fixed, a group asks to be picked up an hour later than the booking says, a vehicle problem comes in on the ops issue form. Each is a future conflict wearing plain clothes, and on a whiteboard it stays invisible until the morning it isn't. Claude reads across the channels, updates the operational record or stages the change, and the conflict engine re-checks the schedule so the collision surfaces while there is still time to solve it. The alternative isn't a tidier database, it's endless texts 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.
Here is what the speed actually buys, and it is the part I did not expect. Planning used to be 20 to 40 hours a week of tracking every detail by hand. It is 2 to 4 now, and the hours did not disappear, they moved to the work I actually like: putting the right guides with the right guests, building the crews that make a trip click, finding the part-timers who are thrilled to be there and lift the culture, and hunting the next revenue line or the next thing to make more efficient. The manual labor was never the job. It was in the way of the job.
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.
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.
A model of the river that gets better every week
Drive time on a river is not a constant, and it is not a road. How long a float takes changes with flow, wind, and the kind of crew, and the gap between two put-ins changes with it. So the system models it: a guide radios a launch time, and it pulls current flows, factors wind and trip type, and tells a driver when to leave to be at the takeout before the boats. Then, every week, it compares what it predicted against what actually happened and tightens the estimates. The model of how this operation moves through time and geography gets more accurate the longer it runs. That is the part a foundation model doesn't bring with it: the accumulated truth of how this particular operation moves through time and geography. It doesn't know that this trailer needs forty minutes to reset, that this stretch runs slow in a headwind, or that this guide is cleared for this water and not that one. That accumulated operational truth is the application.
The money the booking system can't see
The booking system knows the balance. What it doesn't do is turn that balance into an operating priority. The number sits in a record, correct and passive, and nobody is continuously asking the question that matters: what is launching soon, what is still owed on it, what changed since yesterday, and what does a person need to deal with today.
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.
The loop that closes
The system doesn't just flag problems, it carries them to done. Anyone reports an issue, by text or a form, and it becomes a tracked task with a category, a priority, a linked asset, and a named owner. If it needs buying, one checkbox puts it on the shopping list, so it rides to town on the same run as the groceries and comes back installed. So far it has carried 130 issues, closed 97, and put 69% of them on a specific person's row. Of the 14 items that have reached the shopping list, 9 were vehicle or trailer safety parts: a jack, a battery, wipers, a tire tool, the things that get forgotten until they strand a trip.
Here is one loop end to end. In June a rental went out with the wrong oar lengths, a guest complaint. It became a task: match every oar, buy tape and colored zip ties, assigned to a person. Eight weeks later a guide reported back, unprompted, that the shop now runs a blue-and-purple tape system that keeps every pair of oars together, and the counts landed in the inventory table the same night. A guest-facing failure became a purchase, became a physical system in the shop, became a row in the database, with a person doing the real work in the middle and nothing dropped across eight weeks. A booking system records the reservation. It has no concept of a bent oar, a shopping run, or who rigged the boat.
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.
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.