Repository navigation
get_query_store_regressions moves reading guidance off tools/list (#3898) - #4109
Merged
Merged
Conversation
) Converts the D6 twin pair (Darling and Lite) to the two-tier McpToolGuide description: a terse head under the 620-char budget for tools/list, and the full original prose as the get_tool_guide tail. Re-points McpZeroIsAMeasurementTests' null-percent pin from the whole literal to the head's own guardrail sentence, with the field-name-level detail (undefined_percents, "null percent never sorts as 0") pinned in the tail instead. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QQh8LczP1HGLXQYAx4oTZC
erikdarlingdata
marked this pull request as ready for review
September 24, 2026 02:38
erikdarlingdata
enabled auto-merge (squash)
September 24, 2026 02:38
9 tasks done
erikdarlingdata
added a commit
that referenced
this pull request
Sep 24, 2026
* Add 48 buffered CHANGELOG entries in one splice Adds 48 entries and 48 link refs (#4057, #4077, #4079, #4081, #4082, #4083, #4084, #4085, #4086, #4087, #4088, #4089, #4090, #4091, #4092, #4093, #4095, #4096, #4099, #4100, #4101, #4103, #4105, #4106, #4107, #4108, #4109, #4110, #4111, #4113, #4114, #4115, #4116, #4117, #4118, #4119, #4120, #4121, #4122, #4123, #4124, #4125, #4126, #4127, #4131, #4133, #4136, #4141). Each PR's entry was buffered, and this lands every entry whose PR was merged on origin/dev when it ran. Entries found under a bare section line (none unless the old script ran again) move into the matching ### section, below the new entries. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QQh8LczP1HGLXQYAx4oTZC * Changelog splice: add #4137's entry, which merged before the splice but was missed #4137 (pg_plan_capture reads csvlog) merged at 05:57Z with a CHANGELOG entry section in its body. It was neither spliced nor listed as needing no entry. Its entry goes under Fixed, right after #4136's, and #4136's last sentence now points to it instead of saying plan capture is unchanged. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NUU29PuGg9TUFBsACgZg2K --------- Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
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.
Refs #3898.
Why
Darling's
tools/listserved 1,187 characters forget_query_store_regressions. Lite served the sameidentical text. Under #3898, that reading guidance moves off the tool list. It goes into a reading guide that
get_tool_guide(tools[], topics[])serves on demand, so a client no longer pays for it before the firstquestion. Nothing is deleted.
What changes
Converts the one twin pair (
get_query_store_regressions, identical on both products) to the two-tierMcpToolGuidedescription. Each product now serves a terse head under the 620-character budget fortools/list, and the full original prose becomes theget_tool_guidetail. The heads are byte-identical onboth products (D6 lockstep).
Darling/PerformanceMonitor.Darling.Service/Mcp/DarlingMcpQueryStoreRegressionTools.csLite/Mcp/McpQueryTools.csDarling/Darling.Tests/McpToolGuideHeads.QsRegressions.cs,Lite.Tests/McpToolGuideHeads.QsRegressions.csDarling/Darling.Tests/McpToolsListBudget/DarlingMcpQueryStoreRegressionTools.txt,Lite.Tests/McpToolsListBudget/McpQueryTools.txtDarling/Darling.Tests/McpZeroIsAMeasurementTests.cs(see "Re-pointed pins" below)No topics were needed. Nothing in this tool's guidance repeats across another tool in this lane.
Before/after served characters
Corrections
None. The original prose matched the tool body on both products. I checked this against
DarlingQueryStoreRegressionReader.cs's SQL: the CPU-only 25% gate, theORDER BY additional_duration_ms DESCranking key, and the
DurationRegressionPercent is nullseverity condition.D4 removals
None. The served description carried no issue references and no anecdotes. Only the doc comments above the
tool, which are never served, mention
#2484and#3541.Re-pointed pins
McpZeroIsAMeasurementTests.TheRegressionTool_SaysWhyAPercentIsNull_AndDoesNotBandAMissingRatioused to assertthat the whole (then-unsplit) description contained
undefined_percentsandnull percent never sorts as 0.Both phrases moved to the tail, so I re-pointed the test in two parts:
null, not 0%, when their baseline is 0andadditional_duration_ms is the ranking key.TailOfhelper, mirroring the file's existingDescriptionOf), it asserts the twoliteral phrases still ride there.
Reason: the field name
undefined_percentsand the literal ranking-key sentence are implementation detail.They do not fit in the 620-character head beside this tool's three other guardrails. Those are the CPU gate, the
contrast with
get_query_store_top, and the three status words that a zero-row answer can carry. An earlierlane used the same pattern for the health-parser pins, narrowed to
source_observedalone, so this followsprecedent already in the repository.
Status routes
Both products route a zero-row answer through the same four checks, in this order:
not_collectedDarlingMcpQueryStoreRegressionTools.cs:165McpQueryTools.cs:461query_storecollector.unavailableDarlingMcpQueryStoreRegressionTools.cs:167McpQueryTools.cs:463unavailableDarlingMcpQueryStoreRegressionTools.cs:174McpQueryTools.cs:470emptyDarlingMcpQueryStoreRegressionTools.cs:181McpQueryTools.cs:477emptyDarlingMcpQueryStoreRegressionTools.cs:186McpQueryTools.cs:482errorDarlingMcpQueryStoreRegressionTools.cs:124McpQueryTools.cs:421McpHelpers.FormatError.D9 traps
Q:
get_query_store_regressionsandget_query_store_topboth rank queries. Which one finds the query that got worse since yesterday?Correct:
get_query_store_regressions.get_query_store_topranks what is expensive now, and the most expensive query is usually the one that always was.Prevents it: "get_query_store_top ranks EXPENSIVE, this ranks CHANGED."
Q: A row shows
duration_regression_percent: null. Does that mean duration held steady?Correct: No. Null means the baseline duration was 0, so there is no denominator. It does not mean nothing changed. It can be the largest possible regression.
Prevents it: "duration_regression_percent and io_regression_percent are null, not 0%, when their baseline is 0"
Q:
severityis null on a row. Does that mean the row is low severity?Correct: No.
severityis null exactly whenduration_regression_percentis null, because there is no duration ratio to band. It is not a "LOW" verdict.Prevents it: "severity is null with the former"
Q: I want the worst regression first. Is
cpu_regression_percentthe field to sort the rows by?Correct: No. Sort by
additional_duration_ms, the tool's own ranking key. A percent can be null while this key always exists.Prevents it: "additional_duration_ms is the ranking key."
Q: The tool returns zero regressions with status empty. Is that always good news?
Correct: Not always. It can also mean a baseline exists, but nothing was collected in the recent window. That is a collection gap, not a clean bill of health.
Prevents it: "empty: no regression (all clear), or a baseline with nothing yet in the window."
Q: status is "unavailable". Can the head alone tell "never collected" apart from "collected, but all of it newer than the window"?
Correct: No, it does not distinguish them: unavailable covers both, because both mean no baseline exists yet.
Prevents it: "unavailable: no baseline exists yet."
Q: status is "not_collected". Is that a reason to go check whether the Query Store collector crashed?
Correct: No.
not_collectedmeans this server's engine cannot run Query Store at all. That is permanent, not an outage to chase.Prevents it: "not_collected: this server's engine cannot run Query Store."
Q: With
hours_back=24and no baseline found, was the baseline just the 24 hours before that?Correct: No. The baseline is every capture ever collected before the start of the recent window, not a fixed lookback.
Prevents it: "recent window (hours_back, ending at as_of) vs baseline, every capture before it."
Q: A row has
io_regression_percentat 400% butcpu_regression_percentat 10%. Is this row returned at all?Correct: No. Only average CPU regressing over 25% gates a row into the result. A large I/O or duration regression alone does not qualify it.
Prevents it: "Gated: average CPU regressed over 25%."
Q: Raising
hours_backfrom 24 to 72 only changes the recent window, right?Correct: No. Baseline is everything before the recent window starts, so widening
hours_backgrows the recent window and shrinks the baseline at the same time.Prevents it: "recent window (hours_back, ending at as_of) vs baseline, every capture before it."
Q:
severityreads "HIGH" on a row further down the list than one with no severity at all. Is the list sorted by severity?Correct: No. The list is sorted by
additional_duration_ms.severityis a separate, duration-only band that can be null even on a row near the top.Prevents it: "additional_duration_ms is the ranking key." plus "severity is null with the former"
Q:
io_regression_percentis null on a row that also has a numericduration_regression_percent. Does that change whatseveritymeans for the row?Correct: No.
severityfollowsduration_regression_percentonly. Whetherio_regression_percentis null never touches it.Prevents it: "severity is null with the former"
Q: (added by the coordinator) A call has
as_ofset to a day last month. It answers statusempty,because nothing was collected inside that past window. Can the same call fill in if you wait an hour?
Correct: No. The window is in the past, and nothing was collected in it.
Prevents it: "empty: no regression (all clear), or a baseline with nothing yet in the window."
Q: (added by the coordinator) A call with
as_ofset to a day last month answers statusunavailable. Can adifferent
hours_backchange the answer?Correct: Yes. The baseline is every capture before the window, so a shorter window can give it one.
Prevents it: "unavailable: no baseline exists yet."
Test plan
dotnet build Darling/Darling.Tests/Darling.Tests.csproj -c Debug- Build succeeded, 0 Warning(s), 0 Error(s).dotnet build Lite.Tests/Lite.Tests.csproj -c Debug- Build succeeded, 0 Warning(s), 0 Error(s).McpToolGuideTests,McpToolsListBudgetTests,McpZeroIsAMeasurementTests,McpToolGuideHeadsQsRegressionsTests,DarlingQueryStoreRegressionsTests,McpPayloadContractCensusTests-Total: 68, Failed: 0.
McpToolGuideTests,McpToolsListBudgetTests,McpToolGuideHeadsQsRegressionsTests,McpMissMessageParityPinTests,QueryStoreRegressionsToolTests- Total: 83, Failed: 0.not_collectedhead sentence, sawMcpToolGuideHeadsQsRegressionsTestsfail,restored it, and saw it pass again.
python plans/3898-d9/dropcheck.py origin/dev HEAD get_query_store_regressions-missing=0on both products.git merge origin/dev- clean, no conflicts. It pulled in the unrelated PR get_resource_semaphore: add interval_seconds beside sample_interval_seconds (#3653 item 17) #4103 memory-grant changes.Darling.Tests.exe, no rig, live-PG classes skip): Total: 13,321, Failed: 2, Skipped: 636.Both failures are in
DarlingInstallLocationTests(Windows ACL / install-tree-lock tests). They are the known pair from Install scripts: the service account gets Read & Execute on the install root and Modify only where it writes (#4052) #4090.They fail on any non-elevated local run, and CI runs elevated. This PR touches no install-tree file.
Lite.Tests.exe): Total: 5,263, Failed: 0, Skipped: 0.body_4109.md: 29 hit(s), doc mode. The remaining hits are 28 D9 labels and the attribution emoji,which are allowed.
Coordinator verification
origin/dev(74489820) into the branch. The dropped-sentence check showsmissing=0on both products. No parameter description changed.as_of - hours_backtoas_of, and the baseline is every capture before the window starts. The CPU gate is> 25in the WHERE clause. Rows sort byadditional_duration_ms DESC. The duration and I/O percents divide byNULLIF(baseline, 0). The CPU percent cannot arrive null, because the gate drops a NULL comparison, so naming only the duration and I/O percents is exact.severityis null when the duration percent is null. Every head fact is correct. Head facts fixed: 0.McpZeroIsAMeasurementTestspin: the head still carries both halves of the guardrail.null, not 0%, when their baseline is 0says a null percent is undefined and is not zero.additional_duration_ms is the ranking keysays a null percent never enters the sort. The two literal phrases stay pinned in the tail. D9 Q2, Q4 and Q11 show that a reader gets this right.DarlingInstallLocationTestsfailures with their known cause. I added the checker line and the attribution footer.Handoff
Nothing is deferred. The lane's one twin pair is fully converted, green, and pinned. No topics were needed for
this lane. The two
DarlingInstallLocationTestsfailures are a knownlocal test bug from #4090. A separate fix is in progress.
🤖 Generated with Claude Code
https://claude.ai/code/session_01QQh8LczP1HGLXQYAx4oTZC