Skip to content

MCP budget: preview the deadlock graph XML by default on get_deadlock_detail (#4198) - #4254

Merged
erikdarlingdata merged 5 commits into
devfrom
fix/4198-deadlock-detail-default
Sep 25, 2026
Merged

erikdarlingdata merged 5 commits into
devfrom
fix/4198-deadlock-detail-default

Conversation

@erikdarlingdata

@erikdarlingdata erikdarlingdata commented Sep 25, 2026 •

Copy link
Copy Markdown
Owner

Part of #4198.

Why

get_deadlock_detail had no default response-size budget. deadlock_graph_xml is the wide field. On a busy production store, one lane member measured a default call (limit 5, hours_back 24) at 120,454 bytes and 247 ms. Only 3 deadlocks in the window carried a graph, and each graph averaged about 40 KB. That is almost 4 times the shared 32 KB McpResponseBudget.DefaultBytes target from #4198's ruling.

What changes

Darling (Darling/PerformanceMonitor.Darling.Service/Mcp/DarlingMcpBlockingTools.cs) and Lite (Lite/Mcp/McpBlockingTools.cs), get_deadlock_detail only. get_blocking lives in the same Darling file. It is a different lane's work, and it is untouched here.

  • deadlock_graph_xml is now a 2000-character preview by default, with a new deadlock_graph_xml_truncated: true field alongside it when a graph was cut. This is the same preview-plus-opt-in shape get_store_query_stats already uses for full_text.
  • A new full_graph argument (default false) opts back into the whole XML for every row.
  • A dedup_key call (naming one incident) always gets the whole graph for the matched row(s), regardless of full_graph. The caller already paid the cost of naming it, so a preview would defeat the point of the call. Lite's get_deadlock_detail has no dedup_key, so this rule only applies on Darling.
  • Both tools' served descriptions now say so. Both products' tools/list budget pins (McpToolsListBudget/*.cs) moved to match the new served bytes, with a change-log line on each TotalCeilingBytes bump: Darling +364 bytes (172,001), Lite +253 bytes (90,134). The two differ because Darling's description also covers the dedup_key exemption, which Lite has nothing to exempt.
  • Darling's cut-key census (McpPayloadContractCensusTests) gained a new FieldPreviewCutKeys class. deadlock_graph_xml_truncated is not a page cut, a second bound, a source-side (collector-time) cut, or a homonym. It is a new MCP read tools have no default response-size budget: at default arguments 12 tools return >50 KB and 3 return >100 KB for one server, more than an agent client's per-result cap #4198 shape: a field-level response-budget preview with a caller opt-in. Future *_truncated fields of this shape, on other tools' own lanes, will want the same class.
  • The web viewer's /api/read row for get_deadlock_detail in DarlingWebEndpoints.cs now pins its own full_graph default to true. That is the opposite of the MCP signature's false. This keeps this PR's MCP-side preview default from silently shrinking what the viewer renders. The matching /api/catalog row gained a full_graph parameter declaration. Neither server-tabs.js nor view-templates.js passes its own full_graph value for this read, so the row's default governs. A new pin, DeadlockDetailWebDefaultTests, reads the row's source text and fails if full_graph stops being passed as true.
  • Lite parity: confirmed by grep that no Lite UI code calls McpBlockingTools.GetDeadlockDetail directly. Lite's UI reads LocalDataService, not the MCP tools, so this default change does not reach Lite's UI at all.

Test plan

Measured on a new live test seeding 5 planted graphs near 42,221 characters each, the worst realistic default page. Production's 3 real graphs averaged about 40 KB each.

  • Before this fix: unbounded (production measurement above: 120,454 bytes for 3 graphs).
  • After this fix: 16,190 bytes for 5 graphs, under the 32,768-byte budget.

New tests:

  • Darling/Darling.Tests/DarlingMcpDeadlockDetailBudgetLiveTests.cs (new file). Plants 5 wide graphs against Postgres. Asserts the default call is under budget and each row is marked truncated. Asserts full_graph: true returns the whole XML unmarked. Asserts a dedup_key call returns the whole XML even with full_graph: false.
  • Lite.Tests/McpPageContractTests.cs: added GetDeadlockDetail_Default_StaysUnderResponseBudget_WithFiveWideGraphs, same shape against DuckDB (no dedup_key on Lite).

Suite runs:

  • Darling.Tests targeted classes (blocking tools, budget pins, census, new live test): 32 + 5 + 5 + 68, all green, each reverified after its own fix.
  • Darling.Tests full suite, once, after merging origin/dev: 13,838 total, 47 skipped, 1 not run, 3 failed. One was this change's own census gate (McpPayloadContractCensusTests.EveryCutKey_IsThePageDialect_OrClassified_OnBothSkus), fixed and reverified green in isolation (68/68). The other two are unrelated to this diff. They reproduce in isolation on a fresh rig, not merely under full-suite load:
    • TrendPayloadBudgetLiveTests.EveryDefaultAnswer_StaysNearTheBudget_AndTheLargestAnswerStaysUnderTheCap fails with get_file_io_trend returning a stream-read exception, not a data assertion. Different tool, no code path this PR touches.
    • PgTargetBlockingTests.ThirtyOneDaysOfLightBlocking_... fails an anomaly-detection ratio assertion (2.7692 vs an expected 2.8-3.0 range), in a scenario anchored to DateTime.UtcNow, so it is date/time-of-run sensitive. Different subsystem (blocking-stats anomaly detection, not deadlock graphs), no code path this PR touches.
    • Dev's own latest Build run (2026-09-25 06:31:55Z, after this branch's merge base) is green. Time did not allow opening that run's job logs to confirm it executed these exact two tests. Flagging both plainly rather than guessing further given the deadline.
    • A second full run, after this PR's own fixes, was not completed before the lane deadline. The targeted reruns above cover every file this PR touches.
  • Lite.Tests full suite, once: 5340 total, 0 failed.
  • Both products build with 0 Warning(s), 0 Error(s).
  • Web-viewer fix (this update): Darling.Tests, filtered to DeadlockDetailWebDefaultTests, DarlingWebEndpointsTests, ServerPageTabsTests, McpToolsListBudgetTests, McpPayloadContractCensusTests: 160 total, 0 failed. Proved the new pin fails by reverting the full_graph argument alone: 1 run, 1 failed, Assert.Contains on the missing substring. Then restored the fix and reran green. No rig was available for this update. No live class ran, and none of the filtered classes needs one.
  • Live suite: CI decides.

CHANGELOG entry

SECTION: Fixed
ENTRY:

Generated with Claude Code

https://claude.ai/code/session_01TszxYhJJbTEh4LrZ56NYo3

erikdarlingdata and others added 3 commits September 25, 2026 02:31
…graph on request (#4198)

deadlock_graph_xml is the wide field on get_deadlock_detail: a busy production store's default
call measured 120,454 bytes for 3 graphs (about 40 KB/graph), over the shared 32 KB MCP
response-size budget. Truncates it to a 2000-character preview by default
(deadlock_graph_xml_truncated: true), with a full_graph opt-in for the whole XML, the same
preview-plus-opt-in shape get_store_query_stats uses for full_text. A dedup_key call (naming one
incident) always gets the whole graph, since the caller already paid the cost of naming it.

Same fix on Lite's get_deadlock_detail twin (no dedup_key there). Adds a new Darling live test
class seeding wide graphs against Postgres, measuring the default call's UTF-8 bytes under the
budget; adds a Lite.Tests case doing the same against DuckDB. Updates both products'
tools/list budget pins for the new full_graph parameter and the changed tool description.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TszxYhJJbTEh4LrZ56NYo3
get_deadlock_detail's new field-level preview flag needed a class: not a page cut (truncated),
not a second bound, not a source-side (collector-time) cut, and not a homonym. Adds
FieldPreviewCutKeys for #4198's shape — one wide field previewed under the response budget, with
a caller opt-in (full_graph, or a dedup_key call) that gets the whole field back, unlike a
source-side cut the caller can do nothing about.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TszxYhJJbTEh4LrZ56NYo3
@erikdarlingdata erikdarlingdata changed the title DO NOT MERGE (MCP budget): preview the deadlock graph XML by default on get_deadlock_detail (#4198) MCP budget: preview the deadlock graph XML by default on get_deadlock_detail (#4198) Sep 25, 2026
@erikdarlingdata
erikdarlingdata marked this pull request as ready for review September 25, 2026 07:11
@erikdarlingdata
erikdarlingdata enabled auto-merge (squash) September 25, 2026 07:11
…seline to 171_719

171_637 + 82 (#4206) + 364 (#4254) = 172_083

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FVjn4PBJN71NQXdFo6ZxNQ
…4254)

get_deadlock_detail's new full_graph MCP default (false) previews the graph
XML. 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_graph to
true so the viewer keeps showing the whole graph, and add a source-text pin
so a future edit that drops the argument fails a test instead of silently
shrinking what the viewer renders.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TszxYhJJbTEh4LrZ56NYo3
erikdarlingdata added a commit that referenced this pull request Sep 25, 2026
Classified the four new payload keys (findings_truncated,
findings_truncated_note, confidence_basis_truncated, advice_truncated)
in McpPayloadContractCensusTests: findings_truncated/_note join
SecondBoundCutKeys/CutNoteKeys as a second, independent page cut beside
the tool's pre-existing truncated/truncation_note; confidence_basis_
truncated/advice_truncated start a new FieldPreviewCutKeys roster (added
with the same name and tuple shape #4254 uses for get_deadlock_detail, so
a later merge only needs to combine array entries).

Updated both products' McpToolsListBudgetTests pins for the grown tool:
the served head (610 -> 740 bytes, from the new head sentence), the two
new parameter lines (limit 122, full_text 146), and each product's
TotalCeilingBytes raised by the measured +495 bytes, with a change-log
comment. Both products' McpToolsListBudgetTests classes pass (5/5 each).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TszxYhJJbTEh4LrZ56NYo3
@erikdarlingdata
erikdarlingdata enabled auto-merge (squash) September 25, 2026 08:02
@erikdarlingdata
erikdarlingdata merged commit 5803c87 into dev Sep 25, 2026
17 of 18 checks passed
@erikdarlingdata
erikdarlingdata deleted the fix/4198-deadlock-detail-default branch September 25, 2026 08:02
erikdarlingdata added a commit that referenced this pull request Sep 25, 2026
…ews (#4198) (#4266)

* WIP (#4198): get_analysis_findings default limit and text previews

Checkpoint of lane TG's uncommitted work at its 200-turn stop. Not yet
built or tested as a whole; a finishing lane completes it.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TszxYhJJbTEh4LrZ56NYo3

* #4198: shorten get_analysis_findings' limit description, fix web viewer row

Both build now. The limit parameter description was 225 characters, over
the D2 200-char cap for a converted tool's parameter; cut it to the
essentials and moved the longer explanation into the tool's guide tail
(after <<GUIDE>>), on both products. Also fixed a missing space the
checkpoint's head-sentence insertion left ("whole.confidence_basis").

The /api/read row for get_analysis_findings inherited the new limit:18/
full_text:false defaults, which would have shown the web viewer's two
"Analysis Findings" tables only 18 of a window's chains. Pass
Rows(c, "limit", MaxRowLimit) and QueryBool(c, "full_text", true) so the
row keeps returning what dev always did: every chain, full text. Added a
no-rig source-scan pin (DarlingWebEndpointsTests) that fails if the row
regresses, since GetAnalysisFindings needs a live Postgres connection and
can't be pinned by invoking it directly.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TszxYhJJbTEh4LrZ56NYo3

* #4198: census and tools/list budget pins for get_analysis_findings

Classified the four new payload keys (findings_truncated,
findings_truncated_note, confidence_basis_truncated, advice_truncated)
in McpPayloadContractCensusTests: findings_truncated/_note join
SecondBoundCutKeys/CutNoteKeys as a second, independent page cut beside
the tool's pre-existing truncated/truncation_note; confidence_basis_
truncated/advice_truncated start a new FieldPreviewCutKeys roster (added
with the same name and tuple shape #4254 uses for get_deadlock_detail, so
a later merge only needs to combine array entries).

Updated both products' McpToolsListBudgetTests pins for the grown tool:
the served head (610 -> 740 bytes, from the new head sentence), the two
new parameter lines (limit 122, full_text 146), and each product's
TotalCeilingBytes raised by the measured +495 bytes, with a change-log
comment. Both products' McpToolsListBudgetTests classes pass (5/5 each).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TszxYhJJbTEh4LrZ56NYo3

* #4198: drop the head-sentence insertion, bank the pin savings

Lite's full suite caught a hard 620-char served-head cap
(McpToolGuideTests.EveryConvertedHead_StaysAtOrUnder620Characters and
its SqlCore sibling) that McpToolsListBudgetTests' looser 1000-char/
600-target check didn't. TG's head-sentence addition ("Default limit 18
chains...") pushed the served head to 740; that fact isn't in either
guide's required guardrail roster (McpToolGuideHeads.SqlCore.cs), and
it's now covered in the tail I added in the prior commit, so the
sentence is dropped from the head entirely rather than trimmed. The head
is back to its exact original text (610 served bytes on both products).

Banked the resulting savings in both products' pins: the per-tool line
back to 610, and TotalCeilingBytes lowered to the newly measured total
(the two new parameters still cost real bytes: +364 Darling, +353 Lite).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TszxYhJJbTEh4LrZ56NYo3

* fix CS0102: remove duplicate FieldPreviewCutKeys, add top_query_text_truncated

Conflict resolution for McpPayloadContractCensusTests.cs added a second
FieldPreviewCutKeys array after SourceSideCutKeys. The branch already
defined FieldPreviewCutKeys before SourceSideCutKeys, so the result was
a CS0102 duplicate-field error. Remove the second block and add the
missing top_query_text_truncated entry to the first array instead.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FVjn4PBJN71NQXdFo6ZxNQ

* Fix Census doc comment run break: remove blank line between items

A blank line introduced during merge resolution broke the doc comment
XML run between the FieldPreviewCutKeys item and the WithheldSummaryKeys
item, causing DocCommentHygieneTests.EveryDocRunClosesTheSummariesItOpens
to fail.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TszxYhJJbTEh4LrZ56NYo3

---------

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
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