Skip to content

Query Store clutter and oversized-plan backlog: MCP reading guidance moves off tools/list (#3898) - #4067

Merged
erikdarlingdata merged 3 commits into
devfrom
feature/3898-content-sql-tail-a
Sep 23, 2026
Merged

erikdarlingdata merged 3 commits into
devfrom
feature/3898-content-sql-tail-a

Conversation

@erikdarlingdata

@erikdarlingdata erikdarlingdata commented Sep 23, 2026 •

Copy link
Copy Markdown
Owner

Refs #3898. Moves the reading guidance in two Darling-only MCP tools off tools/list into get_tool_guide
tails: get_query_store_clutter and get_oversized_plan_backlog.

Neither tool has a Lite twin. Verified by git grep -n 'Name = "<tool>"' -- Lite/Mcp: both already sit on
CrossAppMcpToolInventoryPinTests' known-missing roster. So this PR makes no Lite changes.

What changes

Both descriptions now carry <<GUIDE>>. A short new head stays on tools/list. The full original prose
(minus two D4 removals) becomes each tool's get_tool_guide tail.

Tool Before (served) After (served head + pointer)
get_query_store_clutter 5,147 610
get_oversized_plan_backlog 2,997 611

include_fleet_median's parameter description was 322 characters. It is now a 190-character guardrail: what
the flag does, that it defaults to false, and to see the reading guide. Its full original wording moves into
the tool's own tail, appended right after the tail's existing fleet_median is opt-in (include_fleet_median): ... sentence.

Corrections

None. Both tools' original prose was checked against the tool bodies while drafting the heads. No factual
errors turned up.

D4 removals

Two, both in get_query_store_clutter's tail. get_oversized_plan_backlog carried no issue references or
anecdotes.

  1. The (#3796) issue reference in "null means the newest capture predates the V137 rung (query_store_health does not collect QUERY_CAPTURE_MODE or WAIT_STATS_CAPTURE_MODE — the one option that names a plan-churn factory is the one the health row omits #3796) that added
    the column." The reference is gone. The rule stays: "predates the V137 rung that added the column, which is
    NEVER ASKED and never NONE."
  2. The #3502 issue reference and its one-clause anecdote in "get_collection_health's fanout.dominance is slowest-vs-mean, not a concentration verdict — publish the slowest item's share of the pass directly #3502 verdict figure;
    the motivating case read 92-96%." Both are gone. The cross-tool naming fact stays:
    "get_collection_health's verdict figure."

Verified with dropcheck.py origin/dev HEAD get_query_store_clutter get_oversized_plan_backlog.
get_oversized_plan_backlog shows missing=0. get_query_store_clutter shows missing=1: one long run-on
chunk, because the splitter finds no sentence break between the (a) read_cost clause and the 92-96%
anecdote inside it. That one reported "sentence" folds both removals above, plus their surrounding unchanged
text, into a single line. Same shape as PR #4063's D4 report. McpToolGuideHeads.SqlTail.cs's
QueryStoreClutter_D4Removals_DropTheIssueRefsAndAnecdote_KeepTheRules pins that both issue numbers and the
anecdote are gone, and that both rules survive.

Re-pointed pins

One, mechanical.
Darling.Tests.QueryStoreClutterTests.Description_TeachesTheArms_TheReplicaRule_TheExclusions_CaptureMode_AndTheWindowFloor
asserted the literal "#3796" against the tool's full description text. D4 removes that issue number from the
wire. So the pin now checks "V137". That is the rung name that the same sentence still carries. The name of
the test already promises to check it ("CaptureMode").

D9 traps (11)

Each is a question that a model can get wrong when it reads only the served heads.

  1. Q: get_query_store_clutter returns qs_overhead with high QDS_* wait time. Which database caused it?
    Correct: none is namable. Overhead is per SERVER.
    Prevents it: the head's "qs_overhead (waits/memory clerk) is per SERVER, never attributed to a
    database."
  2. Q: A database's read_cost/plan_churn/config are all null. Is that database's Query Store idle?
    Correct: clutter is per DATABASE. A null arm on one row says nothing about server overhead or any other
    database.
    Prevents it: the head's "Clutter (read_cost/plan_churn/config) is per DATABASE."
  3. Q: The call returns status: unavailable. Is Query Store just quiet this window?
    Correct: no. No rows in any of the three arms is reported as unavailable, not a clean, quiet answer.
    Check the precondition ladder (engine capability, runtime precondition) before concluding nothing ran.
    Prevents it: the head's "No rows answers unavailable, never a clean bill."
  4. Q: A database reads verdict: Unknown. Is its Query Store healthy?
    Correct: no. Unknown means unmeasured, never quietly Healthy.
    Prevents it: the head's "Verdict Unknown means unmeasured, never quietly Healthy."
  5. Q: A readable-secondary database shows excluded: true, verdict: Unknown. Is that a misconfiguration
    to fix?
    Correct: no. Query Store on a readable secondary is read-only by design. It is never a defect.
    Prevents it: the head's "REPLICAS: excluded, verdict Unknown, never a defect."
  6. Q: query_capture_mode is null. Is the server's capture mode NONE?
    Correct: no. Null means the newest capture predates the column and was never asked. NONE is a real,
    distinct mode the engine reports separately.
    Prevents it: the head's "query_capture_mode null means never asked, never NONE."
  7. Q: window_truncated: true. Were rows paged away? Would a bigger limit fix it?
    Correct: no to both. It is the plan-churn/wait arms' raw-tier retention floor, not a page cut. No
    limit value changes it.
    Prevents it: the head's "window_truncated is a retention floor, not a page cut: no limit fixes it."
  8. Q: The response has no fleet_median. Does that mean no fleet data exists?
    Correct: no. include_fleet_median was left at its default of false, so the field is not computed.
    Prevents it: the head's "fleet_median is null unless include_fleet_median=true."
  9. Q: get_oversized_plan_backlog shows only pending rows with last_captured_at: null. Is that just an
    early, quiet backlog?
    Correct: no. It is the one signature that says the out-of-band sweep's fetch half has never once
    succeeded on that server. It is indistinguishable from healthy on every other surface.
    Prevents it: the head's "Read last_captured_at/last_expired_at FIRST" beside "No time window: a worklist
    updated in place, not a series." There is no earlier or later row to compare against, so the stamp is the
    only signal.
  10. Q: A row reads captured, but get_plan_xml still returns nothing for it. Is that a bug?
    Correct: no. rows_with_content is not the same claim as captured. A captured row can still carry no
    content, which is exactly the case the fallback read cannot serve yet.
    Prevents it: the tail's "rows_with_content sits beside captured on purpose and is NOT redundant" (kept
    off the head for space). The head's own partition fact, "pending+captured+expired=total_rows," at least
    tells a reader the three verdicts are exhaustive and distinct.
  11. Q: A call omits server_name and sets include_rows: true. It comes back with an error. Why not just
    return the fleet's rows?
    Correct: the claim key means something only within one server. A fleet-wide listing is mostly
    identifiers for servers that nobody asked about. So the tool refuses the call and does not widen it.
    Prevents it: the head's "include_rows requires server_name (refused otherwise)."

Test plan

  • Darling/Darling.Tests/Darling.Tests.csproj builds with 0 Warning(s), 0 Error(s).
  • Targeted classes (McpToolGuide*, McpToolsListBudget*, McpPayloadContractCensus*,
    QueryStoreClutter*, OversizedPlanBacklog*, McpDescription*, McpZeroIsAMeasurement*,
    DarlingMcpConfigHistory*): 279 total, 0 failed, 9 skipped (live-PostgreSQL, no rig).
  • Red-watch: broke one McpToolGuideHeads.SqlTail.cs head-fact pin, watched it fail, restored it, watched
    it pass again.
  • dropcheck.py origin/dev HEAD get_query_store_clutter get_oversized_plan_backlog:
    get_oversized_plan_backlog missing=0. get_query_store_clutter missing=1 (the D4 removals above, listed).
  • git merge origin/dev: already up to date, no conflicts.
  • Full Darling suite, once, after the merge: Total: 13216, Errors: 0, Failed: 0, Skipped: 631.
  • Lite build and tests: not run. No Lite twin exists for either tool, confirmed by
    CrossAppMcpToolInventoryPinTests' known-missing roster and a direct git grep of Lite/Mcp. This PR
    touches no file under PerformanceMonitor.Common, so Lite's build is unaffected.
  • Coordinator, after a merge of origin/dev (1dc11cf, with get_collection_log and get_query_store_top reading guides move off tools/list (#3898) #4065): dropcheck shows only the D4 unit listed above. D9: 11/11, with no misreading. The answers to Q2 and Q10 were hedged, and the model marked get_tool_guide as needed. Full Darling.Tests 13,219 total, 0 failed. Full Lite.Tests 5,232 total, 0 failed.
  • Coordinator: the plain-English check on this body shows only the allowed hits and two quotes (tool text and a trap question).

Handoff

Nothing deferred. Both assigned tools are fully converted, pinned and green. No new topic was added to
McpToolGuideTopics.SqlTail.cs. It stays the empty scaffold, because the two tools share no duplicated prose
with each other.

🤖 Generated with Claude Code

https://claude.ai/code/session_01M7r1CXmEcBmsrTV34CCLfF

@erikdarlingdata
erikdarlingdata marked this pull request as ready for review September 23, 2026 20:13
@erikdarlingdata
erikdarlingdata enabled auto-merge (squash) September 23, 2026 20:13
@erikdarlingdata
erikdarlingdata merged commit 9b298cb into dev Sep 23, 2026
16 of 18 checks passed
@erikdarlingdata
erikdarlingdata deleted the feature/3898-content-sql-tail-a branch September 23, 2026 20:23
erikdarlingdata added a commit that referenced this pull request Sep 23, 2026
…ntries in their sections (#4080)

Adds 42 entries and 42 link refs (#3992, #3995, #3996, #3998, #4001, #4002, #4003, #4007, #4010, #4011, #4013, #4015, #4020, #4022, #4025, #4029, #4030, #4031, #4036, #4038, #4039, #4040, #4044, #4047, #4048, #4049, #4050, #4051, #4055, #4061, #4063, #4064, #4065, #4066, #4067, #4068, #4069, #4070, #4071, #4073, #4074, #4078). Each PR's entry was buffered, and this lands every entry whose PR was merged on origin/dev when it ran.

#3989 left 26 entries under bare 'Changed' and 'Fixed' lines above '### Added'. They move into '### Changed' and '### Fixed', below the new entries, and one blank line stays under [Unreleased].


Claude-Session: https://claude.ai/code/session_01Ua31ugERL5DmhFVRtf6keQ

Co-authored-by: Claude Opus 5.5 <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