Small requests from the discussions, each built end to end: planner and
map (cycling mode, date first headings, clear a day, stay times per day,
off-route stops, map lock, compact unplanned markers, pending stays,
cable car, transit quick picks), places (location filter, category from
OSM, open in maps, geo: link, image from an attachment, enrichment on
file import, places near a pin, place name language, Amap first in
China), lists and costs (packed counter, inherited sharing, Markdown
to-dos, payment notes, bookings CSV, my plan PDF), sharing (unplanned
places, travel only, without photos), journey (photo zoom, drafts,
photo order, trip suggestion, day jump), atlas (region counter, region
bucket list, places per country), vacay half company holidays, studio
copy and paste plus the book's own layouts, and RESTORE_FROM_BACKUP for
a first start restore.
Booking a night writes the hotel onto its check-in day as a stop, seated first
or at its check-in, so the drive reaches it even without hotel bookends. With
"Start and end each day at your stay" on, the evening row already ends that day
at the same hotel, so the road trip drove check-out, tonight's hotel, the day's
places and back to the hotel again. Days hides that stop and draws the stay at
the day's edges only.
seatNightBookends now passes over the stop tonight's booking wrote whenever it
seats that stay as the day's evening, in the browser and in calculate_roadtrip
alike. The stops keep their stored index, a hotel the traveller placed stays,
and the booking's stop stays while it is the day's last one or no evening is
seated. With the switch off nothing changes.
Closes#2546
On a day a flight, train, ferry, cruise or bus leaves and lands, tonight's stay was seated
by its check-in time. A hotel in Munich checked into from 15:00 stood ahead of a 15:15 flight
out of Hamburg, so the drive went from the Munich hotel to Hamburg airport and reported the
flight as nine hours late. A check-in is the earliest arrival, not an appointment: such a
stay now waits behind the landing when it lies nearer the arrival than the departure, and
its check-in only holds it when the ride lands before the room is ready. A time typed on the
stop keeps its seat.
With the stays at both ends of a day switched on:
- a day with nothing but the next stay's check-in and a ride booked across it gets no drive
from one hotel to the other, the same rule the day plan follows since #2476;
- a stay is never joined by road to a terminal more than 2000 km away. The check moved to
shared/roadtrip so Days and the road trip read one copy.
A departure terminal's pin is dated: reaching it more than half a day late no longer moves
the flight to the next day without a word, it is a late warning with the full minutes.
A check-in evening also carries the stay's check-in time, as a label only.
calculate_roadtrip runs the same code and its description says all of the above.
The check-out row showed the room's last hour and, beside it, the time the
plan leaves the hotel, which can be hours later when a place further on is
pinned. The row now says "Leaves after check-out" when it does, on the
desktop and on the phone.
A click on a road the day drives twice, out of the hotel in the morning and
again between two of its own places, went to the first pass, the drive out
of the hotel, and was refused. The second pass now takes the via, and where
yesterday's drive into the hotel and today's leg share a road, today's leg
does. A road only the hotel's drives use still takes none.
The wiki says both.
A hotel row now takes its position and name from the trip's place instead of the stay
row, which the planner fetches again only when a stay is edited. A moved pin no longer
seats the hotel at its old spot while the server plans from the new one. The drive out
of the morning hotel goes the way the first place is reached from it, the mode Days
draws that leg in, and it is set in the shared rule so the browser and
calculate_roadtrip agree.
On the rail, a hotel row that took in the morning marker starts the line. A day that
only drives from one stay to the next shows no stop count and takes a dropped stop as
its first. The hotel keeps its pin on the desktop map, and the switch explains itself
under its label and to screen readers. On the phone, the note about figures set on the
desktop now sits above the switch it does not cover. The hint and the refusal on the
hotel's drive say what they mean in all 23 languages. The via tools and the wiki say
that a via into a booked night is kept but not used, the wiki states the overlap rule
Days applies, and the arrived-from branch of the via anchor has tests.
A trip can now switch on roadtrip_hotel_bookends, a shared driving preference that is off
until somebody sets it, so every trip drives exactly as before. Switched on, the planner in
the browser and calculate_roadtrip on the server seat the hotel slept in at the start of the
next day and tonight's hotel at the end of the day, both through seatNightBookends over the
trip's stays in id order, with the earliest booking linked to each. A linked booking changes
the hotel row and asks the router nothing.
The switch sits in the desktop driving settings under the route line, and the phone's
driving figures carry the same switch as the one control they offer; both write through the
planner's own preference save, so a flip offline queues like any other. get_roadtrip_context
reports the stays, calculate_roadtrip previews the switch through its settings, and the
settings, corridor and day boundary tools say what it does and that missing means off.
The drive learns what a booked night at the edge of a day is, while nothing seats one
yet. One rule in shared, seatNightBookends, picks the stay a day wakes up in and the one
it sleeps in the way Days does, over real nights and in id order, and puts each hotel at
the edge of the day as a stop of its own that belongs to no assignment.
The schedule, the day window, the spill into the next morning, the sums and the connected
days read a night spent at one hotel as a leg going nowhere, never cut or postpone the
drive to tonight's hotel, and pass the hotel over wherever a stop is looked up by its
stored index. That lookup now also puts a place added behind a terminal where it fell.
The rail and the phone chain draw the hotel as a row of its own that opens the booking or
the stay, offers no other ways and takes no via, in every language. Until a trip switches
it on, every plan is the one stored.
A leg a routing provider answers without its from and to points threw in
the merge, and the day it belonged to never finished routing. Such a leg now
keeps the first piece's ends; the texts are still written from the sums.
A via cuts a drive into one routed leg per waypoint pair. Folding them back
onto the stop pair summed the metres and seconds but kept the first piece's
printed texts and end point, and the phone reads the texts first: a drive
into the next day of 3.9 mi in 14 min showed 1.9 mi in 6 min. Every choice
under "Other ways" writes vias, so every choice ran into it. The pieces are
now merged by one shared helper that writes the texts from the sums in the
reader's unit, for a day's own run, for the drive between two days and in
the server's plan; the drive between two days is asked for again when the
unit changes, as a day's run already is.
On the desk a refused way was said in the bar's status line and again in a
toast laid over that line. The desk keeps the status line; the phone, whose
bar has no room for the sentence, keeps the toast.
The slot the desktop stores for a ride within one day was worked out over the rows the day
list shows, so a hotel it hides on the far shore was left out, the ferry was filed behind
it and the road trip drove there overland before the crossing. It is now worked out over
every stop the drive reads, timed the way the drive times it. An overnight ride no longer
seats its arrival by the slot seeded on the day it left, and opens the day it lands on
unless somebody placed it there. A via behind a day's last stop goes once that stop is no
longer last, for the rail's drags and removals, the list's reorder and the server's sorts
and seated nights alike; some of them used to bend a leg of the day through it instead.
A leg OSRM drew while the avoidance router did not answer is now known as such: the picker
no longer reads it as that router's road, has the leg routed again when its own road is
chosen and says so, and a join that fell back is asked for again and flagged on its card.
A refused way that leaves a class out names the setting that drives it, the ferry hint
covers a crossing that lands the next day, a road another engine timed names that engine,
and the desk bar keeps the keyboard on the chip while a choice is checked and announces
the outcome. A ride on none of the trip's days is no longer said to have no date.
A ferry between two stops without a clock was seated by the clock alone, which files a
ride with nothing timed before it at the end of the day. The desktop day list stored that
slot, and the road trip drove overland to the far shore and back to the pier before the
crossing. A ride that leaves and lands on the same day, with both terminals located, now
takes the seat between the clocks that adds the fewest straight-line kilometres. One rule
in shared seats it for the day list, the slot the desktop stores, the phone and PDF lists
and the road trip in the browser and on the server. Slots already stored are left alone.
A flight, train, ferry, cruise or bus on no day made no seam and said nothing, while the
map still drew its arc. The rail and the phone tab now name such a booking with a way to
open it, and calculate_roadtrip lists it under undatedRides.
The drive from where one day ended to where the next begins was the one stretch of a
connected road trip that offered no other ways. The band above a card's first stop had no
button on the desk, and the phone did not show that drive at all. A via behind a day's last
stop, where the map already files a point dropped on that drive, was deleted when the day
was reordered under Days, and was left on no stop when that last stop was removed or
dragged up the day. Switching the avoided road classes or the day's profile kept the old
road and minutes on it until a reload.
The drive in now opens the same picker as any leg, on the desk band and on a new row at
the head of the phone's chain that names where it leaves. A choice is proven with the
seam's own router and written behind the last stop of the day before, also when a day with
a single stop lies between. The rule for that via lives once in shared (carriedSeam): it
stays with the last stop while that stop stays last, and is read like any other once it is
not, for the planner's drags and removals as for the server's sorts and seated nights. The
cached answer for a seam now records its mode and avoided classes, so changing either asks
for it again.
The picker of other ways asked OSRM with nothing avoided while the rail drives a trip
that avoids a road class through Valhalla, so the list had no current road, called a road
the rail was not on the fastest, and choosing it changed nothing. A choice was stored as
one via where the offer strayed furthest, without asking whether the router then drove
it: a point on a ferry was pulled to the pier and the drive went overland, and a way
weighed away from motorways kept the motorway after its one point. An OSRM answer standing
in for a Valhalla that did not respond was cached as the avoided road, unmarked.
The rail now hands out a router per leg with the leg's own mode, avoided classes and
engine, and its days carry the line of every leg. Offers come from that engine, and the
rail's own leg always heads the list as Current. A choice is pinned and proven against
that router with at most three pins before anything is written; a way it will not follow
is not saved and says why, with a hint when it crosses by ferry, and a leg that changed
meanwhile or a device offline writes nothing. The OSRM stand-in is filed under the plain
key only and flags the day as not avoided.
The day list draws a day by its starts, the road trip drives the order
it is stored in. Moving a stop with a start onto another day, or adding
a place that carries one, stored it at the end of the day or where it
was dropped, so the list showed it among the stops its hour falls
between while the road trip went there last. It is now stored right
behind the row the list draws it after, with a booked night read by its
check-in as the server's time sort reads it. A stop without a start
lands exactly where it did before, and wherever a moved stop lands in
the middle of a day, the via points drawn on that day stay on the legs
they were drawn for.
Behind a later start the road trip reached such a stop late, and the
night spill took its earlier clock for a midnight: the stop and every
stop after it opened the next day's card, and a day left with one stop
fell to the placeholders at the bottom. A stop the schedule marks late
now stays on the day the schedule files it on, and the stops behind it
are read from its hour, so a clock that goes back past that hour still
starts the next day. The desktop rail, the phone's road trip tab and
the MCP calculate_roadtrip tool share that spill.
The corridor search in the browser cut its tiles around a ride, the MCP tool
did not: it tiled and measured along the straight line between two terminals,
so an assistant searching a flight day got boxes and hits under the flight
path. The gap logic moves into shared/roadtrip/corridor.ts and both sides use
it. A departure less than the check-in allowance after midnight no longer
pins on the previous evening, and a hire car whose pick-up desk has no
position is not seamed at its return desk.
A hire car is no seam, but its desks are where the drive starts and ends: the
pick-up opens its day when nothing timed comes first, the return closes the
day it is handed back on, the road runs through both and the range budget
starts from a full tank at the pick-up. The return is only placed when the
booking names a return day and a located return desk.
A table, tickets or a tour on the day hang under the stop they are pinned to
or booked at as chips that open the booking; one for no stop is listed under
the day. A ride that leaves and lands on the same day is one block on the
rail and in the phone chain, not two terminal rows and a band. Both shells
read the same helpers (carrierRide, stopBookings, roadtripRowModel).
Server reads car bookings into the plan context; the MCP descriptions say so.
The pt-BR locale had the three ride strings in Breton, fixed alongside.
Refs #2428
The road trip routed straight from one stop to the next, whatever stood
between them: a flight booked for the afternoon became an eight-hour drive
on the map, with the hotel reached by road from a town the traveller had
flown out of (issue #2428). The day plan has treated such bookings as
carriers for a while, and the drive now reads them the same way.
A flight, train, ferry, cruise or bus with located terminals is a seam. Its
two terminals are seated among the day's stops where the day plan lists the
booking, as stops of their own: no number, no stay, no kind, not draggable,
and an id below every assignment and every automatic night, because they
stand for none. The road ends at the departure terminal and starts again at
the arrival one; the leg between them is the ride, in the booking's own
mode, with the timetable's minutes and no distance, and no router is ever
asked for it. The departure terminal is pinned a check-in ahead of the
timetable and left on the timetable's minute, so a drive that cannot make
the airport in time is late the way a missed pin is. A ride that lands on a
later day seats its arrival at the start of that day; with connected days
the join between the two is the ride, and with daily travel times set the
ride runs on the booking's clock through the night, with no day end placed
on a flight.
The map draws the ride as the booking's own arc, the one the day plan
already draws, beside the roads. The day's distance and driving time count
the roads only, the range budget spends nothing on a ride, and after one the
car cannot take it starts afresh. The corridor search leaves the ride out
and drops a hit under the flight path; a via cannot be filed against a
terminal, and other ways of driving are offered for the road into a
departure terminal only. The server plan reads the same three tables and
seats the same stops, so calculate_roadtrip answers the same as the rail.
Closes#2428
A hotel entered under Days with a check-in at ten was reached at a quarter
past twelve in road trip mode. Two things: the schedule read the check-in
as a floor that held nothing once anything before it set the clock, and
the server seated the night last unless something pinned later pulled it
forward, so two stops without an hour stood ahead of it.
The check-in now pins the night the way its own time would, in the plain
schedule and in the daily window, where a check-in the drive cannot make
is the conflict a missed pin is. The night is seated first on its day;
only a stop with a clock at or before the check-in stays ahead of it, and
the stops without one follow. A new check-in seats the night afresh, a
change of notes leaves a dragged night where it is.
A day that is nothing but its booked night stands among the days now,
with the hotel and its check-in, instead of at the foot of the rail under
"only X so far", which read as the hotel missing from the road trip.
The client puts a stop the server seated at the front where the server
put it, rather than appending it to draw second until the next reload.
Two boot migrations. The first appends the missing dated days to a trip
the old 365-day cap clipped, only where the dated days still run unbroken
from the start date, so a trip that was re-dated by hand or lost a day in
the middle is left alone; dateless days that hold content stay behind the
dated ones as generateDays keeps them. The second rebuilds
roadtrip_day_boundaries without the CHECK that held day_number to 366,
which would have refused a boundary on a later day of a longer trip.
The road-trip contracts follow the same limit: the day boundary and
corridor schemas, the set_day_boundary tool and the day window's runaway
guard read MAX_TRIP_DAYS instead of their own 365 or 366, and the
tooLong message carries the number as a placeholder in all 23 locales.
A visit's End could be edited and was shown in road trip mode, but the
schedule never read it: the drive always left a stop after its stay, so
a traveller who knew when they had to leave but not how long they would
stand there had no way to say so.
The End is now the time the drive leaves. RoadtripStop.leaveAt is
filled from the visit's End (or its place's) on the client by
useRoadtripRoutes and on the server by roadtrip-plan.service, so
calculate_roadtrip and get_roadtrip_context agree with the planner, and
computeSchedule takes it as the departure it already knew how to hold.
With Start and End both set the stay is the time between them. With
daily travel times on, the stay is the time until then, and an End
before the window start starts the day early the way a Start does.
Before the first fixed time the chain works backwards through an End as
it does through a pinned Start.
An End is read against the drive: the last passing of that clock time
counts as missed when the drive went past it on the road or earlier
the same day, anything else is the next one, so a drive across
midnight neither waits a whole day nor pretends to be on time. A drive
that gets there after the End leaves on arrival and files a
missedLeave finding, shown beside the late badge in the rail and on the
phone.
The rail's STAY badge, the inspector's Stay tile, the phone stop sheet
and the phone chain show the stay the End makes, with an "until 14:00"
hint. It is read off the schedule and never written to
duration_minutes: the stay belongs to the place, the End to one visit,
and a stored copy would go stale as soon as the arrival moved. For such
a stop the stay dialog, desktop and phone, shows that stay read only
and offers to remove the End, through the visit's time route with an
offline path (assignmentRepo.setTimes, setAssignmentTimes). While the
road trip is on, the End field in both place forms says the drive
leaves at that time.
The dead checkoutAt in the plan service is gone. New strings in all 23
locales.
**"Today" was read off UTC while the clock beside it is local.** `localDate.ts` exists for
exactly this and says so in its own comment: between local midnight and the UTC rollover the
two disagree, so east of Greenwich the up-next card hung on yesterday's stage for the first
hours of every night, which is when somebody on a long stage is actually driving. Both the
stage hook and the corridor hook now ask the same helper the rest of the app asks.
**A refused day end left the switch showing a state the trip never reached.** The writer
catches its own failures and shows a toast, so it never throws and the sheet's catch could
not fire, which left the optimistic value standing. It answers whether the day end moved
now, and the switch comes back when it did not. The desktop control ignores that answer, as
it reads its state back from the trip.
**A timed-out corridor search reported more unsearched boxes than it had.** Every worker
still alive ran the deadline branch and each added the whole remainder, so three workers
reported three times the boxes left, which can exceed the number of boxes there ever were.
The remainder is claimed before it is counted, which also ends the other workers' loops.
**A night that fell mid-leg had its morning drive dropped from the stage map.** The join
mark went on the first leg of every day opening on an automatic night, but only a night
standing ON a stop is a connection between two days. `dayWindow` records that difference as
a whole `position` against a fractional one, and where it is fractional the morning drive is
the last stretch to this day's own first stop. It was being filtered off the stage while its
distance stayed in the day's total.
The last change marked the drive into the next day and kept it off a phone stage, but it
only caught the connection `connectDays` builds through `inboundAt`. A trip with a day
window set never takes that path: the window switches `inboundAt` off and builds the days
itself, so day 2 opens on an automatic night standing where day 1 stopped and its first
leg is the drive on from there. That leg is the same connection, it just arrives as one
of day 2's own legs under day 2's number, and the stage drew the whole way back to
yesterday's last stop on today's map. The setting is not even involved: with a window it
happens either way.
So the leg out of an opening night is marked as a join as well, and the phone drops it
with the rest.
One exception, because the filter must not empty the screen: a day that is nothing but
driving, night to night with no stop of its own, has only the connection to show, and
there the line IS the day. When dropping the joins would leave a stage with no line at
all, it keeps them.
"Connect the days" is a whole-drive setting: with it on, the leg from one day's last
stop to the next day's first is drawn in the colour of the day it leaves, so the desktop
map reads as one continuous drive across every day. The phone has no such view by
default. It shows ONE day, and the stage filter went by day number alone, so that
connection came along: a line running off the day on screen to a place it never visits,
and on this trip the longest thing on the map by far.
The assembler now says which lines are connections (`lineJoins`, parallel to `lines` and
`lineDays`) rather than leaving each surface to guess, because the day number cannot tell
them apart: it reads the leaving day for both a day's own legs and for the drive out of
it. A stage drops them; the all-days view keeps them, which is the view the setting is
for and is one tap away on the same map, next to the day chips.
Routes read back from an older cache carry no marks, so nothing is known to be a join and
nothing is dropped: those days draw exactly as they did before.
With connected days the drive from one day's last stop to the next day's
first is drawn at the head of the arriving card, in the colour of the day
before. anchorFor only knew that stretch as a night spill, so a click on it
anchored to the arriving card's first stop: the via was stored on the leg
after that stop, and the next day drove out to the point, turned and came
back, while the stretch that was clicked kept its shape.
assembleRoadtrip now names the stop that joined drive leaves from, and
anchorFor treats it like the stop a night drive left from. Placing and
dragging both resolve to the previous day's last stop, which is the anchor
the seam request already reads its vias from.
A tester found the charging panel once a station was on the trip and asked
for it while still deciding. The availability and the published tariffs were
reachable only for a saved place: the route takes a places id, and a hit
found along the route has no row yet.
ChargingService.load already needed nothing but a coordinate and a name, so
it now has a public method that says so, behind a trip-scoped route with the
same guard stack, a Zod contract in shared, and the parallel MCP tool in this
same change. Same service, same caches, so a station looked at in the dialog
and then added keys the entry already fetched. Trip-scoped even though the
trip is not part of the question: without :tripId the trip access guard goes
too, and what is left is an open proxy in front of somebody else's public API.
It renders in the add dialog, and only while the selected kind is charging,
so a fuel or food stop costs nothing and one opened dialog is one lookup.
Deliberately not per result row: the corridor search publishes box by box,
every mounted panel owns a refresh timer, a cache miss can cost seven
upstream requests, and a station in a dense charging park is the case the
upstream matcher refuses as ambiguous.
Power and socket counts are not in this answer and were not asked of it.
They are OSM tag data and already ride along on the corridor row for free.
noUncheckedIndexedAccess makes every index access optional, DriveLimits wants
its two minute ceilings named even when they are null, and the distance unit is
metric/imperial rather than a unit label.
deriveDriveWarnings takes a two-slot "does this stop refuel" pair, slot 0 for
the stop a leg leaves and slot 1 for the stop it reaches, and the assembler
hard-coded slot 1 to false. The suppression the model was written and tested
for could therefore never fire through it: a day that reaches a charger 700 km
in reported running dry at the charger, and a run of warnings that should
collapse to one stayed at full length. Pinned by a spec of its own now, since
the wrapper had none.
The server's plan built each leg by spreading the whole run's segment and then
overriding only its metres and seconds, so every leg of a multi-stop run
carried the run's distance text, its duration text and its polyline midpoint:
leg one of a 480 km day reported 100 km with "480 km" printed beside it. The
four texts and the midpoint are per leg now, in the reader's own unit rather
than the router's hard-coded kilometres, which also ends a response that mixed
miles in its day labels with kilometres in its legs.
Book a night to check in at ten, pin a stop to three in the afternoon behind it,
and the rail said 13:46. True enough as arithmetic — with no hour of its own the
chain works the first stop backwards out of the first one that has one, so three
minus a fourteen minute drive minus an hour standing there — and nothing anybody
asked for.
A door that opens at ten, with nothing ahead of it deciding otherwise, IS the
hour: the day starts there and runs forwards. Reached later it stays what it was,
a floor, and a floor below the arrival changes nothing — which is what keeps a
hotel behind an afternoon stop at its real arrival instead of dragging it back to
its check-in.
Pinning an afternoon stop sorted that one and left the hotel it was booked around
sitting behind it. The day's auto-sort reads each stop's own hour, and a booked
night has none: nobody types a time into a hotel row, they type a check-in, and
that lives on the booking. It counts now, so the night moves with everything else
instead of staying where it was dropped.
The assignments e2e builds its own tables and had neither the accommodation
column nor day_accommodations, so every create in that file 500'd as soon as the
insert learned to carry a booking id. Both are there now.
And the shared lint, which is stricter than the other two and which I had not run:
three leftovers from taking the check-out out of the chain, none of them used any
more.
It is the hour a room becomes available. Reaching it later is reaching it; only a
time somebody pinned to a stop can be missed. Read as an anchor it did the exact
opposite of what it means: arrive at 16:14 at a hotel that checks in from 11:00
and the rail reported five hours fourteen late, then read that 11:00 as earlier
than the 15:00 before it, took it for a new day and pushed the hotel onto
tomorrow. The day it came from was left with one stop and dropped out of the list
altogether, so the stop before it appeared to vanish.
The schedule now takes it as a floor: waited for when the drive gets there first,
and otherwise silent. Waiting still prints in ink, because the hour is one
somebody named; arriving late to it prints as computed, because nothing was
decided.
Same shape of mistake as the check-out, from the other side. One says the earliest
you may be somewhere, the other the latest you may still be — neither says when
you drive.
With daily travel times set, a stay was spent only against the hours the window
is open. Twenty-four hours then took two of them to run down: a single night
ended on the day after next, and every stop behind it moved with it.
Night time is time. The hours between one window closing and the next opening now
count towards the stay, so a night that starts just after eight is over just after
eight the next morning and the day carries on from there. An eleven-hour visit
from eight is over at seven in the evening rather than saving its last hour for
tomorrow — the traveller is still there after the driving stops, they simply do
not drive on until the window opens again.
Three things the day boundary was swallowing, all from the same stay.
The stop after it stayed on yesterday. It is reached a full day later but at a
later reading on the clock, and the only rule watching for a new day was "the
clock went backwards" — which that never does. The schedule's own count knows,
as long as it is read as a difference: a day whose first arrivals came out
negative is lifted whole, and reading the lifted offsets raw once moved an early
stop to tomorrow while leaving the late one behind. It now crosses over properly,
marked as having set off the day before.
The morning after started at the exact minute the stay ended, as if the road to
the first stop took no time. The carried minute is a departure; the drive from
there is added to it.
And that drive was invisible. It was on the map and in the day's total, but the
rail jumped straight to a stop at 08:11 after a stay that ended at 08:00. It gets
the same band any other drive gets, above the stop it arrives at — except where a
stop crossed over, which already draws that road under its own block.
A check-out is the LATEST the room has to be handed back, never the earliest
anybody may drive on, so it says nothing the chain can use. It shaped four things
it had no business shaping: the departure the day was built from, the length the
rail printed for a booked night, a day-selector and a clock field in the stop
dialog, and the branch that sent a night's STAY badge to the booking form because
its length was not editable here. All four go. The booking keeps its check-out;
it is a booking detail and lives under Days, where it is entered.
What replaces it is the thing that was always the honest answer: how long you
stand there. That is the traveller's own statement rather than a deadline, and a
stay running past midnight now carries the clock into the morning after — a stop
pinned before it ends keeps its hour and collects the late finding, as it would
from any leg it cannot make. A stay spanning whole days walks the clock forward
over them. An ordinary evening hands on nothing: the night between two days is
not a wait somebody is serving out.
The stay dialog goes to a full day rather than four hours, since it is now the
only place a night is given its hours, and offers eight and twelve as presets.
Every stop answers the same question the same way.
Overlay panels lose the skeleton placeholder. It was drawn in the page flow, so
the first time each dialog was ever opened a pale block flashed under the planner
and never again once its chunk was cached.
This reverts commit d4677bdea, and keeps the departure chip's icon.
The premise was wrong. A check-out is the LATEST moment the room has to be
handed back, not the earliest one you may leave: whoever wants to set off at
seven checks out before that or leaves the bags at the desk. Reading it as a
floor turned a perfectly good plan into a finding, and no amount of wording
around that would have made it true.
The chip keeps the door-out mark. That part says what it always said, only in a
symbol that is not a second clock face beside the arrival.
Every day was scheduled from nothing, so a booking that runs into the next day
ended at midnight as far as the chain was concerned. Check in at 08:00 and out
at 08:00 the next day, and the rail was happy to put you somewhere else at 07:00
that morning, with no sign that the room was still yours.
The day a check-out falls in now starts at the check-out rather than at nothing.
A floor, never a move: a stop with a clock of its own keeps it and collects the
late finding any leg it cannot make in time would give it, so a plan that says
07:00 still says 07:00 and merely admits it does not add up. A stop with no clock
starts at the check-out instead of having no time at all.
Only that one day. A stay running Monday to Thursday leaves Tuesday and Wednesday
alone, because you sleep there, you do not sit there.
The departure chip takes the door-out mark instead of a clock face. Beside an
arrival time, a second clock read as a second arrival.
The rail prints an hour in ink when a person picked it and in grey when the chain
worked it out. Every scheduler anchors on `time ?? checkInTime`, so a booked night
starts its day on the check-in exactly the way a pinned stop does — but two of the
three then asked only about `time` when labelling the result, and printed that
chosen hour grey.
The answer lived in three places and two of them disagreed with the anchor they
had just used, so it is one function now. Nothing but the rail reads the flag.
Only visible since a booked night became a stop of its own; before that no stop
carried a check-in.
Dragging a via somewhere else on the map changed where the point IS and never where it
sits in the chain. The anchor that decides which two stops it is threaded between was
worked out once, when the point was dropped, and the drag sent only coordinates —
`roadtripViaUpdateRequestSchema` carried a lat and a lng and nothing else.
So a via pulled past the stop it used to precede kept claiming the earlier leg. The
router then drove out to it and turned back before carrying on, which on screen is not a
subtly wrong route: it is a spur that reads as the drag having done nothing at all, with
the original road still sitting there unchanged.
The anchor is not a property of the point but of where the point sits along the drive, so
it is recomputed on a drag the same way it is on a drop. The projection that did that was
inline in addRoadtripVia; it moves to a shared `anchorFor` and both callers use it.
A drag differs from a drop in two ways, both deliberate. It measures against its OWN day:
letting the nearest day win, which is right for a click on open map, would hand the via to
a neighbouring day whose road happens to pass closer and make it disappear from the day it
was dragged in. And it has no 2 km guard: pulling a via well off the current road is
exactly what the gesture is for, and the anchor only says which leg gets bent.
`after_order_index` is optional on the way in, so a drag inside one leg and an older
client still send just the coordinates, and the stored pin is then left alone rather
than cleared.
The schemas had no spec of their own. The two things worth pinning are the
ones the feature rests on: the anchor is a position and never an id, so a
fraction or a negative is not a leg of anything; and the batch route's
track has three states, because absent and null mean different things to a
day that already follows a line.
The vias already made the day follow the road; nothing said which road. After a
reload the badge could only say "shaped by hand", and re-fitting after the stops
changed meant finding the track again by eye.
One row per day, written in the same transaction as the chain it describes: a
day claiming to follow a road whose vias never landed would be worse than a day
claiming nothing. Three states rather than two, which is why the field is an
object and not an id — absent leaves whatever the day followed alone, null says
it follows nothing, an object records the road it now takes. A bare nullable id
could not tell "do not touch this" apart from "forget it", and a hand-dragged
via on the same day must not wipe the label.
The foreign key is the point: delete the imported track and the label cascades
with it, so no day is left claiming to follow a line nobody can see. The vias
stay, because they are the drive.
Both write paths check that the place is a track of this trip before it becomes
a label — permission alone would let another trip's place id, or an ordinary
place, become a name that can never be drawn.
Also clears three reliability findings the scanner raised on this branch: two
default sorts given an explicit comparator, and a trailing-slash regex the
scanner is right to call backtracking, walked instead.
The single-via route is right for a detour dragged by hand and wrong for
anything that derives its anchors from a line. Pinning a day onto a recorded
track or a signed scenic road means a couple of dozen points, and one request
each re-routes the entire trip once per point, spaced by the routing host's own
rate limit — twenty anchors would spend half a minute drawing routes nobody
asked to see.
replace_legs names the legs to clear rather than taking a flag for the day, and
that is the whole difference: a traveller who bent two legs by hand and then
adopts a scenic road for a third has not asked to lose the two.
The tools follow in the same change, as parity requires. Undoing a detour picks
this up too, where the vias of one leg were deleted one request at a time.