Repository navigation
get_long_query_completions moves reading guidance off tools/list (#3898) - #4122
Merged
Merged
Conversation
Refs #3898. Converts get_long_query_completions (a twin, identical on both products) to the head/tail split: tools/list now serves a 617-char head (down from 1,037) plus a pointer to get_tool_guide, which serves the full original text unchanged. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QQh8LczP1HGLXQYAx4oTZC
…, and empty covers a collector switched off D9 round 1 read truncated=true as "some of the very slowest are missing". The page is ranked duration DESC, so a truncated page cut rows no slower than the page; the head now says so. "empty can mean none in the window, or never enabled" was too narrow: a collector switched off yesterday also leaves the last 24 hours empty, so the clause now reads "or the collector was off". Head 573, served 604 on both products. 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 03:58
erikdarlingdata
enabled auto-merge (squash)
September 24, 2026 03:58
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_long_query_completionsserved the same 1,037-character description on both Darling and Lite. That text isthe full reading guidance a model needs. Without it, a model can misread the page as newest-first instead of
slowest-first. It can misread a null duration as a bad row instead of an attention, or an empty answer as proof
the collector ran. This PR moves that guidance off
tools/listand behindget_tool_guide. Nothing is deleted.What changes
get_long_query_completionsconverts on both products. It is a twin:desc.pyconfirmed the served text wasbyte-identical before this PR. The generic cross-SKU lockstep test confirms the new heads still match.
Darling/Darling.Tests/McpToolGuideHeads.LongQuery.csandLite.Tests/McpToolGuideHeads.LongQuery.cs.get_long_query_completionstool line inDarlingMcpLongQueryTools.txtandMcpLongQueryTools.txt(1,037 to 604). No parameter line changed: every parameter description was alreadyunder the 200-char cap, and none were touched (D8).
Description(...)string moved.Served characters (before / after)
Corrections
None. The original text's claims (page ranking, the empty-answer advice, the opt-in default) all checked out
against the tool body and its status-routing helpers.
D4 removals
None. The description carried no issue references and no anecdotes.
Re-pointed pins
None found. A
git grepfor this tool's description text (SLOWEST,completions_returned,duration-RANKED) found nothing inMcpDescriptionTruthPinTests,McpZeroIsAMeasurementTests, orMcpPayloadContractCensusTestson either product. The only existing references to this tool, inMcpPageContractTestsandRuntimePreconditionMissTests, check tool shape and status routing, not descriptiontext. Neither needed a change.
Status routes (cited from the code)
The head states one status word literally:
empty. The table below also carries the two routes that run beforeit. The head's phrase "can mean none in the window, or the collector was off" is scoped to
emptyonly. It does notclaim to cover a
not_collectedorpreconditionanswer.not_collectedrows.Count == 0AND the server's probed engine cannot run this collector at all.preconditionrows.Count == 0,not_collecteddid not fire, AND the collector's last recorded run named a specific problem (missing capture session, missing extension, or a degraded/non-fatal skip).emptyrows.Count == 0and neither route above fired: either the collector ran clean with no qualifying rows in the window, or it was off: never enabled (nocollection_logrow at all, the default for an opt-in collector), or switched off since, with a last recorded run that named no problem.errorMcpHelpers.FormatError, on both products.D9 traps
oldest_returned_event_timeandnewest_returned_event_timeare two minutes apart inside a 24-hourwindow. Does that mean the read only covered those two minutes?
Correct: No. The page is ranked by duration, not time, so the two stamps only describe how old the
returned slowest rows are. A short, busy spike can hold every one of the window's slowest completions while
the underlying read still covered the full 24 hours.
Prevents it: "THE PAGE IS THE window's limit SLOWEST, NOT ITS NEWEST"
newest_returned_event_timeis well before the window's end (as_of). Does that mean nothing ran inthe most recent part of the window?
Correct: No. It means nothing recent enough made the slowest page. Recent completions can exist but rank
below the limit-th slowest, or run too fast to qualify at all.
Prevents it: "THE PAGE IS THE window's limit SLOWEST, NOT ITS NEWEST"
truncatedis true. Does that mean the page is missing some of the window's very slowest completions?Correct: No, the opposite. Because the page is duration-ranked, a truncated page still holds the window's
slowest rows. What got cut ranked below the limit-th slowest, never above it.
Prevents it: "truncated means the window held more, none slower than the page"
completions_returnedis 30, the default limit. Does that number alone say whether the window heldexactly 30 qualifying completions, or more?
Correct: No, not by itself.
completions_returnedcaps atlimitonce the window has at least that many.truncated: falsemeans that was the whole window.truncated: truemeans there were more.Prevents it: "This is what bounds the page — read truncated to know whether the window held more." (the
limitparameter)duration_ms: nullwhile every other field is populated. Is that a bad or incompletecapture?
Correct: No. It is an attention (a client cancel or query timeout), which has no duration by definition
and is expected to sort after every timed completion.
Prevents it: "attentions (cancels/timeouts), ranked duration DESC, attentions (no duration) last"
resultis "Abort". Is that the same kind of event as an attention?Correct: No. An Abort result belongs to a completed rpc/batch event whose query was cancelled mid-run and
still has a duration. An attention is a separate event type with no duration at all. Both relate to
cancellation, but they are different captures.
Prevents it: "attentions (cancels/timeouts), ranked duration DESC, attentions (no duration) last"
status: empty. Does that prove the long_query_completions collector is enabled andsaw nothing this window?
Correct: No. Empty is also the answer when the collector was off: never turned on (it is opt-in and OFF by
default), or switched off since. The answer cannot tell "ran clean" apart from "never ran" beyond what it already says.
Prevents it: "Collector is opt-in, OFF by default: empty can mean none in the window, or the collector was off"
status: precondition, notempty. Does the emptyanswer's "enable it in the schedule" advice still apply?
Correct: Not directly.
preconditionmeans the collector does have a recent recorded run. That run nameda specific problem: its capture session or extension is missing, or it is degraded. The fix it names can
differ from just flipping the schedule switch. That advice is scoped to the
emptystatus word, not everyno-data answer.
Prevents it: "empty can mean none in the window, or the collector was off"
limit=100was requested and the response holds only 12 completions withtruncated: false. Does theshort page mean the read failed partway through?
Correct: No.
limitis a ceiling, not a target. A quiet window can legitimately hold fewer qualifyingcompletions than requested, and
truncated: falseconfirms that is the complete count.Prevents it: "truncated means the window held more, none slower than the page"
turned that on, does it answer
status: unavailable?Correct: No,
unavailableis not one of this tool's routes for a missing trace. A never-enabled, opt-incollector answers
status: empty, naming the specific collector to turn on, not a server-wide unavailable.Prevents it: "Collector is opt-in, OFF by default: empty can mean none in the window, or the collector was off"
hours_backis 24 (default) and the answer is empty. If it goes to 168 (a week), does that reliablyturn up a non-empty answer when the collector is off?
Correct: No. A wider window only reaches rows captured while the collector was on. If it was never
enabled, every window comes back empty, because nothing was captured at any
hours_back.Prevents it: "Collector is opt-in, OFF by default: empty can mean none in the window, or the collector was off"
duration_ms: 45000first, then the next row showsduration_ms: 100. Isthe ordering broken?
Correct: No. Every timed row sorts strictly by duration, descending. A big drop between adjacent rows is
normal once you are past the true outliers. It is not a sign the sort failed.
Prevents it: "ranked duration DESC, attentions (no duration) last"
Coordinator verification
emptyroute. Take acollector switched off yesterday whose last run named no problem. It also answers
emptyfor the lastday. The
preconditionroute only fires on a recorded problem. The clause now reads "orthe collector was off".
relied on "whether the window held more". The head now says: "truncated means the window held more, none
slower than the page." It replaces: "completions_returned/truncated say how many you got and whether
the window held more."
completions_returnedstays in the tail, and thelimitparameter still says to readtruncated.
entry are updated to match.
yesterday): 1 misread (Q3). Q6, Q8, Q9, Q10 and Q12 were answered "can't tell". The rest were correct.
last row took 2000 ms. Can a cut row have taken 9000 ms? Round 2 found 0 misreads. Q3, Q7 and Q13 were
correct, and Q99 was answered "can't tell".
McpToolGuide*,McpToolsListBudget*,McpDescription*,McpZeroIsAMeasurement*,McpPayloadContractCensus*,MeasurementContractCensus*,McpPageContract*,*LongQuery*): 272 total, 0 failed, 8 skipped.RuntimePreconditionMiss*: 3 total, 0 failed, 3 skipped.Lite, same classes: 117 total, 0 failed.
Test plan
Darling.TestsandLite.Tests: bothBuild succeeded, 0 Warning(s), 0 Error(s).McpToolGuide*,McpToolsListBudget*,McpDescription*,McpZeroIsAMeasurement*,McpPayloadContractCensus*): Darling 206 total, 0 failed, 4 skipped. Lite 62total, 0 failed.
get_long_query_completions: DarlingMcpPageContractTestsplusRuntimePreconditionMissTests: 43 total, 0 failed. LiteMcpPageContractTests: 22 total, 0 failed.McpToolGuideHeadsLongQueryTestsfailed (1 failed), restored it, confirmed green again.get_long_query_completions[Darling] old=1037 new=1634 sentences=6 missing=0. [Lite]old=1037 new=1634 sentences=6 missing=0.
git merge origin/dev(merge commit 23615e6):Darling.TestsTotal 13335,Errors 0, Failed 2, Skipped 636. Both failures are the known non-elevated
DarlingInstallLocationTests(ThePreLockWritableExtractionCheck_...andTheInstallTreeLock_...) thebrief calls out. They are unrelated to this change.
Lite.TestsTotal 5268, Errors 0, Failed 1. The one failure,AnalysisPassTokenThreadingTests.TheReadLockWaitIsAbandonableWhileAWriterHoldsIt, is a threading/locktest in Lite's analysis pass with no relation to this PR's file. Re-run alone 3 times, it passed every
time. It only fails under full-suite concurrency. See Handoff.
Handoff
AnalysisPassTokenThreadingTests.TheReadLockWaitIsAbandonableWhileAWriterHoldsItflake(passes in isolation, fails under full-suite load) is unrelated to this PR. It fits the wave's flake census
better than this lane's narrow scope.
🤖 Generated with Claude Code
https://claude.ai/code/session_01QQh8LczP1HGLXQYAx4oTZC