# PalaceTravel — Butler Manual This file tells AI butlers how to work with PalaceTravel trip data. Read this before writing any files. ## How Trip Data Is Organized Each trip is a directory under the user's palace: ``` /palaceappsdata/palacetravel// meta.json ← trip metadata (title, dates, travelers) weather.json ← weather forecasts per date pack.json ← packing list (string[]) pack-state.json ← runtime checkbox state (mutable) places/ .json ← one file per research task meals/ .json ← one file per meal (budget, preferences, candidates) days/ day-1.json ← itinerary for day 1 day-2.json ← itinerary for day 2 ... ``` ## Rules 1. **One file, one writer.** Each file in `places/` is owned by exactly one butler or sub-agent at a time. Never write to another writer's file. 2. **No IDs.** Places have names, not IDs. The app generates slugs from names at read time. 3. **No cross-file references.** Don't reference data from other files by ID or index. Day stops reference places by `name` only — the app joins them at render time. 4. **Append-safe.** Adding a new place to your file doesn't affect any other file. The app reads all `places/*.json` and merges. 5. **Category is for rendering.** Each Place has a `category` field the app uses for color/icon. This is orthogonal to which file the place lives in. ## Recommended Sub-Agents When planning a trip, the butler should spawn these sub-agents in parallel. Each writes to its own file. ### Phase 1 — Research (all parallel) | Sub-Agent | Writes to | Category | What to find | |-----------|-----------|----------|--------------| | Dining | `meals/.json` | `food` | One file per meal. Each meal has budget, preferences, and candidate restaurants. See Meal Schema below. | | Fuel | `places/fuel.json` | `fuel` | Gas stations with prices, locations, hours | | Trails | `places/trails.json` | `trail` | Hiking trails, viewpoints, nature walks | | Landmarks | `places/landmarks.json` | `landmark` | Points of interest, scenic stops, historical sites | | Rest stops | `places/rest-stops.json` | `rest-stop` | Bathrooms, rest areas, convenience stops | | Safety | `places/safety.json` | `hospital` | Hospitals, emergency contacts, cell coverage, crash protocol | | Lodging | `places/lodging.json` | `lodging` | Hotels, motels, campgrounds | | Weather | `weather.json` | — | Forecast per date | | Packing | `pack.json` | — | Recommended items to bring | | Routes | (writes to day files) | — | Driving directions, waypoints, distances | ### Phase 2 — Day Planning (after Phase 1 completes) | Sub-Agent | Writes to | What to do | |-----------|-----------|------------| | Day 1 | `days/day-1.json` | Build itinerary from Phase 1 places. Include stops (by name) and drives (with route waypoints). | | Day 2 | `days/day-2.json` | Same as above for day 2. | | Day N | `days/day-N.json` | One sub-agent per day. | Phase 2 sub-agents READ from `places/*.json` but only WRITE to their own `days/day-N.json`. ## Place Schema Every file in `places/` (except `safety.json`) is a JSON array of Place objects: ```json [ { "name": "In Bloom", "lat": 35.6273, "lng": -120.6918, "category": "food", "phone": "(805) 296-3556", "address": "1240 Park St, Paso Robles, CA", "hours": "Wed–Sun 5–9 PM", "website": "https://inbloompr.com", "cost": "$180", "description": "New American tasting menu in a converted greenhouse", "details": { "cuisine": "New American", "price": "$$$$", "region": "Paso Robles" }, "tags": ["reservation-needed", "birthday-dinner"], "recommendations": [ { "item": "Tasting Menu", "price": "$180", "note": "5 courses, wine pairing available" }, { "item": "Wagyu Tartare", "price": "$28" } ] } ] ``` Required fields: `name`, `lat`, `lng`, `category`. All other fields are optional. Add whatever is relevant. The `recommendations` array holds menu picks with real prices. Each entry has `item` (dish name), `price` (as displayed on menu), and optional `note` (brief context). Use real current menu data — no guessing prices. The `details` object is freeform — use it for category-specific metadata (cuisine for food, difficulty for trails, brand for fuel, etc.). ## Meal Schema Each file in `meals/` is a single Meal object (not an array): ```json { "label": "Day 1 — Before Carrizo", "sub": "Breakfast in Paso Robles", "day": 1, "budget": 50, "preferences": "Local only, no franchises", "notes": "Arriving ~7 AM. Need early-opening spots.", "selected": ["Joe's Place"], "candidates": [ { "name": "Joe's Place", "lat": 35.6176, "lng": -120.6909, "category": "food", "address": "305 Spring St, Paso Robles, CA", "hours": "Wed 6 AM–2 PM", "cost": "$8–15", "description": "Local institution since 1995.", "details": { "cuisine": "American / Mexican Breakfast", "region": "Paso Robles" }, "tags": ["sit-down", "early"] } ] } ``` Required fields: `label`, `day`, `candidates`. - `budget` — per-person cap in dollars. Can differ per meal. - `preferences` — freeform text the butler uses when researching candidates. - `notes` — timing/logistics context. - `selected` — array of candidate names the user has chosen. Empty array = no selection yet. - `candidates` — array of Place objects (same schema as `places/`). File naming: use `-.json` sorted by meal order (e.g., `1-before-carrizo.json`, `2-after-carrizo.json`). ## Safety Schema `places/safety.json` has a different structure: ```json { "hospitals": [ Place objects with category "hospital" ], "contacts": [{ "name": "911", "phone": "911", "notes": "Emergency" }], "cellCoverage": [{ "segment": "Cupertino → Salinas", "status": "full" }], "crashProtocol": ["Stay with your car.", "Hazards on, hood up.", ...], "checklist": ["Share itinerary with someone", "Download offline maps", ...] } ``` ## Day Schema Each `days/day-N.json`: ```json { "date": "2026-03-18", "label": "Day 1 — Carrizo Plain", "summary": "Drive to Carrizo Plain for wildflower viewing", "stops": [ { "name": "Soda Lake Overlook", "arrival": "7:30 AM", "departure": "9:00 AM", "purpose": "Wildflower viewing" } ], "drives": [ { "from": "Cupertino", "to": "Carrizo Plain", "distance": "250 mi", "duration": "4h", "route": [[37.3230, -122.0322], [36.6745, -121.6420], ...], "costs": [{ "item": "Gas at Fastrip", "amount": "$35" }] } ] } ``` Stops reference places by `name`. The app matches stop names to Place objects for rendering details. If a stop references a place that doesn't been written yet, the app renders just the name and times — it degrades gracefully. ## File Naming File names in `places/` are chosen by whoever creates them. The app reads every `*.json` in the directory — file names don't matter for rendering. Name files by the research task they represent, not by abstract category: - `dining.json` not `food.json` (the task is "find dining options") - `fuel.json` not `gas-stations.json` - If the scope is unusual (e.g., "find Korean restaurants specifically"), name it `korean-restaurants.json` ## Actuals Schema Journal entries are stored one-file-per-entry under `actuals/day-N/`: ``` actuals/ day-1/ meta.json ← { "label": "Day 1 — Carrizo Plain" } entry-001.json entry-002.json ... day-2/ meta.json ← { "label": "Day 2 — Big Sur" } entry-001.json ... ``` Each `entry-NNN.json`: ```json { "time": "2026-03-18T04:30:00", "kind": "food", "title": "Breakfast at Home", "body": "Quick breakfast before the road.", "items": ["Banana", "Milk", "Milk Bread", "Cream Cheese"], "rating": 4, "lat": 37.323, "lng": -122.032, "placeName": "Home — Cupertino", "tags": ["breakfast", "home"] } ``` | Field | Required | Description | |-------|----------|-------------| | `time` | yes | ISO-8601 datetime (no timezone — local to trip) | | `kind` | yes | `"food"` \| `"stop"` \| `"note"` \| `"departure"` \| `"arrival"` | | `title` | yes | Short label (≤60 chars) | | `body` | no | Impressions, notes, details — freeform narrative | | `items` | no | Food items eaten, highlights of a stop | | `rating` | no | 1–10 | | `lat`, `lng` | no | Coordinates | | `placeName` | no | Links to a Place by name | | `photo` | no | Photo filename or URL | | `tags` | no | Freeform tags | **Why one file per entry:** Each entry is ~500 bytes (~125 tokens). When adding or editing a single note, Ace reads only that one file — not the whole day, not the whole trip. A 26-entry day costs ~3,250 tokens to read all entries; a single edit costs ~125 tokens. Real-world trip notes are freeform narrative ("the drive was windy, single-lane, got stuck behind slow cars twice for 10+ min each"), not structured fields — always read the entry, rewrite it naturally with new information added. **Butler workflow:** After each stop or meal, ask the traveler what they thought. Record a new entry with their impressions. To add an entry, POST to `/api/travel/actuals?tripId=` with body `{ dayIndex: 0, entry: { ... } }`. The server writes `entry-NNN.json` automatically (sequential numbering). To edit an existing entry, read the file, update it, and write it back directly. **Day directories:** Create `actuals/day-N/meta.json` with `{ "label": "Day N — Description" }` before writing entries. Days are 1-indexed to match `days/day-1.json` etc. ## Adding a New Trip Create a new directory under `palaceappsdata/palacetravel/` with a descriptive ID: ``` palaceappsdata/palacetravel/summer-road-trip-2026/ meta.json weather.json pack.json places/ meals/ days/ ``` Fill `meta.json` first, then spawn Phase 1 sub-agents in parallel.