Repository navigation
get_active_queries moves reading guidance off tools/list (#3898) - #4107
Merged
Merged
Conversation
Both SKUs' get_active_queries carried a 1.4k-character description in tools/list. The head now states the guardrail facts (SQL-side filters, null-for-zero numeric fields, the blocker_not_shown codes, and that a filtered empty does not prove nothing was ever collected) in 613 served characters; get_tool_guide serves the rest, verbatim, on demand. blocking_only's parameter description was trimmed from 238 to 134 chars to clear the 200-char cap; the elaboration it lost moved verbatim into the tool's own tail. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QQh8LczP1HGLXQYAx4oTZC
…eir window scope (#3898) The head named only the filtered empty, in the negative. It now says what an empty answer means (no snapshot in the window, or none matching the filters) and that not_collected answers only unfiltered calls on an engine that can't run the collector. blocker_not_shown is named as the victim's field. Served head 613 -> 616 on both products; pins and budget follow. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NUU29PuGg9TUFBsACgZg2K
erikdarlingdata
marked this pull request as ready for review
September 24, 2026 02:29
erikdarlingdata
enabled auto-merge (squash)
September 24, 2026 02:29
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
get_active_queriesput a 1,406-character description (1,421 on Lite) into everytools/listresponse on bothproducts. That text carries guardrail facts a model needs to read the answer correctly. It now moves to a short
head plus a
get_tool_guide-served tail, per #3898.What changes
Darling/PerformanceMonitor.Darling.Service/Mcp/DarlingMcpSessionTools.cs:get_active_queries's descriptionsplit into a 616-character served head (585 chars plus the 31-character pointer) and a tail that keeps every
original sentence.
Lite/Mcp/McpSessionTools.cs: the same split, byte-identical head, Lite's own tail (it namesget_blocked_process_reportswhere Darling's namesget_blocking, exactly as the original prose did on eachproduct).
blocking_only's parameter description was 238 characters, over the 200-character cap for a converted tool. Theguardrail clause stayed on the parameter, cut to 134 characters. It reads: "rows with blocking_session_id > 0,
plus the head blockers those rows name in the same capture." The rest moved to the tool's tail: "Applied in SQL,
so total_snapshots counts the blocking population and truncated is measured against it."
Darling/Darling.Tests/McpToolsListBudget/DarlingMcpSessionTools.txtandLite.Tests/McpToolsListBudget/McpSessionTools.txt.McpToolGuideHeads.SqlCore.csfiles. This familyalready held classes for other tools, including
analyze_server,compare_analysis, and the daily-summarypair, from earlier The MCP surface costs ~100k tokens of context before the first call: 157 tools, 336 KB of tools/list, 86 KB of instructions #3898 lanes. The new class is
McpToolGuideHeadsSqlCoreActiveQueriesTests, in bothDarling/Darling.Tests/andLite.Tests/.PerformanceMonitor.Common/Mcp/McpToolGuideTopics.SqlCore.cswas not touched.get_active_queriesis the onlytool in this lane. No prose repeats across two or more tools, so no topic was needed.
Served-chars table
Corrections
None. The original prose matched the tool body on both products.
D4 removals
None. The original description carried no issue references and no anecdotes.
Re-pointed pins
None needed. Every test file that mentions
get_active_queriespins something structural: the tool's name, itsparameter roster, or its page and filter behavior. None pins a substring of the description prose. The files are
DarlingMcpSessionToolsTests.cs,McpFilterSemanticsLivePostgresTests.cs,McpPageContractTests.cson bothproducts,
CrossAppMcpToolInventoryPinTests.cs, andMcpMissMessageParityPinTests.cs. All pass unchanged.Status routes
get_active_queriescan answerempty,not_collected, orerror. Darling and Lite route them the same way,one server call apart:
empty(filtered miss)DarlingMcpSessionTools.cs:132McpSessionTools.cs:46rows.Count == 0anddatabase_nameorblocking_onlywas set. Reported directly, with no check of whether the collector can run on this engine or has ever produced a row.not_collected, elseempty(fallback)DarlingMcpSessionTools.cs:139-140McpSessionTools.cs:53-54rows.Count == 0, no filter set.NotCollectedStatusAsyncruns first. It answersnot_collectedonly when thequery_snapshotscollector cannot run on this server's engine kind, for example a PostgreSQL target. Otherwise it falls through toempty.errorDarlingMcpSessionTools.cs:200McpSessionTools.cs:113McpHelpers.FormatError.The head's status sentence is
Empty: no snapshot in the window, or none matching filters; not_collected (unfiltered only): engine can't run it.It restates both rows' conditions on both products. The filtered branch never runs thenot_collectedcapability check that the unfiltered branchruns. A target that can never run this collector still answers the ordinary filtered-miss
emptymessage, notnot_collected.D9 traps
get_active_queriesreturnsgranted_query_memory_gb: 2.5on a row. Is that value in megabytes orgigabytes?
Correct: Gigabytes.
Prevents it: "memory grant GB"
hours_back=6andas_ofset to a past timestamp. Does the window run forward fromas_of,or end at it?
Correct: It ends at
as_of. The six hours are the six hours immediately before that timestamp.Prevents it: "over a window ending at as_of"
database_name="Sales"returnstotal_snapshots: 40. Is 40 the count for the whole server'swindow, or only for Sales?
Correct: Only for Sales. The filter runs in SQL before the count is taken.
Prevents it: "database_name/blocking_only filter IN SQL: total_snapshots is the filtered count."
truncated: truewithtotal_snapshots: 80and the defaultlimitof 50. Doestruncateddescribe thewhole
hours_backwindow, or the filtered, paged count?Correct: The filtered count: 80 filtered rows exceed a limit of 50, whatever the raw window held.
Prevents it: "truncated means over limit."
wait_time_ms: nullwhilewait_typeis populated. Does the null mean this field failed tocollect?
Correct: Not necessarily.
wait_time_msis null both for a true zero and for "not applicable."Prevents it: "wait_time_ms, dop, granted_query_memory_gb, open_transaction_count: null = zero or not applicable."
granted_query_memory_gb: nullon a row that shows a memory-grant wait type. Does that prove no memorywas granted?
Correct: No, the same field collapses a genuine zero grant and "not applicable" to the same null.
Prevents it: "null = zero or not applicable."
open_transaction_count: null. Does that guarantee zero open transactions right now?Correct: Not from that field alone. It is null both for zero and for not-applicable.
Prevents it: "null = zero or not applicable."
WAITFORshell with no blocking activity of its own. Does it get filtered out as noise?Correct: No. If another row in the same capture names it as the blocker, it stays on the page whatever its
own text is.
Prevents it: "Head blockers are never stripped (is_head_blocker)."
blocker_not_shown: "filtered"on a victim row. Does that mean the victim row itself was excluded byyour
database_namefilter?Correct: No, the victim row is on the page. "filtered" means its blocker, a different row, was excluded.
Prevents it: "a victim's blocker_not_shown is not_captured, filtered, or past_page."
blocker_not_shown: "past_page". Does that mean the blocker was never captured by the collector?Correct: No. "past_page" means the blocker passed every filter and is in the population, just beyond
limit. "not_captured" is the separate code for a blocker that held no running request at all.Prevents it: "a victim's blocker_not_shown is not_captured, filtered, or past_page."
blocker_not_shownis absent on a row. Does that always mean the row is not blocked?Correct: Not by itself. It is also absent when the row is blocked but its blocker IS on the page. Check
blocking_session_idto tell the two apart.Prevents it: "a victim's blocker_not_shown is not_captured, filtered, or past_page."
blocking_only=truereturnsstatus: "empty". Does that prove this server's active-query collector hasnever captured anything, ever?
Correct: No. The filtered branch answers
emptypurely from what matchedblocking_onlythis call. Itnever checks whether the collector has ever produced a row, or whether this engine can run it at all.
Prevents it: "Empty: no snapshot in the window, or none matching filters"
database_nameon a later call. Doestotal_snapshotsstay computed on the same unfilteredpopulation while only the returned rows shrink?
Correct: No, both
total_snapshotsandtruncatedare computed on the filtered population together, sonarrowing the filter changes both.
Prevents it: "database_name/blocking_only filter IN SQL: total_snapshots is the filtered count."
status: "empty". Does that mean this server'sactive-query collector has never captured anything?
Correct: No. It means no snapshot in the window. The unfiltered branch checks the engine first and answers
not_collectedwhen this engine cannot run the collector.Prevents it: "Empty: no snapshot in the window, or none matching filters"
emptywhen it finds no rows?Correct: Yes. An unfiltered call on an engine that cannot run the collector answers
not_collected.Prevents it: "not_collected (unfiltered only): engine can't run it."
Test plan
Darling/Darling.Tests/Darling.Tests.csproj: Build succeeded, 0 Warning(s), 0 Error(s).Lite.Tests/Lite.Tests.csproj: Build succeeded, 0 Warning(s), 0 Error(s).McpPayloadContractCensus*, DarlingMcpSessionToolsTests, McpFilterSemanticsLivePostgresTests,
McpPageContractTests): Total 249, Failed 0, Skipped 8 (live-PostgreSQL, no rig).
McpMissMessageParityPinTests): Total 154, Failed 0, Skipped 0.
McpToolGuideHeadsSqlCoreActiveQueriesTestsfail, thenrestored it and watched it pass again.
get_active_queries old=1406 new=2103 sentences=6 missing=0.get_active_queries old=1421 new=2118 sentences=6 missing=0.git merge origin/dev: clean, no conflicts (Darling install-script files, unrelated to this change).DarlingInstallLocationTests.TheInstallTreeLock_ClosesTheInheritedGrant_KeepsProtectedFiles_AndReportsWhatItCannotCloseand
ThePreLockWritableExtractionCheck_TrustsTheAccountTheLockItselfGranted_OnAnAlreadyLockedTree. Both arepre-existing and unrelated to this PR. They live in a file this PR never touches.
git logshows they camefrom the
origin/devmerge commitc51706c9(Install scripts: the service account gets Read & Execute on the install root and Modify only where it writes (#4052) #4090, an install-script permissions change). Re-run alone,in isolation, they fail the same way, so this is not a suite-ordering artifact of this branch.
body_4107.md: 31 hit(s), doc mode. The remaining hits are 30 D9 labelsand the attribution emoji, which are allowed.
Coordinator verification
origin/dev(bbd7eec6) into the branch. The dropped-sentence check showsmissing=0on both products. The parameter check showsblocking_only238 -> 134 withmissing=0, so the text it lost is verbatim in the tail.McpToolGuideHeads.SqlCore.csfiles are a pure append: 64 and 65 added lines, 0 removed, in one hunk at the end of each file.as_of - hours_backtoas_of. Both filters run inside the SQL, andtotal_snapshotsis that statement'sCOUNT(*) OVER ().truncatedcomes from alimit + 1fetch. The four named fields servex > 0 ? x : null. The reader maps a database NULL to 0 first, so a null covers a zero and a missing value.blocker_not_shownis set on the victim's row.not_collected(an unfiltered call on an engine that cannot run the collector) were missing. The new clause isEmpty: no snapshot in the window, or none matching filters; not_collected (unfiltered only): engine can't run it.a hidden blocker's blocker_not_shownbecamea victim's blocker_not_shown, because the field sits on the victim's row. The null sentence becamewait_time_ms, dop, granted_query_memory_gb, open_transaction_count: null = zero or not applicable.The opening now says(sys.dm_exec_requests) in a window.DarlingInstallLocationTestspair 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.Handoff
Nothing deferred.
get_active_queriesis the only tool in this lane's brief and it is fully converted, green, andcommitted on both products. The 2 pre-existing
DarlingInstallLocationTestsfailures above are outside this PR'sscope. They belong to already-merged PR #4090. The #3898 coordinator reported them to Erik as a known local test bug.
One process note for the coordinator.
McpToolGuideHeads.SqlCore.csis a shared file. It already held classes forother #3898 lanes' tools before this PR touched it:
analyze_server,compare_analysis,get_analysis_facts,get_analysis_findings,audit_config,mute_analysis_finding,get_daily_summary, andget_daily_summary_range.This PR appends a new class,
McpToolGuideHeadsSqlCoreActiveQueriesTests, after the last one. It does not modifyany existing class. Worth calling out in future
sqlCore-family briefs so a lane reads the file before writing toit.
🤖 Generated with Claude Code
https://claude.ai/code/session_01QQh8LczP1HGLXQYAx4oTZC