cocottetech/@platform/codebase/@features/ai-copilot/docs/multi-leg-tour-planning.screen.md
natalie 1b719e1fd7 chore(bootstrap): initial V4 commit
Clean successor to V3 (forge: lilith/atlilith). Seeded from local Mac
working tree at ~/Code/@projects/@cocottetech/. node_modules and build
artifacts excluded via .gitignore.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-18 08:11:41 -07:00

12 KiB
Raw Blame History

multi-leg-tour-planning.screen

Multi-leg tour route comparison — the traveling-salesman optimizer's user-facing decision surface. Implements brief R §R6 (R6aR6j). Reached when Quinn says "plan me a tour across Berlin, Paris, Amsterdam in October" or via the touring drawer (R §R2a) → Calendar tab → Plan multi-leg tour. Voice register: working (decision surface) — strategist proposes, Quinn picks. Plain on jurisdiction / pre-lock conflict; hearth on the ambient optimizer-running cue.

Sibling to tour-leg-detail.screen.md (per-leg interior after approval) and hotel-scout.screen.md (per-city hotel scouting that runs in parallel after approval).

Layout (three-candidate route comparison card)

┌─────────────────────────────────────────────────┐
│ ◄ Touring                              ⋯ menu   │ 56pt — top bar
├─────────────────────────────────────────────────┤
│                                                 │
│  Multi-leg tour · October                       │   title + window
│  Three routes ready. All start & end SF.        │   working register
│                                                 │
│ ┌─────────────────────────────────────────────┐ │
│ │ ● #1 · recommended                          │ │   selected (rose accent)
│ │ Berlin Oct 38 → Paris Oct 912 →           │ │
│ │ Amsterdam Oct 1316                         │ │
│ │ 14 nights · €5,200 hotels + €1,840 transit  │ │
│ │ Projected €11,400 · net €4,360              │ │
│ │ ↑ €240 vs #2                                │ │   RevenueDelta chip
│ │ Anchors: TG Mixer · Fashion-Week tail ·     │ │   AnchorList
│ │          Pride afterglow                    │ │
│ │ [ See the full plan ↗ ]                     │ │
│ └─────────────────────────────────────────────┘ │
│                                                 │
│ ┌─────────────────────────────────────────────┐ │
│ │ ○ #2 · easier pace                          │ │
│ │ Amsterdam Oct 37 → Berlin Oct 813 →       │ │
│ │ Paris Oct 1417                             │ │
│ │ Net €4,120 · cheaper transit                │ │
│ │ ─ €240 vs #1 · TG Mixer missed              │ │   RevenueDelta + warn
│ └─────────────────────────────────────────────┘ │
│                                                 │
│ ┌─────────────────────────────────────────────┐ │
│ │ ○ #3 · two cities                           │ │
│ │ Berlin Oct 310 → Paris Oct 1117           │ │
│ │ (Amsterdam skipped)                         │ │
│ │ Net €3,890 · easiest pace                   │ │
│ └─────────────────────────────────────────────┘ │
│                                                 │
│  [ Re-run with constraints ]      [ Pick #1 ]   │
└─────────────────────────────────────────────────┘

Components

Component Notes
Top bar Back to touring drawer; ⋯ menu: save-as-draft / cancel / view-inputs.
Title block Window label ("October") + working-register subtitle.
RouteCard (×3) Variant of the leg card; selected variant has rose accent + filled radio; unselected uses outlined radio. Each shows city sequence, dates, totals, anchor highlights, RevenueDelta chip.
RevenueDelta chip Signed delta vs the top candidate (↑ €240 / ─ €240 / ↓ €470). Plain numerals — never "optimal." Hidden on #1.
AnchorList Compact event anchors per leg (one line max per city). Tap a route → expanded breakdown opens with full AnchorList.
WarningChip Two trigger sources: K §K4 jurisdiction conflict ("Vienna blocked — your rules") and R6-Q2 pre-lock conflict ("Adina locked — Berlin nights fixed"). Plain register; replaces the RevenueDelta chip slot when active.
Footer Re-run with constraints opens the constraints sheet; Pick #N commits the selected route and triggers R6e fan-out.

States

  1. Typical 3-route compare (default) — three RouteCards, top selected by default, RevenueDelta chips on #2 and #3.
  2. Loading optimizer (hearth — per R6i) — single ambient card: "Strategist is folding in the transit costs. Twenty seconds." Skeleton outlines for three cards beneath.
  3. Only 1 route fits constraints — one RouteCard at full width + soft banner: "Only one route fits your constraints. Loosen any to see alternates." Footer keeps Re-run with constraints.
  4. Re-run-with-constraints sheet open — bottom sheet covers ~60% of viewport; backdrop dims the RouteCards.
  5. Mid-tour reroute entry (from active-leg banner per R6f) — title changes to "Re-route from Berlin · 8 days left," #1 starts from current city; "cancel old legs" inline note explains audit threading per brief I §I4.
  6. Jurisdiction-conflict warning (per R6-Q3) — the route containing the blocked city is hard-filtered before display; a footnote chip under the surviving routes reads (plain): "Vienna dropped — your jurisdiction rules block it. See K §K4." Tap → settings.
  7. Pre-lock-conflict (per R6-Q2) — Quinn pre-locked a hotel in a city but the optimizer's preferred dates conflict. WarningChip on the affected RouteCard (plain): "Adina locked Oct 48 — Berlin nights pinned. Net recalculated." Tap → unlock affordance.
  8. Tie-break within 5% (per R6-Q1) — top two routes within 5% net both render with selectable radios + an explainer line: "These two are within 5% on net. The first earns more; the second is an easier pace. Your call." No #1 recommended label.
  9. Optimizer returned no routes — see Edge cases.
  10. VoiceOver — read order: title → state explainer (if any) → RouteCard #1 (full) → RouteCard #2 (full) → RouteCard #3 → footer actions. WarningChips read as live regions.
  11. Reduced motion / Dynamic Type XXL — RouteCards stack vertically (already default); RevenueDelta numeric only; no animated pulse on warnings.

Re-run constraints sheet

┌─────────────────────────────────────────────────┐
│ Re-run with constraints                    ✕   │
├─────────────────────────────────────────────────┤
│  Add city           [ + Lisbon          ]       │
│  Remove city        [ ✕ Paris           ]       │
│  Cap nights total   [ ≤ 10 ]                    │
│  Lock dates         [ Amsterdam Oct 1316 ]     │
│  Cap budget         [ ≤ €6,000 ]                │
│  Preference         [ prefer warmer cities ]    │
│                                                 │
│  [ Cancel ]              [ Re-run optimizer ]   │
└─────────────────────────────────────────────────┘

Per R6d: each constraint change re-runs the optimizer; new top-3 regenerate in <2s (search space ≤5 cities). Constraint chips persist as filter pills above the RouteCards on return; tap × to clear and re-run. Locked-hotel rows surface here too (read-only, with "unlock" affordance routing to the hotel-scout sibling).

In-the-wild copy (per R6i)

Discovery surface (working — ai-copilot opens the door):

Three cities in Europe cluster in October. Want strategist to draft a route across all three? Some of them, none of them, just the best one — your call.

Optimizer running (hearth — ambient cue while strategist composes):

Strategist is folding in the transit costs. Twenty seconds.

Comparison card landing (working — Quinn deciding):

Three routes ready. The first nets €4,360 across all three cities. The other two trade revenue for an easier pace. Tap to see the full plan.

Mid-tour re-route (plain — operational urgency):

Paris cancelled Oct 11. Re-route from Berlin with the remaining 8 days? Strategist has three alternates.

Tour-close synthesis (hearth — per brief Q §Q2, lands the morning after the final leg closes):

October closed at €4,820. Plan was BerlinParisAmsterdam, ran BerlinAmsterdam. Paris-cancelled wasn't your call. Amsterdam over-performed. Worth queueing again next October.

Gestures

  • Tap a RouteCard → selects (radio fills, accent moves); Pick #N label updates to match.
  • Tap "See the full plan ↗" on selected card → per-leg breakdown sheet (each city expands into a pre-filled R §R1b planning sheet).
  • Long-press a RouteCard → "Save as draft" / "Compare side-by-side" / "Why this route?" (strategist explanation).
  • Tap a WarningChip → routes to K settings (jurisdiction) or hotel-scout sibling (pre-lock).
  • Tap Re-run with constraints → sheet rises from bottom.
  • Tap Pick #N → confirms route; fires R6e fan-out (creates N tour_legs rows under one tour_id parent in one transaction; bookings-hotels scout fires per city in parallel; per-leg tour-leg-detail.screen becomes reachable).
  • Swipe-down on sheet → cancels constraint edits, restores prior routes.
  • Pull-to-refresh on root → re-runs optimizer with current constraints (cheap).
  • VoiceOver focus order matches the read order in States §10.

Edge cases

  • Quinn cancels mid-optimization — back-tap or ⋯ menu → cancel: strategist receives abort signal; partial routes discarded; surface returns to touring drawer. No audit row (no decision was made).
  • Optimizer can't find a valid route (all candidate sets violate hard constraints — jurisdiction + budget + dates collide) — single full-width card replaces the three RouteCards (plain): "No route fits these constraints. The hard blocks: {list}. Loosen one and re-run." Footer hides Pick; only Re-run with constraints remains.
  • Selected route's hotels all skip post-approval — after Quinn picks #1 and the fan-out fires, if every city's bookings-hotels scout returns dead-end (per hotel-scout.screen state 3), a follow-up approval card lands in chat (plain): "Berlin and Paris hotels: no acceptable matches. Re-scout with softer filters, swap cities, or cancel the tour?" The tour_id parent remains in planning state until at least one leg has a booked hotel.
  • Brief R §R6 — parent design (R6a inputs · R6b objective · R6c card · R6d constraints sheet · R6e fan-out · R6f mid-tour · R6g audit · R6h specialist routing · R6i copy · R6j open Qs).
  • specialist-strategist.contract.md — optimizer logic owner (per R6h; not bookings-hotels, which is per-leg operational).
  • Brief K §K4 — jurisdiction hard-filter source for state 6.
  • Brief T §T5 — tour ledger; multi-leg tours show as a single row threaded by tour_id.
  • tour-leg-detail.screen.md — sibling; opens per-leg after Quinn picks a route.
  • hotel-scout.screen.md — sibling; per-city scout fires in parallel after R6e fan-out and is also the unlock target for state 7 pre-lock conflicts.
  • Brief I §I4 — counter-action threading for mid-tour reroute (state 5).
  • Brief Q §Q2 — tour-close synthesis copy.