Skip to content

MCP budget: get_plan_corrections defaults sized to the shared response budget (#4198) - #4257

Merged
erikdarlingdata merged 3 commits into
devfrom
fix/4198-plan-corrections-default
Sep 25, 2026
Merged

erikdarlingdata merged 3 commits into
devfrom
fix/4198-plan-corrections-default

Conversation

@erikdarlingdata

@erikdarlingdata erikdarlingdata commented Sep 25, 2026 •

Copy link
Copy Markdown
Owner

Part of #4198.

Why

get_plan_corrections at default arguments measured 95,428 bytes on a busy production server (24h window, limit=50). That is over 3x the shared 32 KB MCP response budget (McpResponseBudget.DefaultBytes). The default page fetched 51 rows and truncated each row's query_text only at 2,000 characters.

A synthetic reproduction on this lane's rig confirms the shape. It seeded 51 rows across five query-text
lengths, from 100 to 4,600 characters. Most rows were Active with null action fields. One row in five was
acted on. That measured 129,359 bytes at the old defaults. The exact number depends on the seeded
population, but the order of magnitude matches.

The row itself is wide independent of query_text: 20+ named fields, four lifecycle timestamps, several free-text reason columns. Cutting only the text preview was not enough to clear the budget. So the default row limit is cut too, the same way #4198 cut get_query_store_regressions from 20 rows to about 5 for a similarly wide row.

What changes

Both products (DarlingMcpPlanCorrectionTools.cs, McpPlanCorrectionTools.cs), identically:

  • query_text is now previewed at 150 characters by default, was 2,000. A new per-row query_text_truncated flag is true only when that row's text actually exceeded the preview length.
  • A new full_text opt-in parameter, default false, matches get_store_query_stats's existing shape. Pass full_text=true to get every row's whole query_text back, uncut.
  • The default limit drops from 50 to 25.
  • The tool's served head text is unchanged, kept under its own 620-character pin. The new full_text/query_text_truncated behavior is documented in the <<GUIDE>> tail instead. get_tool_guide serves that tail, and it does not count against the tools/list budget.

This PR also carries three mechanical fixes the change above required, in files it already touches:

  • DarlingMcpPlanCorrectionToolsTests.ParamContract_StandardWindowedShape_ServerNameOptional updated for the new full_text parameter.
  • McpPayloadContractCensusTests: added a new roster, FieldPreviewCutKeys, for the <field>_truncated
    shape this fix introduces. That is a wide field's own preview cut. It is distinct from a page cut (row
    count), a second bound, or a source-side (collector-time) cut. The full value is in the store. The flag
    is actionable: pass full_text=true and get it back. Classified query_text_truncated into it on both
    files.
  • McpToolsListBudgetTests (both products): updated the DarlingMcpPlanCorrectionTools/McpPlanCorrectionTools
    blocks for the new full_text parameter line. Raised TotalCeilingBytes by the measured +137 bytes on
    each product's full tools/list payload. Darling went from 171,637 to 171,774. Lite went from 89,881 to
    90,018. A change-log line is above each constant.
  • The web viewer's /api/read row for get_plan_corrections in DarlingWebEndpoints.cs now pins its own
    full_text default to true. That is the opposite of the MCP signature's false. This keeps this PR's
    MCP-side preview default from silently truncating the query text the viewer renders. The row already kept
    limit at the viewer's own 50, unaffected by the MCP default dropping to 25. The matching /api/catalog
    row gained a full_text parameter declaration. The viewer's server-tabs.js panel passes its own
    limit: 50 but no full_text, so the row's default governs. A new pin, PlanCorrectionsWebDefaultTests,
    reads the row's own dispatch line and fails if full_text stops being passed as true. Lite parity:
    confirmed by grep that no Lite UI code calls McpPlanCorrectionTools.GetPlanCorrections directly. Lite's
    UI reads LocalDataService, not the MCP tools, so this default change does not reach Lite's UI at all.

Test plan

  • New live test Darling/Darling.Tests/PlanCorrectionBudgetLiveTests.cs. Its own file, its own seeding, not extending a shared seeder. Seeds 51 rows across five query_text lengths (100/2200/2800/3600/4600 chars) with realistic field sparsity. Asserts the default call is under McpResponseBudget.DefaultBytes. Asserts both query_text_truncated=true and =false rows appear on the default page. Asserts full_text=true returns the newest row's whole query_text uncut. Measured before the fix (temporarily reverting the tool file, same seed): 129,359 bytes. After the fix: 29,075 bytes, 11% headroom under the 32,768-byte budget. Ran alone: pass.
  • New non-live test Lite.Tests/PlanCorrectionBudgetTests.cs (DuckDB, no rig needed). Same shape and assertions as the Darling live test. Ran alone: pass.
  • Darling.Tests: DarlingMcpPlanCorrectionToolsTests, PlanCorrectionFrameLiveTests, DarlingPlanCorrectionMigrationTests, DarlingPlanCorrectionLiveMigrationTests, PlanCorrectionBudgetLiveTests, 15 tests, pass. McpToolsListBudgetTests, 5 tests, pass. McpPayloadContractCensusTests plus McpToolGuideHeadsSqlTailPlanCorrectionsTests, 70 tests, pass.
  • Lite.Tests: McpPageContractTests, McpToolsListBudgetTests, McpToolGuideHeadsSqlTailPlanCorrectionsTests, PlanCorrectionBudgetTests, 30 tests, pass.
  • Merged origin/dev: fast-forward, no conflicts with this lane's files. Field data confirms the interval-table fix removes the spill, ship it (#3953) #4208 had not landed at merge time and did not touch plan_correction files in what merged.
  • Full Darling.Tests suite, once, against this lane's rig (UTC, port 55978): 13,844 total, 48 failed, 1 errored, 47 skipped, 666.7s. None of the 48 failures are in a file this PR touches, and none are get_plan_corrections/plan_correction-related. They cluster in PayloadDimensionLiveTests, TimescaleAggregateCompressionTests, several *AgainstDevPostgres-suffixed classes, and one TrendPayloadBudgetLiveTests stream-read exception. All look environment-related: this rig has no DARLING_TEST_PG_TARGET second Postgres instance configured, which several of those classes need. I did not cross-check these against dev's own latest CI run. The coordinator needs to do that before treating them as pre-existing.
  • Full Lite.Tests suite, once: 5,338 total, 0 failed, 0 errors, 279.6s. Clean.
  • Web-viewer fix (this update): Darling.Tests, filtered to every class that reads
    DarlingWebEndpoints.cs (55 classes, including DarlingWebEndpointsTests and ServerPageTabsTests).
    Also filtered to PlanCorrectionsWebDefaultTests, McpToolsListBudgetTests and
    McpPayloadContractCensusTests. Result: 1,434 total, 0 failed, 31 skipped (live classes, no rig for this
    update). Proved the new pin fails by reverting the full_text argument alone: 1 run, 1 failed,
    Assert.Contains on the missing substring. Restored the fix and reran the full filtered set green.
  • Live suite: CI decides.

CHANGELOG entry

SECTION: Fixed
ENTRY:

For the coordinator

  • Please verify the 48 Darling.Tests full-suite failures against dev's latest CI Build run before treating them as pre-existing. I ran out of context budget to do that cross-check myself. None are in files this PR touches.
  • Full Lite.Tests run finished clean after my first report: 5,338 total, 0 failed.
  • I reverted the tool file's short, pre-<<GUIDE>>, head sentence about the new preview after McpToolGuideHeadsSqlTailPlanCorrectionsTests pinned that head at a 620-character target. The explanation lives in the <<GUIDE>> tail instead. That keeps the served head unchanged and saves tools/list bytes versus my first pass.

Generated with Claude Code

https://claude.ai/code/session_01TszxYhJJbTEh4LrZ56NYo3

erikdarlingdata and others added 3 commits September 25, 2026 03:00
…#4198)

A default call measured 95,428 bytes on a busy production server, over
3x the 32 KB shared budget, because 50 rows each carried query_text
truncated only at 2,000 characters. Preview it at 150 chars with a
query_text_truncated flag and a full_text opt-in, and cut the default
row limit to 25 to match the row's own width once query_text is thin.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TszxYhJJbTEh4LrZ56NYo3
…fault cut (#4257)

get_plan_corrections's new full_text MCP default (false) previews query_text
at 150 characters. The web viewer's /api/read row calls the same MCP method,
so without its own default it would inherit the preview. Pin the row's
full_text to true so the viewer keeps showing the whole query text, and add
a source-text pin so a future edit that drops the argument fails a test
instead of silently truncating what the viewer renders.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TszxYhJJbTEh4LrZ56NYo3
Darling McpToolsListBudgetTests: 171_637 + 137 (plan_corrections) + 82
(audit_config) + 364 (deadlock_detail) = 172_220. Lite: 89_881 + 137 + 253
= 90_271. McpPayloadContractCensusTests: keep dev's FieldPreviewCutKeys
description, add both deadlock_graph_xml_truncated and query_text_truncated.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FVjn4PBJN71NQXdFo6ZxNQ
@erikdarlingdata erikdarlingdata changed the title DO NOT MERGE (MCP budget): get_plan_corrections defaults sized to the shared response budget (#4198) MCP budget: get_plan_corrections defaults sized to the shared response budget (#4198) Sep 25, 2026
@erikdarlingdata
erikdarlingdata marked this pull request as ready for review September 25, 2026 08:05
@erikdarlingdata
erikdarlingdata merged commit 3aff908 into dev Sep 25, 2026
16 of 18 checks passed
@erikdarlingdata
erikdarlingdata deleted the fix/4198-plan-corrections-default branch September 25, 2026 08:19
erikdarlingdata added a commit that referenced this pull request Sep 25, 2026
Incorporates plan_corrections (+137 Darling/Lite) from dev (#4257).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FVjn4PBJN71NQXdFo6ZxNQ
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant