feat(reporters): surface modeled tables in the CI comment - #193
Merged
Merged
Conversation
The synthesizer estimates row counts for tables the production snapshot never covered, so any cost touching one is modeled rather than measured. The MCP tools already say so. The CI comment did not, leaving no way to tell a verified cost from an estimate (Site#3420). Render a non-blocking NOTE naming those tables. The wording and the ten-name cap come from Site's `modeledTablesWarning`, so the two surfaces stay consistent. `buildViewModel` returns from two places, and the early return handles runs with no baseline. Both carry the new field, and a test pins the non-comparison path. Existing snapshots set no modeled tables, so they are unchanged. Co-Authored-By: Claude <noreply@anthropic.com>
There was a problem hiding this comment.
Query Doctor Analysis
3 queries analyzed
0 regressed · 0 improved · 0 new · 0 removed
2 pre-existing issues
SELECT "guests"."id", "guests"."session_id", "guests"."username", "guests"."avatar_path", "guests"."color", "guests"."side", "guests"."audio_recording_path", "guests"."audio_recording_public", "gue...
indexassets(event_id, inserted_at desc)
cost 31,003,449 → 1,498 (100% reduction)SELECT * FROM guest_ip_addresses WHERE ip_address = '127.0.0.1';
indexguest_ip_addresses(ip_address)
cost 154,402 → 8 (100% reduction)
Using assumed statistics (10000000 rows/table). For better results, sync production stats.
More detail → get_ci_run({ runId: "019fa48c-cf18-7970-b05b-acf99dba5223" }) · view run · docs
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.
Goal
A developer reading a Query Doctor PR comment should be able to tell a measured cost from an estimated one. Costs are planned against a snapshot of production statistics; a table the snapshot has never seen has no real row count, so the synthesizer estimates one. Those estimates are plausible, not measured, and a query against such a table can pass the gate for the wrong reason.
This is the reporting half of Site#3420. The data half merged in #184. The MCP tools already carry the caveat; this brings the CI comment in line. No follow-up is planned after this — see the note at the bottom for a separate item this uncovered.
What
Before: a PR touching a table the snapshot doesn't cover showed its cost with no indication the number was estimated.
After: the comment opens with a note naming those tables.
Note
1 table(s) in your schema aren't covered by the statistics you loaded, so their costs are modeled, not verified against real data:
public.invoices. A query touching one isn't a clean pass.Nothing renders when the snapshot covers the whole schema. Nothing gates on it — the severity is
NOTE, notCAUTION, so it must not read as a blocking error.How
Read
src/reporters/github/github.tsfirst.buildModeledTablesNotice(ctx)turnsReportContext.modeledTablesinto{count, list}, ornullwhen the list is empty, and the template renders on that single truthy check. The wording and the ten-nameand N morecap are copied frommodeledTablesWarningin Site'spackages/mcp-server/src/apply-stats.ts, so the CI comment and the MCP tools tell a developer the same thing.Two decisions worth flagging:
The field is named
modeledTablesNotice, notmodeledTables.report()renders with{...ctx, ...viewModel}, soctx.modeledTablesis already in template scope. Reusing the key would shadow astring[]with an object, which works only as long as viewModel keeps spreading second.buildViewModelhas two return statements, and the early one covers runs with no baseline. A field added to one and not the other vanishes silently on exactly those runs — it compiles, it type-checks, and it produces the false all-clear this work exists to remove. Both returns carry the field, and a test pins the no-baseline path on its own. The same shape of bug hit the Site side once already (ac5c73c72).src/reporters/reporter.tshas a one-line doc fix: the comment onmodeledTablesclaimed the field wasn't rendered yet.Tests
Eight tests in
github.test.ts. On the view model: the comparison path, the no-baseline path, the ten-name cap withand 3 more, an empty list, and an absent list. On the template: the note renders with aNOTEseverity and neverCAUTIONorWARNING, it renders on a run with no baseline, and it renders nothing when there are no modeled tables.The existing snapshots are unchanged rather than regenerated. Neither sets
modeledTables, so nothing new renders for them; leaving them untouched proves this didn't disturb unrelated output.Full suite passes (344 tests, 35 files).
tsc --noEmitis clean.Beyond the suite, I rendered the real
success.md.j2through the realbuildViewModel, using the same spread orderreport()uses, across four contexts: with a baseline, without one, with 13 tables, and with none. Output matched in all four.Not verified: no live CI run against a branch that adds an uncovered table. Everything upstream of
ReportContext.modeledTablesis trusted on #184 having merged, not observed here. Worth aqa-setup-cipass against a throwaway migration before this is relied on.Not in this PR
Statistics's optional fifth constructor argument: synthesis only runs when a caller passescurrentSchema, andStatistics.fromPostgrescan't pass it, so some paths never synthesize at all. Core0.16.0is published and this repo now calls the constructor positionally, so changing that shape is a coordinated break across two repos. It should be done deliberately and separately.