Drayage planning fails when a model treats a marine terminal and a rail ramp as the same door, or treats a chassis clock as a guess. This page defines the moves, the clocks, and the data the drayage TMS must hand over before a recommendation exists. Pair it with Predictive Dispatch Constraint Solving for the driver's hard hours, and with Carrier AI Readiness Evaluation before any recommendation is allowed on a dispatch screen. A model may recommend. A dispatcher accepts. The terminal appointment system stays outside the solver.
- A marine terminal and a rail ramp are different doors. Terminal id or ramp id equality is the only port identification that passes.
- The score counts bobtail miles, chassis flips, and storage and per diem clocks, and shows all three beside the plan.
- Appointment, chassis pool, last free day, driver hours, and port id fail closed. The score cannot buy them.
- A street turn is only a candidate until a named person confirms eligibility.
- The solver never books a gate appointment. A missing id is NEEDS_APPOINTMENT.
Marine terminal versus rail ramp
A marine terminal is the dock complex tied to a vessel. The container's location is a terminal id inside a port, not the port city's name. Import storage usually runs to a last free day, then a storage or demurrage clock that belongs to the terminal or the line. Export freight has a cutoff and often an earliest return time. Gate entry is an appointment or a ticket the terminal recognizes. Holds (customs, freight, line, or terminal) block the outgate even when a truck is at the pedestal. Chassis may come from a pool serving that terminal, from the line, or from the carrier's own fleet. Those are different pool ids.
A rail ramp is an inland terminal tied to a train. The location id is a ramp id. Grounded ramps and wheeled ramps do not behave the same: a grounded box needs a lift onto a chassis before it rolls, and that lift is a flip in the objective. Reservation rules, free time, and who bills storage are the ramp's published rules as loaded into the TMS, not a copy of the marine terminal's last free day. A plan that uses a port appointment id at a ramp, or a ramp reservation at a berth, is a wrong-identifier failure. City, ZIP code, or "the port" is not an identifier.
Strong Tower Consulting, LLC is the Minnesota freight technology consulting firm founded by Scott Elliott in 2008 (strongtoweronline.com). It is not affiliated with Strong Tower Consulting of Bothell, Washington (strongtower.consulting or strongtower.ai).
Dual transaction, bobtail, and chassis split
A dual transaction is one visit to the same terminal or ramp in which the driver brings a container in and takes a different container out. The tractor does not deadhead between those two boxes. Both units must be clear of holds, the terminal must allow the pair on its tickets, and the chassis under the outbound box must be legal for the inbound box or the plan must show an explicit flip. A dual is useful when it removes a bobtail that would otherwise run back to the yard for a second chassis trip. It is not useful across two terminal ids, and it is not a way to skip an appointment the terminal still requires for each box.
A bobtail, in this model, is a tractor moving with no chassis. A bare chassis move (chassis, no container) is a separate reposition and is counted on its own. Bobtail miles are what the objective tries to shrink. They are still the correct move when the driver must go get a chassis, or must leave a terminal after dropping a chassis and no dual is legal. A chassis split separates the container from the chassis. The usual planning reason is a wait: the box cannot deliver yet, and leaving it mounted would keep the chassis per diem clock running. The split parks the box where it is allowed to sit and frees the chassis. It adds a lift now and another lift when the box is mounted again.
What the objective is allowed to care about
The score the solver may compute has three counted terms: bobtail miles, chassis flips, and the storage and per diem clocks. Bobtail miles are tractor miles with no chassis, from the carrier's own mileage file, not a straight-line guess. A flip is one lift that mounts or grounds a container, counted as an integer. Storage and per diem enter as days or hours past the expiry timestamps on the snapshot, using the carrier's own planning rates if a dollar column is shown. This page does not publish a rate. Any dollar figure on a local worksheet must be labeled an illustrative planning figure until the carrier types its number.
The terms conflict on purpose. A split can stop chassis per diem and add two flips plus yard storage. A dual can cut bobtail miles and still miss a last free day if the paired box is not ready. The solver returns the feasible plan with the lowest score and shows the three terms beside it. It does not hide a per diem day inside a prettier mileage number. If two plans tie, the published tie break is the one that expires no clock sooner. A person accepts. The score is not a dispatch status.
Hard constraints the score cannot buy
Five checks fail closed. The terminal appointment must already exist as an appointment id on the correct terminal or ramp, or the plan is NEEDS_APPOINTMENT and cannot be dispatched. The chassis pool must be the pool id that will issue or accept that chassis, in the size and type the box requires, with a snapshot count or a specific chassis id the carrier already has out. Last free day is an expiry timestamp. A pickup or a grounded plan that runs past lfd_expires_at is infeasible unless a named person records an LFD_EXCEPTION. Driver hours of service remain hard: 11-hour driving, the 14-hour window, the 30-minute break, and the assigned 60/7 or 70/8 cycle, read from the hours engine. Port identification means terminal_id or ramp_id equality. A matching city does not pass.
Holds are hard as well. If the TMS marks customs, freight, line, or terminal hold true at the snapshot as-of, the outgate is infeasible. The solver does not predict when a hold will lift and does not build a plan that arrives at the pedestal hoping it has. Chassis compatibility is hard: size and type on the container versus the chassis. A 40-foot box on a chassis the pool will not accept for that box is not a small penalty. Stale data fails closed too. If any of the clock as-of times is older than the published limit, an illustrative planning figure of 15 minutes, the recommendation is not written.
When each move is legal and useful
The menu is closed. The solver classifies a candidate as DUAL, BOBTAIL, CHASSIS_SPLIT, or STREET_TURN, or it returns no move. STREET_TURN is listed here so nobody smuggles it in as a kind of split. It has its own confirmation rule in a later section. A move with no class is not "optimized." It is unscored. Dispatchers can still build a manual order, and that order has to pass the same hard checks and name a person.
Use the table as the acceptance test for a vendor demo. If the demo cannot say which row it is on, which terminal id it used, and which clock expiry it read, it is not this model. Illustrative dwell budgets in a local config are planning figures the carrier replaces with its own gate history. They are not in the table because a borrowed dwell number will falsely pass an hours-of-service check.
| Move | Plain definition | Legal and useful when | Refuse when |
|---|---|---|---|
| Dual transaction | One visit, one container in and another out, same terminal or ramp | Same location id, both boxes clear of holds, tickets allow the pair, chassis fits or the flip is explicit, driver hours cover the visit | Two different location ids, a hold, a missing appointment id, or a hidden chassis mismatch |
| Bobtail | Tractor moves with no chassis | The driver must fetch a chassis or leave after dropping one, and no legal dual or confirmed street turn removes the mile | Never as a silent default. The miles stay in the objective. |
| Chassis split | Container and chassis are separated | Per diem would run through a known wait, a permitted place will take the grounded box, and a person accepts the extra lifts | The box is on hold, the grounding location is not on file, or either clock timestamp is missing |
| Street turn | An empty container is reused for a booking without an empty return to the terminal | A named person has confirmed line approval, size and type, condition, and cutoff | The solver only "thinks" it matches. Unconfirmed candidates stay NOT_CONFIRMED. |
Per diem and storage are clocks with an as-of time
Storage and per diem are not a mood about the port being busy. Each one is a timestamp the TMS stored from the terminal, the ramp, the pool, or the line, plus the time that value was known. lfd_expires_at is when free storage ends. The carrier also stores the rule it was given, as that timestamp, not as a bare date the solver interprets. If the terminal's free time runs through the end of a calendar day, the TMS writes that end of day in the terminal's time zone. The solver does not assume noon, and it does not assume local midnight in the data center.
Chassis per diem uses per_diem_free_ends_at for the chassis id, from the pool or line agreement the carrier loaded. Starting a split does not zero the clock by optimism. The clock stops only when the event the agreement cares about is posted, usually an ingate of the bare chassis to the pool or a grounded status the pool accepts. Until that outgate or ingate is on the snapshot, the plan shows the clock still running. If storage_as_of or per_diem_as_of is missing or stale, the status is STALE_CLOCK and no split is offered to "save" a day the system has not seen. A guessed last free day is a defect, not a default.
Do
- Store lfd_expires_at as a timestamp in the terminal's time zone
- Show the per diem clock running until the pool event posts
- Mark STALE_CLOCK when an as-of is missing or old
Avoid
- Assuming noon or data-center midnight for free time
- Treating a split as zeroing the per diem clock
- Offering a split to save a day the system has not seen
| Clock | Fields | As-of | Hard rule |
|---|---|---|---|
| Terminal or ramp storage | lfd_expires_at on the container and location id | storage_as_of | Past expiry is infeasible unless a named LFD_EXCEPTION exists |
| Chassis per diem | chassis_id and per_diem_free_ends_at | per_diem_as_of | Do not split or return against a blank free-time end |
| Driver hours | Remaining drive, 14-hour anchor, break, cycle | hos_as_of | The hours engine receipt wins. The drayage score cannot extend it. |
| Gate appointment | appointment_id, window, location id | appointment_as_of | Missing id is NEEDS_APPOINTMENT. The solver does not create one. |
Street-turn eligibility is confirmed by a person
A street turn reuses an empty import container for an export booking and skips the empty return. It is attractive in the objective because it can cut a bobtail and a terminal visit. It is also the move most likely to be illegal for that box. Eligibility is not a distance match. The row needs the equipment owner's approval code, the booking number, size and type equality, a condition check, and proof the export cutoff is still reachable on the driver's hours. The solver may propose a candidate when size, type, and location line up. The candidate status is NOT_CONFIRMED.
A named person confirms it. The eligibility row stores user id, confirmed_at, approval code, and the container number they looked at. Dispatch of a STREET_TURN without that row is a pilot failure. Confirmation is not a standing rule for the steamship line. The next box needs its own confirm. If the person declines, the empty goes back under ordinary return rules, and the solver may recommend a dual or a bobtail instead. It may not relabel the declined turn as a chassis split to keep the same score.
The solver must not book a gate appointment
Appointment systems belong to the terminal or the ramp. The solver has no credential there and no job that posts a reservation. It may read appointment ids the TMS already stored after a person, or a separate booked process, created them. It may say NEEDS_APPOINTMENT and show the window it would have wanted, as advice. That window is not a slot. A driver is not sent because the advice said 14:00. The accept path checks that appointment_id is present, that its location id equals the container's terminal or ramp, and that the window still covers the planned arrival after dwell. If the id is absent, accept is rejected.
- Appointment id present. Read only an id the TMS already stored. Absent means accept is rejected.
- Location id matches. The appointment's location id equals the container's terminal or ramp.
- Window still covers arrival. The window covers the planned arrival after dwell.
There is no "book if the API is up" flag. Logs should show zero create-appointment calls from the solver process. A non-zero count stops the pilot.
This rule is structural. There is no "book if the API is up" flag. A later integration that creates appointments is a different system with its own human control, and it is not given to this solver by this page. Suggested times that happen to match a slot someone else booked can be linked by id. They are not evidence the model reserved the gate. Logs should show zero create-appointment calls from the solver process. A non-zero count stops the pilot.
Data the drayage TMS must supply
The snapshot is a contract. Required fields are container number, chassis id (null only when the box is already grounded and the plan says so), last free day as lfd_expires_at, and the ingate and outgate timestamps the TMS already knows for that container and that chassis. Ingate and outgate can be null on a box that has not moved yet. They cannot be invented. Also required for the hard checks: location id (terminal or ramp), hold flags, size and type, pool id, appointment id if one exists, and the snapshot as-of. A chassis id that does not belong to the container on the last outgate is a data error, not a split.
The hours engine receipt is attached for the driver the plan would use. Mileage for bobtail and bare-chassis legs comes from the carrier's network, keyed by the same location ids. The solver does not geocode a terminal from a marketing name. If container number or lfd_expires_at is missing, the row is UNSCORED. Partial cleverness, such as optimizing bobtail miles while the last free day is blank, is how a cheap mile becomes a storage bill. The dispatcher sees the missing field. They do not see a recommended split.
Pilot metrics on one terminal or one ramp
Pilot on one marine terminal or one rail ramp, not a coast. The gate is three zeros: zero appointment-create calls from the solver, zero dispatches whose location id disagreed with the container, and zero street turns dispatched without a confirmation user. A fourth operating rule sits beside them: no move past lfd_expires_at unless the exception row names the person who accepted the storage. One break stops the pilot. Do not retune the bobtail weight until the exception disappears from the report.
Collect the rest without a target: recommendations by move type, NEEDS_APPOINTMENT counts, STALE_CLOCK aborts, flips on accepted plans, bobtail miles on accepted plans, and how often the per diem clock was still blank. An illustrative watch is four weeks. That is a planning figure for the gate, not a claim that any harbor has already cut bobtail miles or per diem. If the three zeros fail in week one, week four does not get to average them away. Write the location id on every row so another terminal's clean days cannot be pasted into the log.
Frequently asked questions
The answers are dispatch rules. They do not state a terminal's current tariff, and they do not clear a hold. Tariffs and hold release stay in the systems that already own them. This model reads the timestamps those systems gave the TMS and refuses to plan past a blank or a stale one.
Bring one terminal's location id, a week of ingate and outgate rows, and the name of the person who may confirm a street turn. If the last free day is still a note in a spreadsheet with no expiry time, the honest status is UNSCORED.
What is a dual transaction, a bobtail, and a chassis split?
A dual transaction is one gate visit that brings one container in and takes another out. A bobtail is a tractor moving with no chassis. A chassis split separates the container from the chassis so the chassis can be returned or reused while the box waits. They are different moves. A street turn is a fourth case and needs a person to confirm eligibility.
How are per diem and storage clocks read?
As timestamps with an as-of time from the drayage TMS, not as a guessed date. Storage uses lfd_expires_at. Chassis per diem uses the free-time end the pool or line actually gave the carrier. If the as-of is older than the published limit, the solver does not recommend a split or a pickup to save a clock it has not seen.
Who confirms that a street turn is allowed?
A named person. The solver may list a candidate when size, type, and location match a booking. It cannot mark the container eligible. Dispatch requires an eligibility row with that person's user id, the line approval code, and the time they confirmed condition and cutoff.
Can the solver book a gate appointment by itself?
No. It may attach an appointment id the TMS already holds. If that id is missing, the status is NEEDS_APPOINTMENT and a person books the slot in the terminal or ramp system. The solver has no credential to create a reservation, and a suggested clock time is not a booking.
What must the drayage TMS supply before a plan is scored?
Container number, chassis id when a chassis is under the box, last free day as an expiry timestamp, and the ingate and outgate times it already knows. Terminal or ramp id, hold flags, and appointment id are required for the hard checks. A missing last free day blocks any plan that would race that clock.
Score drayage moves against real clocks, not guesses.
Strong Tower Consulting provides independent systems architecture, feasibility audits, and engineering roadmaps for motor carriers and freight brokers. Contact our advisory team or call 763-244-1373 to discuss your operational data and evaluate practical implementation paths for your fleet.
Published October 23, 2026 by Strong Tower Consulting, LLC. Founded 2008 in Minnesota by Scott Elliott.