Long trips broke this product for a while, and they broke it in the least helpful way available: not with an error, but with a plan that started, streamed a few sections, and then simply stopped progressing.
The cause was a single request. Composing a day-by-day itinerary in one call is fine at seven days and fatal at sixty. The call runs past the serverless function's limit and is killed mid-sentence; whatever partial text came back fails validation, because half an itinerary is not an itinerary; and the pass ends with nothing persisted. Retrying does the same thing, at the same cost, at the same point.
Windows
The itinerary is now composed one window of days at a time. Each window is a separate call, sized so it comfortably finishes, and no window is allowed to straddle a change of base — so a call always has a single town's verified places in front of it, which improves the writing as well as the odds of finishing.
Each window is persisted the moment it lands. That single change is what makes long trips possible, because it converts the work from one thing that must succeed into many small things that accumulate. A pass that dies after four windows has still banked four windows.
Passes, and the deadline that ends them
A trip too long to finish inside one invocation stops at a soft deadline of its own — comfortably before the platform would kill it — and tells the client it has more to do. The client immediately reopens the stream, and the next pass composes only the days that are still missing. As many passes as it takes.
There is one more safety net behind that. A plan that reached a terminal state with fewer days than it asked for is picked up and finished the next time somebody opens it, rather than sitting there permanently short.
Why “partial” had to be redefined
The itinerary emits repeatedly as it fills in, and those intermediate emits are provisional. Early on they coloured the trip's status, so a perfectly healthy forty-day plan that was simply not finished yet reported itself as partial — which is the word we reserve for a section that genuinely could not be verified. Provisional emits now persist and stream but never touch the status.
What actually limits length
There is no architectural maximum any more. The cap in the code is a sanity bound, not a design limit; what really governs how long a trip can be is credits, since a plan is charged in proportion to its length. That is the honest constraint, and it is a much better one than “the request timed out”.
The general lesson is not about travel. Any long generation on a platform with a hard execution ceiling has to be decomposed into units that are individually completable and individually valuable — and then written down the instant each one lands.
Put it to the test
Every claim in this post is something you can check on a real plan. One sentence is enough to start one.