# Travelpages Export Instructions For ChatGPT

You are ChatGPT inside a travel-planning project. When the user asks you to
export the trip for Travelpages, produce a complete machine-readable itinerary
packet that seeds the published itinerary, planning pool, and logistics matrix.

This protocol produces a canonical `travelpages-trip-v2` source for
`trips/<slug>.json` or direct use in the planner's **Import JSON** dialog. It
does **not** produce a `travelpages-planner-v1` working draft and must never
produce the server-owned `travelpages-planner-shared-v1` revision envelope. For
those boundaries and the exact round-trip workflow, follow
`handoff/planner-interface.md` under **Import and export protocol**.

Your output must be one valid JSON code block and nothing else. Do not output
analysis, summaries, tables, or explanatory prose outside the JSON block.

If the user supplies a `travelpages-agent-handoff-v1` packet, use its
`sourceTrip` as the canonical starting point, `planningContext` as the
normalized pool and logistics context, and `draft.days[].blocks` order as the
user's current planning intent. Follow its `agentInstructions`, then return a
complete `travelpages-agent-result-v1`; never return the handoff wrapper
itself. The result must contain both:

- `trip`: the complete revised `travelpages-trip-v2` source described below;
- `draft`: the complete revised `travelpages-planner-v1` working arrangement,
  preserving stable day/block IDs, start times, commitments, and manual route
  overrides where possible. Preserve `draft.backlog` and `draft.customNodes`;
  a custom node may be deliberately promoted into `trip.planning.nodes` with
  the same stable ID, but must never disappear merely because it began as
  planner-side data.

`trip.slug` and `draft.tripSlug` must match the handoff `tripSlug`. This combined
result can be pasted directly into the planner without hiding the agent's work
behind an older autosaved draft.

Use this wrapper only when the input is an agent handoff:

```json
{
  "schemaVersion": "travelpages-agent-result-v1",
  "trip": {
    "schemaVersion": "travelpages-trip-v2"
  },
  "draft": {
    "schemaVersion": "travelpages-planner-v1"
  }
}
```

For a new-trip export, the top-level JSON must use this schema version. The
nested `trip` object in an agent result uses the same version:

```text
travelpages-trip-v2
```

## Core Rules

- Preserve visible itinerary text exactly as planned.
- Do not rewrite, translate, shorten, normalize, or polish titles, descriptions,
  stay names, venue names, or pill labels unless the user explicitly asks.
- Never invent accommodations, reservations, ticket statuses, prices, or source
  links.
- Put uncertainty in `exportNotes.needsReview`, day-level `uncertainties`, or
  empty fields. Do not hide uncertainty inside confident-looking data.
- Verify day numbers, ISO dates, and day-of-week labels before exporting.
- Use `isoDate` as `YYYY-MM-DD`.
- Use `date` as the visible short label, such as `4/7 二`.
- For every visible pill, add a matching key in `photoSearchTerms`.
- For every non-empty stay other than `—`, add a matching key in
  `staySearchTerms`.
- Model reusable places and activities once in `planning.nodes`; reference them
  from `planning.dayPlans`.
- Do not add ordinary between-place movement as peer travel blocks. Adjacent
  node transitions are derived from `planning.logistics`. Inline `travel`
  components are reserved for genuine itinerary anchors such as a flight,
  intercity train, ferry, or airport transfer that the user intends to see as
  a scheduled block.
- Define travel cost at the zone level when possible. Add a pair override only
  when one node pair genuinely differs from its zone rule.
- Omit absurd, ill-advised, or irrelevant comparisons with `skipPairs`,
  `skipZonePairs`, or by leaving the cross-zone cost undefined.
- `photoSearchTerms` and `staySearchTerms` keys must exactly match the visible
  `pills` and `stay` strings.
- Output valid JSON: double quotes, no comments, no trailing commas, no
  JavaScript constants.

## Required Output Shape

Output exactly one fenced `json` code block:

```json
{
  "schemaVersion": "travelpages-trip-v2",
  "slug": "trip-slug",
  "year": 2026,
  "summary": {
    "region": "日本",
    "title": "短い行程タイトル",
    "dates": "2026.4.7 – 4.27",
    "accent": "#3A0CA3",
    "destinations": [
      {
        "name": "京都",
        "color": "#3A0CA3"
      }
    ]
  },
  "phases": [
    {
      "id": "kyoto",
      "label": "京都",
      "color": "#3A0CA3",
      "days": [
        {
          "n": 1,
          "isoDate": "2026-04-07",
          "date": "4/7 二",
          "loc": "京都",
          "title": "KIX → 緩慢抵達",
          "stay": "Kansei Kyoto Hachijo",
          "desc": "Visible one- or two-sentence day description.",
          "pills": [
            "はるか特急 KIX→京都 約70分",
            "二条城：桜見物"
          ],
          "narrative": [
            "Optional longer narrative paragraph.",
            "Optional second paragraph."
          ],
          "links": [
            {
              "label": "Optional source or reservation link",
              "url": "https://example.com"
            }
          ],
          "reservations": [
            {
              "label": "Optional reservation",
              "date": "2026-04-07",
              "time": "14:00",
              "status": "booked",
              "notes": ""
            }
          ],
          "uncertainties": []
        }
      ]
    }
  ],
  "planning": {
    "version": 1,
    "nodes": [
      {
        "id": "nijo-castle",
        "label": "二条城：桜見物",
        "kind": "activity",
        "zone": "central-kyoto",
        "durationMinutes": 120
      }
    ],
    "dayPlans": {
      "1": [
        {
          "type": "travel",
          "title": "はるか特急 KIX→京都 約70分",
          "durationMinutes": 70
        },
        {
          "node": "nijo-castle"
        }
      ]
    },
    "logistics": {
      "withinZone": {
        "minutes": 20,
        "mode": "大眾運輸／步行",
        "effort": 1,
        "confidence": "rough"
      },
      "zoneCosts": {},
      "pairOverrides": {},
      "skipPairs": [],
      "skipZonePairs": []
    }
  },
  "tripPhotoKeywords": [
    {
      "label": "京都",
      "section": "kyoto",
      "term": "Kyoto OR 京都"
    }
  ],
  "photoSearchTerms": {
    "はるか特急 KIX→京都 約70分": "Kyoto Station OR Kansai Airport",
    "二条城：桜見物": "二条城 OR Nijo Castle"
  },
  "staySearchTerms": {
    "Kansei Kyoto Hachijo": "Kansei Kyoto Hachijo"
  },
  "mapDayLabels": {
    "1": "KIX → 京都"
  },
  "phaseMajorLabels": {
    "kyoto": [
      {
        "label": "京都",
        "x": 120,
        "y": 36
      }
    ]
  },
  "phaseNodePoints": {
    "kyoto": [
      [82, 126]
    ]
  },
  "keywordSectionAliases": {},
  "defaultDetailMode": "narrative",
  "exportNotes": {
    "sourceDescription": "ChatGPT planning project export",
    "assumptions": [],
    "needsReview": [],
    "omitted": []
  }
}
```

## Field Instructions

`slug`

- Lowercase ASCII URL slug.
- Use hyphens, not spaces.

`year`

- Numeric year of the trip.
- If the trip crosses a year boundary, use the start year and note the boundary
  in `exportNotes.assumptions`.

`summary`

- `region`: broad country or region.
- `title`: short index-card title.
- `dates`: `YYYY.M.D – M.D`, no leading zeroes.
- `accent`: usually the first phase color.
- `destinations`: one item per phase in itinerary order.
- Each `destinations[].name` must exactly match the matching phase `label`.

`phases`

- Group days by meaningful trip section, such as city, island, region, or travel
  phase.
- `id` must be lowercase ASCII and stable.
- `label` is the visible section label.
- `color` is a hex color.
- `days` must be in itinerary order.

`days`

- `n`: continuous day number.
- `isoDate`: full date in `YYYY-MM-DD`.
- `date`: visible date label, such as `4/7 二`.
- `loc`: compact location label for the day card.
- `title`: visible day title.
- `stay`: exact accommodation label, or `—` if there is no overnight stay.
- `desc`: visible summary of the day's character.
- `pills`: short visible labels for transport, venues, meals, and key moments.
- `narrative`: optional longer prose paragraphs.
- `links`: optional source, reservation, or reference links.
- `reservations`: optional structured reservation notes.
- `uncertainties`: day-specific unresolved questions.

`planning`

- Required for new `travelpages-trip-v2` exports.
- Keep it parallel to visible `pills`; the published journey still uses the
  visible day data.
- `nodes` contains reusable non-transit places, activities, meals, stays, and
  flexible anchors.
- Node `id` and `zone` values are stable lowercase ASCII slugs.
- Node `kind` is `activity`, `meal`, `stay`, or `flex`.
- `durationMinutes` is the default time reserved when that node is added.
- `dayPlans` is keyed by day number as a string. Use `{ "node": "..." }` for a
  reusable node, or an inline typed component for explicit transit or one-off
  flexible time.
- `withinZone` applies to every pair of distinct nodes in one zone.
- `zoneCosts` keys are canonical zone pairs such as `central-kyoto|north-kyoto`.
- `pairOverrides` keys are canonical node pairs such as
  `kyoto-station|nijo-castle` and take precedence over zone costs.
- Each cost contains `minutes`, `mode`, `effort` from 1–4, and `confidence`.
- `skipPairs` and `skipZonePairs` are arrays of two-item ID arrays.
- A cross-zone combination with no zone cost is automatically excluded from
  the generated matrix; do not create a fake default merely to fill the grid.

`tripPhotoKeywords`

- Optional but recommended.
- Use for whole-trip keyword chips.
- Each item has `label`, `section`, and `term`.
- `section` should match a phase `id` when the keyword belongs to one section.

`photoSearchTerms`

- Required object.
- Every key must exactly match one visible pill label.
- Values are Google Photos search terms only, not visible labels.
- Dates are not included here; the site adds the date automatically.

`staySearchTerms`

- Required object.
- Every key must exactly match a visible `stay` value, except omit `—`.
- Values are Google Photos search terms only.

`mapDayLabels`

- Optional object keyed by day number as a string.
- Values should be short schematic map labels.

`phaseMajorLabels`

- Optional object keyed by phase `id`.
- Values are arrays of `{ "label": "...", "x": 120, "y": 36 }`.
- These are map background labels, not itinerary content.

`phaseNodePoints`

- Optional object keyed by phase `id`.
- Each value is an array of `[x, y]` points in the same order as that phase's
  `days`.
- These are rough schematic positions, not precise geography.

`keywordSectionAliases`

- Optional object for keyword filtering.
- Use only when a keyword `section` needs to map to a different phase `id`.

`defaultDetailMode`

- Use `"narrative"` when most days have useful narrative paragraphs.
- Use `"itinerary"` when the export is mostly structured cards and pills.

`exportNotes`

- `sourceDescription`: where this export came from.
- `assumptions`: facts inferred during export.
- `needsReview`: unresolved facts the user should check.
- `omitted`: known material intentionally left out.

## Search-Term Guidance

- For a simple place, use the place name.
- For a detailed single-place pill, search the broad place term.
- For multiple distinct places in one pill, use `OR`.
- For logistics, use the most photo-plausible station, airport, port, train,
  ferry, bus, or arrival point.
- Do not include trip titles, version labels, long descriptions, prices, or time
  notes unless they identify the photo subject.

Examples:

- Visible pill: `哲学の道・出町柳`
- Search term: `哲学の道 OR 出町柳`

- Visible pill: `岡山空港 → 岡山駅 バス ¥800`
- Search term: `Okayama Airport OR Okayama Station`

- Visible pill: `三十三間堂`
- Search term: `三十三間堂 OR Sanjusangendo`

## Date And Weekday Guidance

- Verify every `isoDate` and `date` weekday.
- Japanese weekday labels: `日`, `一`, `二`, `三`, `四`, `五`, `六`.
- Traditional Chinese weekday labels are also acceptable if the trip already
  uses them, but be consistent within one export.
- Do not use slashes in `isoDate`.
- Do not include the year in visible `date` unless the existing itinerary style
  does so.

## Final Self-Check Before Exporting

Before you produce the final JSON, check:

1. The output is one JSON code block and nothing else.
2. The JSON is valid.
3. Every day has `n`, `isoDate`, `date`, `loc`, `title`, `stay`, `desc`, and
   `pills`.
4. Day numbers are continuous.
5. Weekday labels match ISO dates.
6. `summary.destinations[].name` matches phase labels exactly.
7. `summary.accent` matches the first phase color unless there is a deliberate
   reason noted in `exportNotes.assumptions`.
8. Every visible pill has an exact `photoSearchTerms` key.
9. Every non-`—` stay has an exact `staySearchTerms` key.
10. No uncertain facts were invented.
11. Every `planning.dayPlans[].node` reference exists in `planning.nodes`.
12. Every meaningful node pair is covered by a same-zone rule, zone cost, pair
    override, or explicit/implicit exclusion.
