From 54f049eeccd6f4f02a692445cd9470c44da427ba Mon Sep 17 00:00:00 2001 From: Greg Pomerantz Date: Wed, 9 Sep 2026 16:43:47 -0400 Subject: [PATCH] Enforce the date-sync invariant in commit/restore, not just the flight tool MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit redateStaysAndPlaces() now runs in commit() (before the snapshot, so history stores consistent pairs) and in restore(). Any present or future edit that changes day dates keeps stays/places/booking strings in sync for free; consistent plans are an idempotent no-op. apply_flight_anchors no longer calls it directly — commit is the single enforcement point. enterTrip's load-time self-heal stays for plans persisted by older code. --- mock/app.js | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/mock/app.js b/mock/app.js index 870cbf5..6983713 100644 --- a/mock/app.js +++ b/mock/app.js @@ -413,12 +413,10 @@ const TOOLS = { } last.wakingHours = Math.max(1, Math.round((depMin - last.startMin) / 60)); - // 5) sync the date-carrying neighbours (stays / places / hotel bookings) - redateStaysAndPlaces(); - - // 6) commit (persisted immediately — this is the edit we must never lose) - // + re-render every date-bearing UI piece (renderAll alone only covers - // rail + map, which left tabs, chips and title showing old dates) + // 5) commit (persisted immediately — this is the edit we must never lose; + // commit() also re-syncs stays/places/booking dates to the new day + // dates) + re-render every date-bearing UI piece (renderAll alone only + // covers rail + map, which left tabs, chips and title showing old dates) ids.forEach(id => rebuildLegs(days[id])); commit('[flight] re-anchored around booked arrival + departure'); persistTrip(true); @@ -1659,6 +1657,11 @@ function recentHistory() { return out; } const commit = (label) => { + // invariant for every edit, not just flight re-anchoring: the date-carrying + // neighbours (stays, places, hotel booking strings) must track the day + // dates. Idempotent — a consistent plan is a no-op. Runs before the + // snapshot so history stores consistent pairs. + redateStaysAndPlaces(); history = history.slice(0, hIdx + 1); // drop any redo branch history.push({ v: ++version, label, at: Date.now(), days: deep(days), stays: deep(stays), bookings: deep(M.trip.bookings), title: M.trip.title }); hIdx = history.length - 1; @@ -1674,6 +1677,7 @@ function restore(idx, silent) { staySeq = stays.reduce((m, o) => Math.max(m, +String(o.id).replace(/\D/g, '') || 0), 1); if (s.bookings) M.trip.bookings = deep(s.bookings); if (s.title != null) M.trip.title = s.title; + redateStaysAndPlaces(); // old-era snapshots may hold inconsistent pairs closeDiscover(); closeL3(); rebuildLegs(day); // snapshots may predate route enrichment — re-upgrade legs renderHotelMks(); renderBaseChip();