Lite MCP tools disclose a truncated query-stats window (#4231) - #4279
Conversation
…cedures_by_cpu, get_query_store_top (#4231) query_stats, procedure_stats and query_store_stats are raw-only in Lite (no rollup fallback), and the default 30-day retention_days is per-collector and user-settable, so a window can silently serve less than asked for. Adds one shared floor probe (LocalDataService.GetQueryWindowFloorAsync) reading the same v_ view (hot table UNION archived parquet) each grid/tool reads, and wires effective_start / effective_hours_back / window_truncated into the three MCP tools that read those tables, using McpQueryTools.TruncationSlack (already defined for the trend tools) as the threshold. Part 1 of #4231's Lite half (helper + MCP tools + tests). Grids (Lite/Controls/ServerTab.*) are next. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TszxYhJJbTEh4LrZ56NYo3
…er note (#4231) get_top_queries_by_cpu / get_top_procedures_by_cpu now carry Lite's (#4279) exact truncation_note sentence, including the "or this server has been monitored for less time than that" clause Q1a's version was missing. WPF: Top Queries / Top Procedures / Query Store grids each read their raw table's floor through the shared RawWindowFloor probe (ViewerDataService.*WindowFloorAsync) and show "Showing since <effective start>" in the grid header when truncated, formatted the way Performance Trends already formats a truncated head. Wired into both the toolbar-window load and each slicer-drag re-read. Web: the same three MCP tools' truncation_note now renders through panels.js's existing #3278 noteKey opt-in, on the built-in server tabs and the starter dashboard template. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TszxYhJJbTEh4LrZ56NYo3
The Top Queries, Top Procedures and Query Store grids (and their time-range slicers, which re-read the same grid over a narrower window) now show "Showing since <effective start>" in the header when the shared GetQueryWindowFloorAsync probe finds the raw table's floor cutting the requested window short -- the same words and probe the three MCP tools added in the previous commit on this branch use, via one shared RefreshWindowTruncatedBannerAsync helper so the grid and the tool can never disagree. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TszxYhJJbTEh4LrZ56NYo3
A source pin (QueriesTabGridReads_RouteThroughSharedWindowFloorHelper) confirms every Queries-tab grid and slicer read of query_stats, procedure_stats and query_store_stats routes through the ONE shared GetQueryWindowFloorAsync probe and calls RefreshWindowTruncatedBannerAsync, never a hand-rolled second copy. Two more tests pin ServerTab.SetWindowTruncatedBanner's text and visibility for the truncated and not-truncated cases. Verified the source pin catches a dropped call site by temporarily removing one and reverting. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TszxYhJJbTEh4LrZ56NYo3
…ext (#4231) The earlier commits on this branch put the new raw-tier/window_truncated sentence in the SERVED HEAD (before <<GUIDE>>) for get_top_procedures_by_cpu and get_query_store_top, pushing them over their tools/list budget pins and, for get_top_procedures_by_cpu, over the 620-char hard target. Both already carry the same fact via the shared McpHelpers.WindowTruncatedDescription appended in the guide tail, so the head text was redundant as well as oversized. - get_top_procedures_by_cpu: dropped the redundant head sentence, matching get_top_queries_by_cpu's existing (correct) pattern of leaving the disclosure to the tail. - get_query_store_top: trimmed the head sentence to the essential fact (raw-tier retention floor, no rollup) instead of restating what the tail already says. - Banked the resulting saving in McpToolsListBudget/McpQueryTools.txt (get_query_store_top's ceiling: 422 -> 390). - Lite.Tests/McpToolGuideHeads.CollectionLog.cs: the "Lite: no such floor" guardrail fact and its dedicated test were pinning the PRE-#4231 shape (Lite genuinely had no floor then); updated both to the current, true fact and renamed the test accordingly. - Lite.Tests/McpToolGuideHeads.Data.cs: CpuTimeExtremesTopic_RidesOnBothTopByCpuTools asserted the CPU-extremes topic ends each tool's tail; it no longer does, since WindowTruncatedDescription now trails it on both tools. Switched the assertion from EndsWith to Contains. Full Lite.Tests suite pending in the next commit. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TszxYhJJbTEh4LrZ56NYo3
…dinator (#4231) Darling.Tests' McpToolGuideTests.EverySharedToolName_CarriesTheMarkerOnBothSkus_OrNeither_WithByteIdenticalHeads requires get_query_store_top's served head to be byte-identical on both SKUs. Darling's own #4231 PR (#4278) is a separate, still-open lane that keeps its head as on dev, so this reverts Lite's head to match dev's Darling head exactly (including its now-stale "Lite: no such floor" line) and moves the corrected #4231 explanation to the tail instead, where get_tool_guide serves it -- the payload's own window_truncated / effective_start / effective_hours_back fields (and their tests) were never wrong, only this shared head sentence. Also updates Darling.Tests/McpPayloadContractCensusTests.cs's cross-SKU rosters (WindowFloorBlocks, WindowFloorTools, CutNoteKeys) for the three Lite tools that legitimately gained McpHelpers.WindowTruncatedDescription and truncation_note in this PR -- data-only roster additions, no Darling product code touched. Verified: Darling.Tests McpToolGuideTests, McpToolGuideHeadsDataTests, McpPayloadContractCensusTests all green (81/81); Lite.Tests McpToolsListBudgetTests, McpToolGuideTests, McpToolGuideHeadsDataTests, McpToolGuideHeadsCollectionLogTests, QueryWindowTruncationTests, ServerTabCapabilityPinTests all green (37/37). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TszxYhJJbTEh4LrZ56NYo3
…he twin-pin census check Three fixes to the checkpoint (fdda3d0) that shrank get_top_queries_by_cpu's and get_top_procedures_by_cpu's served heads back to dev's byte-identical values, and the one census assert that shrink left failing: - DarlingMcpDataTools.cs: swap the tail concatenation order on both tools to McpHelpers.WindowTruncatedDescription + McpToolGuideTopics.CpuTimeExtremesAndAttribution, so the tail ends with the shared CPU-extremes topic again (McpToolGuideHeadsDataTests.CpuTimeExtremesTopic_RidesOnBothTopByCpuTools). - DarlingMcpDataTools.txt: bank the two tool lines to the now-measured 406/474 bytes (down from the stale 678/742 sized for the removed head sentence). - McpToolsListBudgetTests.cs: rewrite the #4231 change-log comments; they claimed +550 head bytes that no longer exist now the head matches dev. - McpPayloadContractCensusTests.cs: EveryWindowFloorTool_CarriesTheSharedClause_AndNoOtherToolDoes required window_truncated / "not a page cut" in these two tools' HEAD, which is what the checkpoint's shrink had removed to satisfy D6's twin pin (McpToolGuideTests.EverySharedToolName_CarriesTheMarkerOnBothSkus_OrNeither_WithByteIdenticalHeads, shared with Lite's #4279). The two requirements are unsatisfiable together for a tool pair split across two PRs targeting the same tree: a Darling-only head sentence fails the twin pin regardless of merge order, so D6 wins. Scoped the census check to read both facts from the tail for just these two tools, with the reason and a note that a follow-up can move one identical sentence into both SKUs' heads once #4279 merges. Targeted classes (151 total, 0 failed): RepoFileAdoptionTests, McpToolGuideHeadsDataTests, McpToolGuideTests, McpPayloadContractCensusTests, McpToolsListBudgetTests, QueryStoreTopWindowTests, QueryStoreClutterTests, RawWindowFloorSharedHelperSourcePinTests. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TszxYhJJbTEh4LrZ56NYo3
…4279) The slicer handlers passed server-local fromServer/toServer (ServerTimeHelper.ToServerTime) to RefreshWindowTruncatedBannerAsync, and the custom-range refresh path passed server-local cStart/cEnd, while GetQueryWindowFloorAsync compares straight against UTC collection_time. On any server not on UTC the banner probed a window shifted by the server's offset. Slicers now pass e.StartUtc/e.EndUtc directly. Refresh.cs computes the banner's window through a new internal LocalDataService.GetQueriesTabWindowUtc helper -- the same GetTimeRange call GetTopQueriesByCpuAsync/GetTopProceduresByCpuAsync/GetQueryStoreTopQueriesAsync already use for their own window -- so the grid and its banner can never drift onto two different ranges again. The comparison calls on the same lines (RefreshQueryStatsComparisonAsync and its two twins) are deliberately untouched: GetComparisonRange() (ServerTab.Comparison.cs:59) is UTC for the default hoursBack window but server-local for a custom range, the same split as the bug just fixed, so giving the "current" side a UTC window while the baseline stays server-local would desync them instead of fixing them. That needs GetComparisonRange() itself rerouted, which is a separate design decision. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TszxYhJJbTEh4LrZ56NYo3
…isclosure # Conflicts: # Darling/Darling.Tests/McpPayloadContractCensusTests.cs
Fix: the banner now probes the same UTC window the grid readConfirmed the defect as described.
New helper: Also renamed Comparison calls: left untouched, with the reasonChecked
Per the brief's ruling, I did not touch these. Rerouting only the "current" side onto the new UTC helper while Tests (
|
…4231) (#4278) * MCP raw-window disclosure: get_top_queries_by_cpu / get_top_procedures_by_cpu (#4231) query_stats and procedure_stats are raw-only tables dropped at 4 days when the rollups are armed, and get_top_queries_by_cpu / get_top_procedures_by_cpu never reported how far back their window actually reached. Adds a shared RawWindowFloor probe (generalized from #2364's single-table QueryStoreWindowFloorSql) and wires both tools to publish effective_start / effective_hours_back / window_truncated, the same names and meaning get_query_store_top already uses. Part of #4231. WPF grid headers and the web viewer note are not in this commit; see the PR body for the handoff. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TszxYhJJbTEh4LrZ56NYo3 * MCP truncation_note parity, WPF Queries-tab grid disclosure, web viewer note (#4231) get_top_queries_by_cpu / get_top_procedures_by_cpu now carry Lite's (#4279) exact truncation_note sentence, including the "or this server has been monitored for less time than that" clause Q1a's version was missing. WPF: Top Queries / Top Procedures / Query Store grids each read their raw table's floor through the shared RawWindowFloor probe (ViewerDataService.*WindowFloorAsync) and show "Showing since <effective start>" in the grid header when truncated, formatted the way Performance Trends already formats a truncated head. Wired into both the toolbar-window load and each slicer-drag re-read. Web: the same three MCP tools' truncation_note now renders through panels.js's existing #3278 noteKey opt-in, on the built-in server tabs and the starter dashboard template. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TszxYhJJbTEh4LrZ56NYo3 * Checkpoint: LF-read adoption for the #4231 viewer pin, top-by-CPU description heads (#4231) Q1a2 stopped at its turn limit before re-running the 5 suite failures; unverified. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TszxYhJJbTEh4LrZ56NYo3 * #4231: fix the CpuExtremes tail order, bank the shrunk heads, scope the twin-pin census check Three fixes to the checkpoint (fdda3d0) that shrank get_top_queries_by_cpu's and get_top_procedures_by_cpu's served heads back to dev's byte-identical values, and the one census assert that shrink left failing: - DarlingMcpDataTools.cs: swap the tail concatenation order on both tools to McpHelpers.WindowTruncatedDescription + McpToolGuideTopics.CpuTimeExtremesAndAttribution, so the tail ends with the shared CPU-extremes topic again (McpToolGuideHeadsDataTests.CpuTimeExtremesTopic_RidesOnBothTopByCpuTools). - DarlingMcpDataTools.txt: bank the two tool lines to the now-measured 406/474 bytes (down from the stale 678/742 sized for the removed head sentence). - McpToolsListBudgetTests.cs: rewrite the #4231 change-log comments; they claimed +550 head bytes that no longer exist now the head matches dev. - McpPayloadContractCensusTests.cs: EveryWindowFloorTool_CarriesTheSharedClause_AndNoOtherToolDoes required window_truncated / "not a page cut" in these two tools' HEAD, which is what the checkpoint's shrink had removed to satisfy D6's twin pin (McpToolGuideTests.EverySharedToolName_CarriesTheMarkerOnBothSkus_OrNeither_WithByteIdenticalHeads, shared with Lite's #4279). The two requirements are unsatisfiable together for a tool pair split across two PRs targeting the same tree: a Darling-only head sentence fails the twin pin regardless of merge order, so D6 wins. Scoped the census check to read both facts from the tail for just these two tools, with the reason and a note that a follow-up can move one identical sentence into both SKUs' heads once #4279 merges. Targeted classes (151 total, 0 failed): RepoFileAdoptionTests, McpToolGuideHeadsDataTests, McpToolGuideTests, McpPayloadContractCensusTests, McpToolsListBudgetTests, QueryStoreTopWindowTests, QueryStoreClutterTests, RawWindowFloorSharedHelperSourcePinTests. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TszxYhJJbTEh4LrZ56NYo3 --------- Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
Resolves the get_query_store_top conflict in Lite/Mcp/McpQueryTools.cs: keeps dev's QueryTextPreviewLength=400 constant and query_text preview sentence, keeps this branch's module_name-miss hint clause, and drops the "#4231 WIRE CHANGE" paragraph now that the next commit unifies the head text across both products. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TszxYhJJbTEh4LrZ56NYo3
get_query_store_top's head drops the "Darling: "/"Lite: no such floor" split for one shared sentence: window_truncated is a window floor, not a page cut, because stored history can be shorter than asked. Both get_top_queries_by_cpu and get_top_procedures_by_cpu gain the same sentence in their heads (both stay under the 620-char budget). Lite's CPU-tool tails now order McpHelpers.WindowTruncatedDescription before McpToolGuideTopics.CpuTimeExtremesAndAttribution, matching Darling, and all four now join with exactly one space at each seam. Updates the pin tests that expected the old split text: the census carve-out for the two CPU tools, both McpToolGuideHeads.CollectionLog twin tests (rewritten as one shared-head assertion), and new guardrail rows in both McpToolGuideHeads.Data files. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TszxYhJJbTEh4LrZ56NYo3
|
Finishing-step lane report (merge dev + shared window-floor head). Context ran out before the full test pass, so this is a checkpoint, not a finish. Everything below is pushed to 1. Merge of origin/dev (commit
|
…#4279) Lite's #4279 lands the shared window-floor head, so the twin exemption in Darling's budget no longer applies. Re-measured tools/list for both SKUs: get_query_store_top's head drops the old split for the shared sentence (422 -> 351), get_top_queries_by_cpu and get_top_procedures_by_cpu each gain that same sentence (474 -> 600, 406 -> 532). Net +181 bytes on both sides. Updated the three per-tool lines and each project's TotalCeilingBytes (174,373 -> 174,554 Darling; 92,041 -> 92,222 Lite), with a change-log comment recording the deltas. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FVjn4PBJN71NQXdFo6ZxNQ
Budgets and test runs finished (code was already done)Branch Builds
Measured budgets (old -> new, both projects agree exactly)
Net per-tool delta is +181, and it matches the total delta on both sides exactly, confirming no other tool moved.
Targeted classes (both projects, all green)Darling.Tests (single run, 89/89 pass, then split to confirm no class silently matched zero tests):
Lite.Tests (single run, 31/31 pass, same per-class confirmation): Lite has no
Full suites (run once each, foreground, no merge from
|
Part of #4231.
Why
At "Last 7 days", three Lite MCP tools read raw-only tables (
query_stats,procedure_stats,query_store_stats) with no rollup fallback. Lite keeps these tables 30 days by default, but the retention is set per collector and a user can lower it. A new install also has less history than the window asks for. In each case the read can serve far less than the caller asked for, and nothing in the answer says so.#2364 fixed the same problem in Darling's
get_query_store_top. This PR is the Lite half of #4231: the shared floor probe, the three MCP tools, and the matching WPF grids. The Darling half, #4278, is merged. This PR also makes the three tools' descriptions match in both products, the last step #4278 left.What changes
LocalDataService.GetQueryWindowFloorAsync(QueryWindowRelation, serverId, startUtc, endUtc)inLite/Services/LocalDataService.QueryWindowFloor.cs. It readsMIN(collection_time)fromv_query_stats,v_procedure_statsorv_query_store_stats, bounded on both sides of the window. It reads thev_view, not the bare table. That view is the hot table plus the parquet archive, so the floor covers archived history too. On a seeded store with 500 hot and 500 archivedquery_statsrows, the probe took 50 to 92 ms locally.get_top_queries_by_cpu,get_top_procedures_by_cpuandget_query_store_top(Lite/Mcp/McpQueryTools.cs) each gaineffective_start,effective_hours_back,window_truncatedand atruncation_note. Every existing field, default and argument is unchanged. The names and meaning match Darling's.window_truncateddescribes the window's floor, not a page cut, andtoporlimitnever changes it. The threshold is the existing 90-minuteMcpQueryTools.TruncationSlack.ServerTimeHelper.FormatServerTime, like the comparison notes on the same tabs. Each grid refreshes the note on a sub-tab switch, a full refresh and a slicer drag. The Query Store slicer reads the same view as its grid, so it shares that grid's probe.0ad7d3d9). The probe compares its bounds straight against UTCcollection_time. The first version passed server-local times after a slicer drag and under a custom range. On a server not on UTC, the note then probed a window shifted by the server's offset. It then showed a false "Showing since", or missed a real one. Now the slicer handlers pass the slicer's owne.StartUtcande.EndUtc. The refresh paths call a newLocalDataService.GetQueriesTabWindowUtc, which makes the sameGetTimeRangecall the three grid reads make.One description for each tool, in both products (commits
f10d80c3,4226552e,f6e9f53a)McpToolGuideTestsrequires each shared tool's served head (the text before<<GUIDE>>) to be the same bytes in both products. So neither PR can change these heads alone. Once #4278 merged, this branch mergeddevand changed them inLite/Mcp/McpQueryTools.csand Darling'sDarlingMcpDataTools.cstogether:get_query_store_top: the head said "Darling: window_truncated marks a window floor..." and then "Lite: no such floor; the full requested window is always read." The second line was false once Lite had a floor. Both sentences are now one: "window_truncated marks a window floor, not a page cut — no limit changes it — because stored history can be shorter than asked; effective_start / effective_hours_back give the reach actually served." The head went from 422 to 351 bytes.get_top_queries_by_cpuandget_top_procedures_by_cpu: each head gains "window_truncated marks a window floor, not a page cut; effective_start / effective_hours_back give the reach actually served." The heads went from 474 to 600 bytes and from 406 to 532. Both stay under the 620-character limit.get_query_store_top's tail about the stale head line.Tests that pinned the old text changed with it:
McpPayloadContractCensusTests: the exception MCP top-queries/top-procedures disclose a shortened raw-tier window (#4231) #4278 added for the two CPU tools is gone. Every Darling window-floor tool now checks its head for "window_truncated" and "not a page cut".McpToolGuideHeads.CollectionLog.cs, in both test projects: the test that pinned "Darling: window_truncated" and "Lite: no such floor" now checks the shared head and that neither old line is served.McpToolGuideHeads.Data.cs, in both: guardrail rows for the two CPU tools' new sentence. Lite's CPU-topic test is back toAssert.EndsWith.tools/listbudget, in both: the three tools' lines inDarlingMcpDataTools.txtandMcpQueryTools.txt.TotalCeilingBytesgoes from 174,373 to 174,554 in Darling and from 92,041 to 92,222 in Lite. Both grew by the same 181 bytes.Found, not fixed here
The Queries tab's comparison reads, beside the new note, have the same time-basis bug, and it is older than this PR. After a slicer drag or under a custom range, they read a window shifted by the server's UTC offset. That is #4284. Moving them onto UTC also means moving the comparison's own baseline range, so it gets its own change.
Left for #4231
Stage 3: routing the top-N reads to the hourly rollups, after #4186 merges.
Tests
In
Lite.Tests/QueryWindowTruncationTests.cs, with its ownDuckDbInitializerso a test controls the archive folder:window_truncated: trueand the seeded floor aseffective_startwhen the raw table starts after the window's start.window_truncated: falseand a nulltruncation_note.QueriesTabGridReads_RouteThroughSharedWindowFloorHelperchecks the source. The probe has one call site, and every grid and slicer path calls the shared note helper. NoServerTab*.csfile writes its ownMIN(collection_time)query.ServerTab.SetWindowTruncatedBanneron an STA thread.0ad7d3d9, at a -240 minute offset: one pinsGetQueriesTabWindowUtcunder a custom range. One checks that a slicer's UTC range matches the window the grid read. One scans the source so that every note call takes UTC bounds. With the old slicer and refresh files put back, that source test failed. With the fix restored, it passed.Runs, on
f6e9f53a:dotnet buildofLite.TestsandDarling.Tests: 0 warnings, 0 errors.McpPayloadContractCensusTests,McpToolGuideTests,McpToolGuideHeadsDataTests,McpToolGuideHeadsCollectionLogTestsandMcpToolsListBudgetTests: 89 of 89 passed.McpToolGuideTests,McpToolGuideHeadsDataTests,McpToolGuideHeadsCollectionLogTests,McpToolsListBudgetTestsandQueryWindowTruncationTests: 31 of 31 passed.Darling.Tests, once: 14,003 tests, 0 failed, 740 skipped, 1 not run. The skips are live classes. No PostgreSQL rig ran.Lite.Tests, once: 5,383 tests, 0 failed.f6e9f53a: build, Darling PostgreSQL tests and Lite tests all pass.CHANGELOG entry
SECTION: Fixed
ENTRY:
get_top_queries_by_cpu,get_top_procedures_by_cpuandget_query_store_top. Lite keeps those tables 30 days by default, and a user can lower that per collector. So a long window was read from much less history, with nothing to say so. Each tool now returnseffective_start,effective_hours_backandwindow_truncated, plus atruncation_notewhen the window was cut short. The three grids show "Showing since" and the effective start above the grid in the same case, after a time-range change or a slicer drag. On Lite and Darling alike, each tool's short description now explainswindow_truncated. The false line that said Lite always reads the full window is gone.REF:
[Lite MCP tools disclose a truncated query-stats window (#4231) #4279]: Lite MCP tools disclose a truncated query-stats window (#4231) #4279
🤖 Generated with Claude Code
https://claude.ai/code/session_01FVjn4PBJN71NQXdFo6ZxNQ