SAKURAKO
A Tokyo Date Planner for What the Booking Sites Don't List
GetYourGuide and Klook are good at ticketed experiences and useless at 'there is a riverside path in Kagurazaka that is lovely at 4pm and costs nothing'. Sakurako covers both — parks, beaches, festivals, backstreets and free things alongside bookable activities — planned around the hours she is actually free, with an eight-year-old along.
Origin Story開発の背景
Built for one specific couple starting from Akebonobashi in Shinjuku-ku, which is why the constraints are concrete rather than configurable. Her week is painted by hand on a 7×16 grid from 07:00 to 23:00 with three states per hour: busy, free with her daughter, free just the two of you. Every free stretch becomes a plannable window with one click, and the window — start, end, travel each way, and the child — is part of the prompt rather than a filter applied afterwards.
How It's Builtつくり方
The aggregators' browse pages return 403 to automated fetches, verified rather than assumed, and there is no public GetYourGuide or Klook API without affiliate credentials — so search genuinely is the only route in. Their individual product pages are indexed, though, so the research reaches them with site: queries instead, and Japanese queries are part of the strategy rather than an afterthought: asoview, craftie and activityjapan carry local workshops that never surface in English results. Two keyless APIs fill in what search should not be asked for — Open-Meteo for the forecast, Nominatim for geocoding.
Engineering Notesエンジニアリングノート
The search belongs in Rust
Queries are fired at Exa concurrently, deduplicated by canonical URL and round-robined across queries so a trimmed set still covers every category. Only then does a single toolless instance read them. Removing the agent loop took a search from roughly 30% of a five-hour usage window to about $0.20, and from minutes to seconds.
Killing what you spawned
Each job spawns in its own process group via setsid, because a plain kill orphans the CLI's node children. Teardown is guaranteed twice over — both window-close and app-exit events call it — sending SIGTERM to the group, waiting 600ms, then SIGKILL. Closing the app never leaves a paid instance running.
Reaching what the aggregators block
GetYourGuide, Klook and Viator browse pages return 403 to automated fetches — verified, not assumed — and there is no public API without affiliate credentials. Their product pages are indexed though, so site: queries reach them, and Japanese queries surface asoview and activityjapan workshops that never appear in English results.
Distrusting the schema
The final balanced JSON object is extracted from the reply rather than the first — the ideas stream ahead of it, so taking the first would return an idea and lose the summary — and every field is coerced defensively, so a missing price becomes 'not confirmed' rather than a fabricated number.
The rewrite is the lesson here. The first version looked sophisticated — a dozen agents, each with its own search loop — and it was slower, twenty times more expensive, and no better. The work an agent loop does well is deciding what to look for; the looking itself is a job for ordinary deterministic code.
What It Does主な機能
Her Week, Painted by Hand
Three states per hour across a 7×16 grid. The plan is built for the window rather than trimmed to fit it afterwards.
Timed Itineraries
Shortlist a few ideas and a second instance turns them into a real schedule: travel legs, a food stop, buffer, a packing list and a rain backup.
Vouchers, Marked Honestly
It hunts current Klook / GetYourGuide / Voyagin / Asoview promo codes, combo tickets, transit day passes and child-free-entry rules. A code with no source URL it actually visited is shown as Unverified rather than quietly presented as fact.
Budget in Pounds
You think in GBP and the research is given yen. The rate is fetched live on launch from ECB reference data via Frankfurter and falls back to the last stored value offline. Prices show both.
Booking Links That Are Real
Direct ticket URL, official info page and a Google Maps link on every listing, opened in your actual browser — and any link the model did not get from the search results is stripped rather than shown.
Honest About Staleness
Prices, opening hours and especially voucher codes are read off live pages and go stale. Sakurako marks what it could not confirm instead of guessing. Closing days are the most common trap — many Tokyo museums shut on Mondays.
Where It Stands現在地
Sakurako builds to a DMG but is deliberately unlisted: it is unsigned, so Gatekeeper refuses it on first launch, and it needs both the Claude Code CLI and an Exa key to do anything at all. The rearchitecture that cut a search roughly tenfold is in and holding; signing it properly is a paid-developer-account decision rather than an engineering one.
Tauri 2 · React 19 · Vite · TypeScript · Tailwind 4. Searching happens in Rust, not in the model: queries are fired at Exa concurrently, deduplicated by canonical URL and round-robined across queries so a trimmed set still covers every category, then handed to one instance launched with no tools at all. That replaced a fan-out of about a dozen instances each running their own search loop — a structural cost rather than a model one, since every instance paid a measured ~21k tokens of harness overhead before doing anything and re-sent each fetched page on every later turn. Removing the agent loop took a search from roughly 30% of a five-hour usage window to about $0.20, and from minutes to seconds. Instance lifecycle is the careful part: each job spawns in its own process group via setsid so the CLI and its node children die together, progress is streamed by parsing the stream-json line protocol including partial deltas (with only one turn, nothing would appear until the whole reply landed), every instance runs under a hard budget ceiling where hitting the cap keeps what already streamed and reports the run as capped, teardown is guaranteed twice over from both window and app exit events with SIGTERM then SIGKILL, and a per-job watchdog kills anything past the timeout. The instance is confined to the web — search and fetch pre-approved, everything that reads disk, executes, delegates or schedules on an explicit deny list, cwd in a temp directory. Replies are never trusted to match the schema: the final balanced JSON object is extracted from the reply, and every field is coerced defensively so a missing price becomes 'not confirmed' rather than a fabricated number.