Flagship app: reform spine, shell, parameter search, and composer (feature-flagged) - #1136
Draft
PavelMakarchuk wants to merge 84 commits into
Draft
Flagship app: reform spine, shell, parameter search, and composer (feature-flagged)#1136PavelMakarchuk wants to merge 84 commits into
PavelMakarchuk wants to merge 84 commits into
Conversation
Introduces the canonical Reform object with provenance (manual/chat/bill/ tool) as the shared spine of the flagship app rework, following the existing ingredient + store patterns: - Reform type built on the existing Parameter/ValueInterval types, with a policyId link for materializing into canonical API policies - ReformStore interface with ApiReformStore (/api/reforms) and LocalStorageReformStore implementations, mirroring the user-*-association stores - ReformAdapter for camelCase/snake_case wire conversion - Central Postgres store: Drizzle schema + checked-in migration and /api/reforms route handlers in calculator-app (Web-standard Request/Response so app/tsconfig's program stays free of next/server's React typings) - Handlers return 503 when DATABASE_URL is unset, so environments without the database degrade cleanly; no existing page imports any of this, so prod behavior is unchanged Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
34 integration tests run the actual /api/reforms route handlers against an in-memory Postgres with the checked-in migration applied: full CRUD, the complete validation battery, SQL-injection-shaped and unicode labels, 500-parameter payloads with mixed value types, per-user isolation, updated-first ordering, 404 paths, and the degraded 503 mode when no database is configured. Adds setDbForTesting() injection to the db module and guards the vitest browser mocks so node-environment suites can run. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The four-verb navigation from the flagship design doc, dark by default:
enable with VITE_FLAGSHIP_SHELL=true (NEXT_PUBLIC_FLAGSHIP_SHELL in
calculator-app) or localStorage.setItem('pe-flagship-shell', 'on').
- Ask: natural-language entry stub; examples per country, routes into
Build until the hosted agent service lands (phase 2)
- Tracker: links to the existing proxied bill tracker until the native
feed lands (phase 3)
- Build: fronts the existing policies/create pathway
- Library: first real reform-store consumer — lists saved reforms with
provenance badges, empty and error states, links to reports/households
- Sidebar and CalculatorRouter switch on the flag; with it off, routes
and nav are byte-identical to before
12 new tests cover the flag, both sidebar states, Ask interactions, and
Library loading/empty/error states.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Two-stage search over the full parameter index (~52k entries for the US): a token AND-prefilter over precomputed breadcrumb+path haystacks answers common queries in a few milliseconds, with fuzzy re-ranking of the candidate set; full-fuzzy fallback catches typos. Stress testing against real US metadata drove the design — a single-stage fuzzy scan took ~1.3s per multi-word query, far too slow for typeahead. Breadcrumbs come from the existing getHierarchicalLabels util, so a query like "child tax credit amount" now finds gov.irs.credits.ctc.amount even though its leaf label is just "amount" — the core findability fix the flagship rework promised. - libs/parameterSearch.ts: entries, index, two-stage search, memoized selectors (shared later by the agent's locate stage) - ParameterSearchBox: keyboard-navigable typeahead showing breadcrumb + path - Build page: search fronts the editor; selecting a parameter shows breadcrumb, path, unit, description, and formatted current value - 18 tests including search-quality assertions against the real 52k US metadata and a latency regression bound Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Running the flagship shell locally surfaced three issues: - The lockfile churn from earlier dependency adds split @tanstack/react-query into two copies (root 5.90.20 + app/ 5.84.1), so the QueryClient context lookup crashed every calculator page during dev SSR. Regenerated bun.lock from the branch-base lockfile so only the genuinely new packages are added and a single react-query resolves. - calculator-app routes via per-page Next wrappers, not the shared react-router table, so ask/tracker/build/library got thin Next pages following the existing pattern, gated by FlagshipGate (404 behavior when the flag is off) plus a flag-aware country index redirect. - FlagshipGate originally fell back to the shared NotFoundPage, whose react-router Link crashed under Next SSR whenever the server-side flag check disagreed with the client. The gate now uses an SSR-stable env check (NEXT_PUBLIC_FLAGSHIP_SHELL is inlined into both bundles), reads the localStorage override after mount, and renders a router-free fallback. All four flagship routes now server-render cleanly with no hydration errors. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The blanket build/ ignore rule for build output also matched the Next route segment calculator-app/.../(calculator)/build/, silently dropping its page from the previous commit. Scope a negation so route source under calculator-app/src/app survives. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
…-end The flagship loop now works without any backend: describe or find a parameter → editable draft reform → save to library. - draftReform lib: cross-page draft state (localStorage + useSyncExternalStore) with provisions carrying breadcrumb, unit, and baseline; converts to the Reform store shape with provenance intact - ReformPreviewCard: the trust layer — every provision spelled out as baseline → new value, editable inline, before anything is saved - Ask: questions run through the parameter search index; matched parameters become suggested provisions with an honest note that keyword matching stands in for AI drafting until the hosted service lands; recent reforms surface below - Build: parameter detail card gains "add to draft reform" - Tracker: native feed of clearly-labeled sample bills demonstrating the bill → editable reform bridge (the Modal tracker exposes no public JSON API yet); full tracker remains linked - Library: edit (loads the composer), duplicate, and delete actions - Shared parameterValues utils (current-value + formatting) 36 new tests cover the draft lifecycle, preview card interactions, Ask matching, tracker bridge, and library actions. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
In the flagship IA a report is the output of a reform, not a thing you start from scratch — so the shell's primary action opens Build (the composer) instead of the legacy report wizard. Legacy mode keeps the existing "New report" button untouched; tests cover both states. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Over half the US index (23k of 42k parameters) is state-specific, plus 392 contributed/experimental parameters — the two main sources of result clutter: - Entries carry isContrib and stateCode parsed from the path - searchParameters takes filters: contributed hidden by default, state scope selectable (all / federal only / federal + one state) - Search box grows a scope dropdown and contributed opt-in, state and contributed badges on result rows, and a "N matches hidden by filters" hint so filtered-out results are never a silent mystery - Leaf labels get their own strong fuse weight (0.3) and join the fast-path haystack — they are often the most information-dense part of the hierarchy - Ask inherits the same defaults (no contributed suggestions) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Four entry points don't earn a 300px persistent sidebar — that's dashboard chrome, and the flagship's front door (Ask) wants to be full-width and calm. In flagship mode StandardLayout now renders a slim tab bar (Ask · Tracker · Build · Library + "New reform") under the header and no sidebar; content gets the full viewport width. The legacy sidebar layout is byte-identical with the flag off, and Sidebar.tsx reverts to its pre-flagship form since it no longer renders in the shell. Tests cover both layouts and the top nav interactions. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Replace the hand-rolled tab and toggle buttons with the shared design system components the report-output redesign and one-off tools use: the radix Tabs pill list, SegmentedControl for the dollars/percent chart toggle, and MetricCard (hero revenue card with trend chips, invertArrow poverty metrics) inside bordered report cards. The title takes the report layout's teal 3xl treatment, and the sponsor/date attribution row gets aligned icons. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The pill tab bar reads cleaner as the line variant — hairline rule under the full bar, teal underline on the active tab, wider gaps. The bill summary paragraph moves above the metric cards in Overview. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Selecting a state in the scope dropdown now returns only that state's parameters (previously it kept federal too, labeled "Federal + CA"); options read "CA only" to match. The scope and contributed filters move above the search input, and the "N matches hidden by filters" hint is removed. The Ask page's three section tiles stretch to equal height. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ic contract For countryId uk, Ask now streams real turns from policyengine-uk-chat through a same-origin proxy route (no CORS coordination, no service changes): markdown answers, tool activity lines, follow-up suggestion chips, and a draft bridge — the validated reform JSON the service's simulation tools carry is mapped onto our parameter index and offered as 'Add to draft' provisions for the composer. Transport failure falls back to the keyword matcher; the US path is unchanged. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Adds /api/us-ask/chat/message — Claude (claude-opus-5) with deterministic parameter-search tools (search_parameters, get_parameter, validate_reform) over live policyengine-us metadata, streaming the same SSE event shapes as the UK chat service so the Ask client and the chat→draft bridge work unchanged for both countries. The agent drafts and validates reforms; impacts still come from running the report. The tool layer is pure and unit-tested in app/src/libs/flagship/usAskAgent.ts; the route holds only the streaming loop and a cached metadata index. The chat client generalizes to askChat.ts with per-country endpoint resolution: UK stays on by default, US is opt-in via NEXT_PUBLIC_US_ASK=on and needs a server-side ANTHROPIC_API_KEY (the route 503s without one and Ask falls back to keyword matching). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Next.js inlines process.env.NEXT_PUBLIC_* textually into the client bundle, so the dynamic process.env[name] lookup was always undefined in the browser and the US opt-in flag never enabled. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The add-to-draft card only exists when the model calls validate_reform; a turn that described the reform in prose and told the user to add it left nothing to click. Make the tool call a hard precondition for proposing a reform, and validate in the first response when the user's message already fully specifies the change. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
For fully specified imperatives the validate_reform card is the answer; cap the accompanying text at a sentence or two and reserve longer explanations for open questions. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A multi-provision directive burned all six tool turns on research and returned an empty answer with no card. Raise the budget to ten turns and, past it, re-run with tool_choice none so the model must close with prose instead of ending tool-hungry. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ack record Two validation surfaces on the bill report, one tab: - External checks for this bill: the tracker pipeline's validation_metadata (official fiscal note, third-party analyses, accepted range, within-range verdict, discrepancy explanation) joins into the bill feed and renders as a comparison table, with a within-range chip in the report header. - Model track record: a new /api/model-validation route serves compact US-level comparable rows from the PolicyEngine scorecard (PE vs external analyses like Urban's State of the Safety Net) for the programs the bill's provisions touch, cached server-side; rows are labeled held-out vs calibrated so the honesty distinction survives into the UI. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Radix unmounts inactive tab panels, so the scorecard fetch only started when the Validation tab was opened. Lift it into a useModelTrackRecord hook called at page level so it runs in parallel with the rest of the report, and show a loading note for the rare case the tab opens before it lands. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Budgetary gains an average-per-household card and, when the tracker's validation pass found external estimates, a magnitude-plotted comparison chart (PolicyEngine vs fiscal-note estimates or range) with signed labels. Poverty gains a grouped before/after bar chart for overall and child rates. Distribution gets value labels and a taller canvas. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The prose findings duplicated what the report now shows properly — headline metrics as tiles, external checks on the report's Validation tab, and provenance under Notes and sources. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The bill detail duplicated the report's overview (summary, tiles, provisions) as an extra hop with nothing of its own. Scored bill cards now navigate straight to the report; the detail remains the launchpad for bills without computed impacts and the editor for saved reforms. The report content column widens 900→1040 inside a 1480 shell so tabs and charts use the space. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Also gitignore calculator-app/.vercel from the CLI link. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The findings prose reiterated the impact tabs and the validation tab; what the tab uniquely owns is provenance — model version, data version, and computed date. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The deployed score repository at policyengine.org/scorecard publishes its data under /scorecard/data; reading the Urban shard directly means the report's track-record rows always match what a click-through to the scorecard shows, including per-row held-out/seeded relationships and the shard's row_defaults. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The Urban comparisons validate the model's baseline representation of the programs a bill touches; they are not reform-matched. Retitle and caption them so they can't be read as a check of the bill's estimate, which only the fiscal-note section claims. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
External numbers now flow through ValidationClaim — a policy-keyed row mirroring the deployed scorecard's data shape — with an adapter from the tracker's validation_metadata. Bill checks render from claims, so when fiscal notes move into the scorecard as a source, the swap is the adapter, not the UI. Claim rows also show the PE-to-external ratio. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Two costs made each Build navigation pay ~700ms: ParameterSearchBox built its own index per mount, and index creation eagerly constructed a Fuse instance over ~20k entries that the fast prefilter rarely needs. The box now accepts the store-memoized index (built once per session) and Fuse construction is lazy, deferred to the first query the prefilter can't answer. Warm tab switches drop to ~150ms. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
entriesByPath rebuilt a 20k-entry Map per mount and addablePaths rebuilt a 20k Set per render. Both are now reselect selectors computed once per metadata load, bringing Build's warm switch time level with the other tabs. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The report is the main event; adjusting stays one click away on the slim edge tab. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The deployed scorecard's data layout changed (shards back to a single comparison file), 502ing the track-record route and leaving Validation tabs blank. The route now tries each known layout in order and normalizes both row shapes, with the repo's committed file as final fallback; a failed fetch renders a note linking to the scorecard instead of nothing. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The blank-tab regression shipped because nothing asserted what renders when the scorecard is unreachable. Cover unavailable, loading, empty, and populated states of the track-record section. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…nchors it Rhode Island-style validations triangulate from scaled third-party scores; calling that a fiscal-note range would over-claim. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A second-pass verifier now re-checks every stored claim against its source; the panel shows the outcome (re-verified / partially / disputed / unverifiable) with the date. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Each validation_metadata row now pins what it checked (validated_against: PE estimate, model version, bill status). billFeed compares the snapshot to the live run and flags drift; the chip drops its verdict and asks for a re-check, with a note explaining why. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The report page's Validation section now renders the model track record for the drafted provisions and an opt-in "Find external estimates" action. It dispatches a web-search agent that hunts official and third-party scores of the same or similar proposals and returns structured findings with honest comparability tiers (direct / similar / context) — figures only as sources state them, never constructed. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The localStorage override now only works where FLAGSHIP_OVERRIDE=allow is set (dev servers and the beta deployment) — production builds have no runtime activation path. All flagship API routes (us-ask, uk-chat proxy, validate-estimate, model-validation, reforms/reports persistence) 404 unless NEXT_PUBLIC_FLAGSHIP_SHELL is on, so flag-off deployments expose no new surface. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
First implementation pass of the flagship app rework: one product spine (reform → simulation → report) with three entry modes, built behind a feature flag so production is unchanged until enabled.
Reformobject with provenance (manual | chat | bill | tool),ReformStore(API + localStorage impls) following the existing user-association store patterns, Postgres schema (Drizzle) +/api/reformsroute handlers in calculator-app. Handlers degrade to 503 withoutDATABASE_URL; Neon provisioning via Vercel marketplace is documented incalculator-app/src/db/README.md.NEXT_PUBLIC_FLAGSHIP_SHELL=true(orlocalStorage.setItem('pe-flagship-shell', 'on')). With the flag off, routes and nav are unchanged.gov.irs.credits.ctc.amountin milliseconds across the 52k-parameter US index.Testing
turbo run test), both typechecks, production buildHow to try
Design doc: internal artifact "One app: PolicyEngine flagship design" (Aug 2026).
🤖 Generated with Claude Code