The recommendation problem, in travel form
In Part 1 I turned a decade of daily notes and tens of thousands of photos into a structured trip database — trip, day, activity, place — without writing a single new trip report. That solved the backward problem: what have we done.
This part is the forward one: where should we go next. And travel planners get this consistently, almost comically, wrong. Ask a typical "AI trip planner" for a Greek island and it suggests Santorini — the single most overcrowded island in the Aegean, the exact thing I wrote "avoid" next to in my own notes. It matched on Greek island. It had no idea what I'd refuse.
That's the same failure I've written about before in a different domain: personalization is built on one verb, like, and has no representation for never. A planner worth trusting needs three inputs, not one — what you love, what you'd never touch, and what you've already done to death. I had all three sitting in structured form. So I wired them together.
Three signals, all already on disk
What I love lives in a preferences file I'd maintained for other reasons — scored destination types and countries. Islands 0.95. National parks 0.95. Cities with water 0.95. Cultural cities 0.9. Not vibes — numbers.
What I'd never touch was hiding in the notes field of that same file: "avoid overcrowded islands like Santorini/Mykonos." A loader mines those "avoid…" cues and stores Santorini and Mykonos as explicit negative preferences — not low scores, vetoes.
What I've already done I don't have to declare at all — it's derivable. Part 1's trip database already knows every country and city I've set foot in. A second pass aggregates it: France 18 times, Italy 16, the United States 15, across 29 countries and 642 places. Home (Netherlands, Rotterdam, Amsterdam) is filtered out — it's not a "destination." Everything visited is marked revisit-OK, because having been somewhere shouldn't ban it; it should just stop the planner from pitching Sicily to me as a thrilling discovery.
Three tables. None of them written from scratch for the planner — two already existed, one is derived from data I built in Part 1. That reuse is not a coincidence. It's what a real backbone buys you.
Affinity: a score, then a veto
For any candidate destination, the planner computes an affinity score. Matching positive preferences lift it. Priority lifts it — a bucket-list flag counts more than a passing "medium." Already-visited nudges it down a little (revisit, not new). And an anti-preference doesn't nudge — it slams the score down, because it's a veto wearing the costume of a weight.
Run it across my wishlist and the ranking comes out exactly right:
1.00 New Zealand (bucket_list)
0.89 Milos (Islands, high) ⚠ avoid: Santorini; Mykonos
0.89 Naxos (Islands, high) ⚠ avoid: Santorini; Mykonos
0.89 Folegandros(Islands, high) ⚠ avoid: Santorini; Mykonos
0.75 Taiwan (country, medium)
New Zealand tops it — bucket-list, national parks, the whole profile lights up. The non-touristy Greek islands cluster just behind, each carrying the Santorini/Mykonos caveat as a visible flag, not a buried assumption. The planner is reasoning with my actual taste, including the half most systems can't represent.
Then — and only then — the model
Here's where AI finally earns its place, after all the structure has done its work. For the top candidate, the planner assembles a facts-only brief: the destination, why it's on my list, the avoid-notes, my matching preferences, the actual travel books on my shelf (Kinfolk's Slower Ways to See the World, Lonely Planet's hiking guide), and a novelty check against where I've been. Then a writer model turns that brief into a pitch and a loose day-by-day outline — in my voice, under a grounding contract that forbids inventing specific venues, prices, or opening hours. Suggestions are framed as suggestions. The avoid-list is honoured.
The model writes the prose. It does not choose the destination, invent the preferences, or decide what I've already seen. By the time it's invoked, every judgement that needs to be mine has already been made — deterministically, from my own data. The model is the last mile, not the engine.
That ordering is the entire trick. Most "AI planners" lead with the model and bolt facts on afterward, which is why they hallucinate a hotel and pitch you Santorini. Lead with the structure — scored preferences, mined vetoes, derived exclusions — and the model inherits good judgement instead of having to fake it.
Why the same backbone serves three masters
Step back and look at what one DuckDB file now does. The visited side exports curated trips to my sister's site. The same trip records derive the exclusions for the wishlist side. The same scored preferences that rank the wishlist could just as well curate a "places we haven't been but want to" page on my own site. One model, one voice, three consumers: the exchange, the planner, and the eventual site.
I didn't plan three products. I built one honest structure and the products fell out of it. That's the quiet compounding return of getting the shape right: you stop building features and start deriving them.

The line, again
The model in this story writes maybe a hundred words of itinerary. Everything that makes those hundred words trustworthy happens before it runs — in the scored preferences, the mined anti-preferences, the exclusions derived from a decade of trips. The magic isn't in the model. It's in the structure you give it.
A travel planner that knows where you've already been, and what you'd never choose, isn't a smarter model. It's the same model, finally handed the structure it needed to stop guessing. Your trips were always structured data. Read forward, they don't just tell you where you went. They tell you where to go.
Part 1 turned the past into a queryable backbone. Part 2 pointed it at the future. The constant across both: nothing here was magic. It was structure — and structure is the part you can actually build.
