Skip to main content

AI Use

Rather than letting AI run on autopilot, I used the methods below to keep it a copilot: the right model for each decision, a specific skill for each kind of problem, and an evaluation that decides which changes stay.

Which model, and when​

Spec — Fable 5.1. The design spec and implementation plan were written with the most capable model available. Getting scope wrong at hour one costs more than any later bug, and a weaker model will agree with a bad plan. The tool design, the fixed answer layout and the pinned feed were all settled here.

Development — Opus 5. Most of the app: the DuckDB-WASM store, tool schemas and SQL guard, the agent loop, the map, the charts, Docker and CI.

Small fixes and tweaks — Sonnet 5. Renames, copy changes, one-line corrections.

Back to Fable 5.1 when integration got messy. Some later problems Opus 5 circled without closing, such as the second-feed schema, the data panel and a CSS bug from an indirect sqlrooms package. When a model fails twice on the same problem, I switch models instead of retrying.

Skills, and what each one was for​

grilling — defining the spec. The skill interrogated me before any code existed: what "frequent" means in minutes, which day, which reference stop on a branched route. Those are the same questions a planner's question leaves open, which is where the ask_user tool came from.

superpowers — the first full skeleton. The plan split into eight tasks, each with a brief, an implementation pass and a review of its diff. This got a running app on day one.

context7 — official docs. sqlrooms, DuckDB-WASM, deck.gl and Next.js 16 have all changed since the models' training data. Pulling current docs first avoids code written against APIs that no longer exist.

ponytail — keeping it simple. AI adds features faster than I can judge them, and the usual failure of an AI-built codebase is bloat. Ponytail cut features and abstractions instead of adding them.

impeccable — removing the AI look from the UI. Gradient cards, emoji headings, uniform padding. I used it to strip that out so the layout serves the data.

Evaluation is what makes AI converge​

AI writes GTFS SQL fast and confidently, and a confidently wrong headway is worse than no answer. So before tuning anything I built an eval: eval/questions.json holds the demo questions with reference SQL, and eval/run.ts runs the real agent against them and checks the numbers.

The system prompt in src/lib/agent/instructions.ts changed many times, almost all driven by eval failures: Green Line branches named wrong, hours bucketed where periods were meant, the wrong reference stop. Change the prompt, re-run, keep the better version. The same harness produced the model comparison in docs/observability-evaluation.md: six configurations across GPT-5.6 Luna (three effort levels), Gemini 3.6 Flash, Llama 4 Maverick, and one run with ask_user off to measure what clarification is worth.

Without an eval, improving a prompt is rewording and hoping. With it, each change is measurably better or it gets reverted.