Round #126Score 3.5/5Transport: air

One overloaded component split in two, and Itinerary finally took shape

Route: /trip-itinerary
Desktop preview
Mobile preview

Captured at a 375px viewport. Long pages scroll inside the frame.

The screenshots stay in Japanese. Every round is a real screen for a real Japanese industry, so an English re-render would no longer be the screen the agent actually produced.

Write-up

One overloaded component split in two, and Itinerary finally took shape

⁠Build log series⁠: Each round hands an AI new to Gunjo (@gunjo/ui) the public npm package and the gunjo.jp docs, then asks for a real screen. ⁠Continuing the deeper passenger-facing air work from the passenger backlog, this was ⁠itinerary management⁠, presenting the outbound flight, the hotel, activities, and the return flight as a timeline grouped by day.

Result: 3.5/5, and the route component finally settled

The screen passed tsc and fit a 375px mobile-first layout. The agent reported:

PageHeader was built for this, with no friction.⁠ Its docstring spells out the top bar every consumer screen needs, at least 44px, and safe-area support for the notch. onBack, actions, and align="center" are all there. ⁠The one component that was genuinely born on the passenger side.⁠ ⁠But the centerpiece of the screen, a composite timeline grouped by day, has no dedicated component.⁠

PageHeader, which I built in , was found without prompting in #126⁠, one round later, by a different agent working in another area.

The core observation: four rounds of wandering before an itinerary component

Across the series the route and itinerary component ⁠wandered four times⁠:

  • Journey segments in , outbound and return legs in , and directions in and all reused RouteStops ⁠with its delivery vocabulary intact⁠, including "not delivered", "in transit", and planned against actual times.
  • Baggage tracking in was the first time RouteStops fit, for tracking a state.

Then the agent put the difference into words:

⁠The composite timeline grouped by day was hand-rolled, and it is the leading gap.⁠ RouteStops is pinned to delivery and does not suit a trip that has not happened yet; Timeline is too plain, with no item kinds, no day grouping, and no tap target. "⁠A TripItinerary or Itinerary component is needed: day groups, a kind per item mapping to an icon and a tone, a slot for rich content, and tap.⁠ It is the biggest single addition on the table."

Journey segments in , international legs in , and the itinerary here make ⁠three hand-rolled cases⁠. RouteStops tracks a state, as settled, and Itinerary carries a future trip or route. The two responsibilities split apart.⁠ I built it on the spot.

What I built: Itinerary

const days: ItineraryDay[] = [
  { label: "Day 1", sublabel: "Sat 27 June - Tokyo to Honolulu", items: [
    { time: "21:55 dep / 10:25 arr (local)", icon: <IconPlane/>, tone: "primary",
      title: "NH182 Haneda (HND) - Honolulu (HNL)", description: "Outbound - about 7h30m - seat 32K",
      trailing: <Badge variant="info">Outbound</Badge>, onSelect: () => openFlight() },
    { time: "Check-in 15:00", icon: <IconBuildingPavilion/>, tone: "success",
      title: "Hyatt Regency Waikiki", description: "3 nights - booking ref RZ8K4P", onSelect: () => openHotel() },
  ]},
]
<Itinerary days={days} />   // or flat: <Itinerary items={...} />
  • days[] with a day heading, a place subtitle, and items, or a flat items[]; items mix flights, hotels, activities, and legs; ⁠an icon and tone marker plus a connector per kind⁠; a slot for rich content; and onSelect giving a tap target of at least 44px.
  • ⁠This resolves issue #358⁠: a future trip or journey goes to Itinerary, while node-centered status tracking (deliveries, orders, repairs, bags) stays with RouteStops. I split the table entry in two as well, which stops itineraries being sent to RouteStops.
  • PR#375, verified on a real 375px render with two day sections, mixed kind markers, three tappable rows, and no overflow.

⁠What I built under src this round: Itinerary, on its third case and the tenth component to come out of this series.⁠

What I took away: when responsibilities split, both halves become components

RouteStops wandered four times because ⁠one component was carrying two jobs⁠: tracking a state, and describing a trip that has not happened yet. settled that tracking belongs to RouteStops, and this round settled that a future trip belongs to Itinerary. ⁠Depth pulls apart two roles that had been conflated and turns each into its own component.⁠ That is a third kind of outcome, different from filling a gap and different from settling a boundary: ⁠one ambiguous component became two clear ones.⁠ The map got more precise again.

📊 Component scoreboard (⁠10 built⁠)

AmountBreakdown / ActionQueue / ListCard / Gantt-segments / SeatMap / LoyaltySummaryCard / RadioCard / FilterChips / PageHeader / ⁠Itinerary⁠ In progress: TicketStub at two of three, SegmentedControl, a success variant on StatusScreen, an hour axis on Gantt, FlightSegment, and a bottom bar.

Next up: build log

  • The rest of the passenger backlog: air, with lounges and priority services, or the rest of rail, where in-station recommendations could give TicketStub its third case through a membership card or a coupon, or refunds. The deeper passenger work is nearly a full lap.

The run

The route component wandered for four rounds because it was doing two jobs. Splitting it produced two clear components instead of one vague one.

This series is co-created with AI (Claude and Codex). A human designs the experiments, makes the calls, and fact-checks before publishing; the AI does the hands-on work and drafting.

Components from @gunjo/ui

The components this screen's source imports directly.

The code the agent actually wrote

Click a file to expand its source.

業界別「AI指示書パック」、先行登録を受付中です。