How it's built · an agentic project · 2026

A scoring engine, built and run by an AI agent

This page is the how-to: the method, and the exact prompt to build a tool like this yourself, for any decision you care about. What this particular tool computes today, and where its data comes from, is on About.

01Tools & mode

Built in Cowork, by design. The Input Card, the scoring engine, and every output section run as plain JavaScript in your browser. Two things leave it, and both are stated on screen where they happen: a fare search sends the route, dates, cabin, and traveler count to the site's own /api/fares function, and watching a trip stores that route server-side so the scheduled re-check has something to check. Your wallet stays put. Cards and balances never leave the browser.

Chat
decide
  • the weighting model behind all 7 priorities, and the reference-range mileage values
  • what "genuinely strong" means for a redemption
Cowork
build & maintain
  • the scoring engine (app.js)
  • every static page, end to end
  • Netlify deploy
Your browser
run the engine
  • reads your Input Card
  • computes scores locally
  • wallet stays local
Backend (live)
fetch & watch
  • real Google Flights fares
  • watched trips re-checked every other day
  • a weekly Concert Radar sweep
  • award space: still the gap

No terminal in this stack. Cowork builds it; your browser runs it.

02Fares are real or absent, never invented

Fares have exactly one mode: real. Every run pulls live Google Flights prices through the site's own backend, merges in anything you scanned yourself from Kayak or Booking (each one carries a labeled source pill), and scores the lot with the engine below. When nothing real comes back, the site says so and shows no figures at all. The old synthetic fallback model was deleted on 2026-07-26, on the principle that a plausible invented number is worse than an honest empty state.

A few other numbers are estimates, and they say so on screen. Flight times for offers that arrive without a duration get computed from great-circle geometry against a hand-maintained city coordinates table. Mileage figures are distance-scaled planning ranges, because award availability isn't searched yet. Upgrade friendliness is the softest of them: a per-carrier reputation judgment, nowhere near a live seat map.

The Points Intelligence panel carries a different kind of number again. Point valuations (which airline's miles are worth the most, in cents per point) and active transfer bonuses are published figures from The Points Guy's monthly valuations, hand-copied with their date shown on screen and refreshed by hand each month. They're facts with a timestamp. Not a model output, and not a live feed either, which is why that as-of date always sits right next to them.

03Seven dimensions, weighted by your ranking, never a single variable

Every candidate itinerary is scored 0 to 100 on Cost, Comfort, Upgrade Potential, Award Value, Flexibility, Risk, and Time. Your ranked priority list, from Cheapest to Best Overall Experience, maps onto those seven dimensions through a fixed weighting table: a rank-1 priority carries more weight than a rank-7 one. The final score is a weighted sum, shown on the results page as an explicit percentage per dimension, so the reasoning behind the recommendation is never reduced to a single number. Time was added 2026-07-25, after Fastest and Cheapest returned the same pick on a route where every offer had one stop: Risk only scored stop count, so there was nothing left for "fastest" to actually rank by.

Always shown

The exact weight (as a %) each of the seven dimensions received, computed live from your priority ranking, not a fixed default.

Never done

Optimizing for price alone. A cheaper itinerary that scores meaningfully worse on your higher-ranked priorities will not out-score the better-rounded one.

The actual math, in full: each of the 7 priority lenses (Cheapest, Best Value, Fastest, Most Comfortable, Best Upgrade Opportunity, Best Use of Miles, Best Overall Experience) has its own fixed weight per dimension, hand-tuned in app.js. If you rank more than one priority, a rank-1 pick's vector counts more than a rank-2 pick's, by this formula:

rankWeight   = (totalPriorities + 1 − rank) / totalPriorities
dimWeight[d] = Σ (priorityVector[priority][d] × rankWeight[priority]),
               then normalized so all 7 weights sum to 1
score        = Σ (dimensionScore[d] × dimWeight[d])  for d in the 7 dimensions

For example, under "Best Overall Experience" (the default lens most results are viewed through), the fixed vector is Cost 1.4, Comfort 0.5, Upgrade 0.4, Award 0.3, Flexibility 0.3, Risk 0.3, Time 0.2. Cost was raised from 0.3 to 1.4 on 2026-07-19, a direct, documented judgment call: an earlier version had a $2,400 nonstop beating a $1,300 one-stop under "Best Overall," which didn't hold up as a real recommendation. The new weight was tuned against that exact case and pinned by a regression test, so it can't silently drift back.

04Same inputs, same answer, every time

The scoring is deterministic. Run the same trip against the same fares tomorrow and you get the identical answer, down to the weights. Nothing is randomized and nothing calls a language model at recommendation time, which is what makes the weight breakdown on the results page worth reading: it's the arithmetic that actually produced the pick, not a rationalization written afterward.

05Push to main, Netlify deploys: five small Functions, nothing more

Every push to main auto-publishes, with no manual step. The backend is five small functions and nothing else. fares runs the live search; scan-offers holds the fares you scanned by hand. watch keeps the watched-trip list and watch-check re-searches it every other day. watched-trips serves the privacy-scoped read for the Concert Radar link. A separate weekly GitHub Actions script, not a Netlify function, sweeps for shows that have just started matching and commits the result. No framework, no build step, no database beyond one storage service.

Claude / Cowork builds & pushes to GitHub GitHub repo, main branch Netlify auto-deploy on push Your browser runs the engine locally the engine ships in the static bundle; the server side is five small functions: the fare search, the hand-scanned fare store, watch, and the every-other-day re-check, and the public watched-trips read vinylscout.org consumes
The real deploy path today.
06What's still manual, and what's still missing

Award-space search is the biggest gap left. Mileage figures are planning ranges, never availability checks.

The watcher does the fare-watching on its own now, re-checking each saved trip roughly every other day, capped at six trips (raised from three on 2026-08-11). Each check spends one metered search against a 100-search monthly quota, which is why the cap exists. Raising it further, or adding email and push alerts, needs a new credential or a quota decision, so it waits for Susan's explicit yes, the same rule that governs every other real cost here.

Saved memory covers the wallet, the preferences, and the watched trips. Other inputs still reset each session. The Points Guy valuation and transfer-bonus tables are a manual monthly refresh, copied by hand with their date on screen, so a stale month shows rather than hides.

Build your own · a starting point

Copy this prompt to start

This engine started from one written request to Claude. It grew in stages after that: a live fares feed, a real domain, a watcher, saved preferences, the Concert Radar cross-check, with every irreversible step held for explicit approval along the way. Below is a version of that first request. Copy it, adapt it to whatever you're actually comparing, and paste it into a new Claude conversation.

I want to build a personal tool that scores options I'm comparing (flights, routes, whatever the decision actually is) against what matters to me, not just price.

Here's the pattern I want it to follow:
1. Let me rank what matters to me in order (cost, comfort, flexibility, whatever my real priorities are) and weight every recommendation by that ranking, shown as an explicit breakdown per factor, never collapsed into one unexplained score.
2. Start with a reasonable estimate model if a live data source isn't connected yet, but always label clearly on-screen which mode I'm in, modeled estimate versus real live data, and never present one as the other.
3. Keep anything sensitive, like payment or account details, entirely local to my browser. Never sent anywhere or stored remotely.
4. Publish it as a small, real website I can use from my phone, with the scoring logic running instantly, without waiting on a server round-trip.
5. Let me set up a watch on something I care about, with a scheduled check that re-runs itself and tells me about a meaningful change without my having to ask.
6. Keep the backend radically simple. A feature that needs real infrastructure has to earn it, not get added by default.

Please help me scope this into a first small version we can finish today, tell me exactly what you'd need from me at each step (any account access, any purchase like a domain name, anything you can't do without my say-so), and don't do anything irreversible without checking with me first.
01

Claude asks before it assumes

Expect questions about what your real priorities are before any scoring gets built.

02

A first version, same session

A working, if small, scoring tool is realistic to see before the conversation ends.

03

You approve anything with a consequence

Spending money, connecting an account, or publishing something public always waits for your yes.

04

It grows in later sessions

The live fares feed, the domain, the fare watcher: none of that was in the first version. The Roadmap has the real order it happened in.

How this actually gets maintained

All of this runs inside Cowork, working from a single project folder Claude keeps on your behalf: the code, the data, and a file called CLAUDE.md, Claude's own running notes on how the system works, decisions already made, and what's still open. Every new Cowork session reads that folder first, so it picks up exactly where the last one left off, and writes its notes back before it ends. You never have to re-explain the project from scratch, even months later.

What it takes on your end