The copilot pattern is usually: user types, model answers, pray. This one has no model in it. In nl-to-sql you ask the federal budget in plain English, and a deterministic pattern matcher either routes the question to one of 40 pre-approved, read-only SELECTs — or refuses, with a specific named reason.
It runs on the frozen budget database (15 tables, 46,042 rows, SHA-256 pinned). The results were computed once, at build time, and ship as JSON. At view time the page makes no API calls, has no build step, and no browser SQL engine — you get the generated SQL and the frozen result, side by side.
The catalog
Forty query keys across fourteen intent shapes: agency execution by fiscal year (budget authority, obligated, outlay, pacing) for the three tracked agencies; government-wide budgetary resources; cumulative obligations by fiscal period; VA’s top federal accounts; the debt trajectory; month-over-month debt growth; MTS receipts by source; the seven macro series; outlays vs their trailing average; top agencies by budget authority; and a few point lookups (latest debt, first-observation-of-month debt, latest value of one series, receipts current/FYTD/prior-FYTD). 228 frozen rows total — every cell one of those queries produces.
The refusal taxonomy
The interesting part is what happens at the edge. Every non-answer is a named fallback reason, rendered on the page:
| Reason | Trigger |
|---|---|
no-intent | out-of-scope topics — weather, opinions, open-ended questions |
untracked-agency | the agency is recognized (DoD 097, HHS 075, NASA 080, …) but absent from the period tables — by design, not by omission |
multi-agency | two or more agencies in one question; the grid holds one agency at a time |
fy-out-of-range / date-out-of-range | years or dates outside the frozen data |
calendar-month-not-mappable | a calendar month asked of period-keyed data |
injection / arbitrary-sql | SQL trying to get past the catalog — ;, --, DML, “write me a query” |
not-in-grid | a valid-looking combination the frozen grid doesn’t cover — never a guess |
The untracked-agency row deserves its place: only three agencies
(036, 020, 012) are in the per-period tables, and the router knows the other
fifteen by name and code — so “what did DoD obligate in FY2024” gets
“DoD (097) is recognized but not tracked in the period tables,” not a
number it doesn’t have.
Alias and typo tolerance is real but bounded: “treas,” “veterans affairs,”
and “whats the fed funds rat” all route correctly. The vocabulary is the
tracked-plus-untracked agency alias sets, the seven series alias sets,
fiscal-year forms (FY2024, fy23, bare years, “last year”), fiscal
periods (“period 11”, “P11”), and calendar months.
The eval is a precondition
Baked into the router and re-run on every page load: a 20-question evaluation — 12 answerable questions (each with an expected key and a pin row that must exist verbatim in the frozen data) plus 8 out-of-scope questions (each with the expected refusal), covering an alias question, a typo question, an untracked agency, a future fiscal year, an out-of-range date, a calendar-month mismatch, an injection attempt, and an arbitrary-SQL request. 20/20 pass is a page-load precondition — if the eval fails, the page shows a failure banner instead of routing anything unverified.
Why refusal is the feature
A copilot whose job is “answer anything” can be wrong about the data it has. A copilot whose contract is “here is exactly what I can answer, and here is why I can’t” can’t — every answer traces to a pre-approved query over a pinned database, and every non-answer names its reason. That’s the whole design, and it’s the reason the page ships the SQL next to every result.
Next: the same data through three modern tools.
— Joanna