Repository navigation
analyze_query_plan moves reading guidance off tools/list (#3898) - #4126
Merged
Merged
Conversation
Splits analyze_query_plan's served tools/list description into a terse head (<=620 served chars) plus a get_tool_guide tail carrying the full original prose, on both Darling and Lite. Adds McpToolGuideHeads.AnalyzeQueryPlan.cs on both products and updates each product's DarlingMcpPlanTools.txt / McpPlanTools.txt budget for analyze_query_plan only. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QQh8LczP1HGLXQYAx4oTZC
…head via the shared helper (#4115) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NUU29PuGg9TUFBsACgZg2K
…3898 D9) The three plan tools (analyze_plan_xml #4115, analyze_procedure_plan #4117, analyze_query_plan here) now share one guardrail block verbatim in their heads. Replaces lane e1's own head wording with the ruled block: per statement warnings, missing_indexes/impact_basis/create_statement caveats, parameters, memory_grant, top_operators. Adds the null-not-zero actual_* sentence the tool's head needs on top of that shared block. Verified against the code on both products before writing the head: - "latest plan" is collection_time DESC LIMIT 1 on both Lite (LocalDataService.QueryStats.cs) and Darling (DarlingStoredPlanReader.QueryStatsPlanXmlByHashSql). - Both route not_collected before falling back to unavailable (McpPlanTools.cs / DarlingMcpPlanTools.cs AnalyzeQueryPlan). - actual_rows/actual_elapsed_ms/actual_cpu_ms are (long?)null, not 0, without HasActualStats (McpPlanAnalysisFormatter.cs), shared by both products. Tails are untouched: both already carried every pre-#3898 sentence verbatim, so dropcheck reports missing=0 without any tail edit. Updates McpToolGuideHeads.AnalyzeQueryPlan.cs (both products) to pin the new head's guardrail facts instead of the old ones, and the tools/list budget lines (613 -> 616, the new served length) in both products' McpToolsListBudget files. Does not touch Lite.Tests/McpDescriptionTruthPinTests.cs or Darling/Darling.Tests/DarlingMcpPlanToolsTests.cs -- their existing plan-tool pins already match this head's fragments verbatim. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NUU29PuGg9TUFBsACgZg2K
erikdarlingdata
marked this pull request as ready for review
September 24, 2026 04:51
erikdarlingdata
enabled auto-merge (squash)
September 24, 2026 04:51
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
analyze_query_planis a twin tool served on both products. Both descriptions were over the ~800-charthreshold this wave converts (Darling 915 served chars, Lite 903). Splitting them moves reading guidance off
tools/listinto a per-tool guide thatget_tool_guideserves on demand. Nothing is deleted.What changes
Darling/PerformanceMonitor.Darling.Service/Mcp/DarlingMcpPlanTools.csandLite/Mcp/McpPlanTools.cs:analyze_query_plan's head is now the shared plan-tool guardrail block, ruled final and shared verbatim withanalyze_plan_xml(analyze_plan_xml moves reading guidance off tools/list (#3898) #4115, merged) andanalyze_procedure_plan(analyze_procedure_plan moves reading guidance off tools/list (#3898) #4117). This replaces the lane's own earlierhead wording. The
<<GUIDE>>marker and the entire original tail are unchanged on both products. Only thetext before the marker moved.
Darling/Darling.Tests/McpToolGuideHeads.AnalyzeQueryPlan.csandLite.Tests/McpToolGuideHeads.AnalyzeQueryPlan.cs:rewritten to a
HeadFactsarray, the same patternanalyze_procedure_plan's own head-pin files use. It pinsthe new head's guardrail facts instead of the old ones.
analyze_query_planline only, 613 -> 616 (the corrected served length), in bothDarling/Darling.Tests/McpToolsListBudget/DarlingMcpPlanTools.txtandLite.Tests/McpToolsListBudget/McpPlanTools.txt.No other tool's line in either file changed.
Lite.Tests/McpDescriptionTruthPinTests.csandDarling/Darling.Tests/DarlingMcpPlanToolsTests.cs: nottouched by this PR. Both files' existing plan-tool pins pass unmodified against the new head. Every fragment
they check is verbatim in the new shared block. That covers the impact_basis label, the create_statement
sentence, the corroboration sentence, the fixed caveat, and the regression-risk clause. See "Coordinator
verification" below.
4126-reported.txt, outside the repo) corrected: 613 -> 616.Served-chars table
The head is byte-identical on both products: 585 chars, served 616 with the 31-char pointer. It was built from
one set of string literals applied to both files. It was then confirmed with
desc.py analyze_query_plan, andwith the generic
McpToolGuideTests.EverySharedToolName_CarriesTheMarkerOnBothSkus_OrNeither_WithByteIdenticalHeadstest in
Darling.Tests, which scans both SKUs' source. Both passed.Corrections
None found in the tail. The original prose was checked against
AnalyzeQueryPlanon both products,McpPlanAnalysisFormatter.BuildAnalysisResult,DarlingEngineCapability.NotCollectedStatusAsync, andCollectorEngineCapability.NotCollectedMessage. All of it held up. This pass re-checked the "latest plan"ORDER BY and the not_collected/unavailable route order against current code for the new head. See the file:line
citations under "Coordinator verification."
D4 removals
None. The original sentences on each product carry no issue references and no anecdotes, so nothing needed to
come off the wire. The tail is the full original text, unchanged by this pass. Only the head text changed.
Dropcheck confirms this:
missing=0on both products (see Test plan).Coordinator verification
This PR was at the D9 finish-pass stage. The shared plan-tool guardrail ruling applies here, and a prior edit
to the shared test helper needed to be reverted. That work is done.
analyze_query_plan's head is now the same 419-char guardrail block, verbatim(including the U+2014 dash), that
analyze_plan_xmlandanalyze_procedure_plancarry. The block was copiedfrom
analyze_procedure_plan's already-ruled head, not re-derived, and split across source lines the sameway: one concatenated string per sentence.
Lite.Tests/McpDescriptionTruthPinTests.cstodev's version before this pass started. This pass does not touch it, or its Darling twinDarlingMcpPlanToolsTests.cs. Both files' existing plan-tool pins pass unmodified (see Test plan).New head (585 chars, served 616, identical on both products):
Every head fact checked against the code, file:line on each product:
collection_time DESC LIMIT 1.Lite:
Lite/Services/LocalDataService.QueryStats.cs:667. Darling:Darling/PerformanceMonitor.Darling.Service/Mcp/DarlingStoredPlanReader.cs:70, theQueryStatsPlanXmlByHashSqlconstant, read fromGetQueryStatsPlanXmlByHashAsyncat line 122.NotCollectedStatusAsyncfirst.unavailableis only the fallback when that call returns null.Lite:
Lite/Mcp/McpPlanTools.cs:28-32callingLite/Mcp/McpEngineCapability.cs:43-69. Darling:Darling/PerformanceMonitor.Darling.Service/Mcp/DarlingMcpPlanTools.cs:59-63callingDarling/PerformanceMonitor.Darling.Service/Mcp/DarlingEngineCapability.cs:62-82. Both return thenot_collectedenvelope only when the collector cannot run on the server's probed engine. A registry readthat fails answers null on both products, falling through to the ordinary
unavailablemiss, not a thirdstatus.
create_statementcorroboration andnever-a-diagnosis caveat, and the fixed per-row caveat naming regression risk and write cost. Copied verbatim
from
analyze_procedure_plan's ruled head, not re-derived for this tool.PerformanceMonitor.PlanAnalysis/McpPlanAnalysisFormatter.cs:129-131.actual_rows,actual_elapsed_ms,and
actual_cpu_msare each(long?)nullwhenHasActualStatsis false, never0.analyze_query_plananalyzes one stored plan byquery_hash. Itis not a latest-snapshot read across many rows in wave E lesson 5's sense, so the phrase does not apply.
PR tender verification
ORDER BY collection_time DESC LIMIT 1. SeeLite/Services/LocalDataService.QueryStats.cs:658(GetCachedQueryPlanAsync) andDarling/PerformanceMonitor.Darling.Service/Mcp/DarlingStoredPlanReader.cs:70-71.NotCollectedStatusAsyncruns first, thenunavailable.See
Lite/Mcp/McpPlanTools.cs:35-39andDarlingMcpPlanTools.cs:66-70. An exception returnserrorthrough
McpHelpers.FormatError.actual_rows,actual_elapsed_msandactual_cpu_msare null when the plan has no runtime stats. See
PerformanceMonitor.PlanAnalysis/McpPlanAnalysisFormatter.cs:129-131.20abc27dmerged dev after analyze_procedure_plan moves reading guidance off tools/list (#3898) #4117 landed, since analyze_procedure_plan moves reading guidance off tools/list (#3898) #4117 changed the same plan files. The merge was clean.Both budget lines stay on both products:
analyze_procedure_plan 618andanalyze_query_plan 616.database_name, and one onwhether the plan is read live. It found 0 misreads. Q4, Q6 and Q8 were answered "can't tell". The other five answers were
correct.
are the Test plan's list plus
*McpPlanAnalysis*.4126-reported.txtis true as written (915 and 903 served characters before, 616 after).D9 traps
top_operatorsrow has noactual_elapsed_ms. Does that mean the operator ran in 0 ms?Correct: No.
actual_*fields go null, not 0, when the plan carries no runtime statistics. A 0 thereis a real measurement.
Prevents it: "actual_* are null, not 0, without runtime stats."
status: "unavailable". Does that mean the same thing asstatus: "not_collected"?Correct: No.
not_collectedmeans the engine itself cannot collectquery_stats, checked first.unavailablemeans the engine can collect it, but there is no stored plan for this one hash.Prevents it: "No plan: not_collected if the engine can't collect query_stats, else unavailable."
create_statementreturns a ready-madeCREATE INDEXstatement. Is it safe to run as returned?Correct: No. It is corroboration for a statement already measured slow, never a diagnosis on its own.
Every row carries the fixed caveat: regression risk for other plans, and write cost.
Prevents it: "corroboration for a statement already measured slow, never a diagnosis; every row carries
the fixed caveat (regression risk for other plans, write cost)."
analyze_query_plantwice in a row for the samequery_hashis guaranteed to return the sameplan both times.
Correct: No. It always analyzes
query_hash's latest plan, so a newer capture landing between the twocalls changes the answer.
Prevents it: "Analyzes query_hash's latest plan."
missing_indexesrows from two statements withimpact_basis40 and 30. Doescombining the index help by 70 percent?
Correct: No. The block opens "Per statement," and that scopes everything under it to its own statement:
warnings, missing_indexes/impact_basis, parameters, memory_grant, top_operators. The two numbers are not
from the same scope, so they are not additive.
Prevents it: "Per statement: warnings; missing_indexes labelled impact_basis, ..."
warningsarray is empty. Does that mean the query has no performance problems?Correct: No. The head promises only five categories per statement: warnings, missing_indexes, parameters,
memory_grant, top_operators. An empty array in one category means that category had nothing to report, not
that the query is problem-free overall.
Prevents it: "Per statement: warnings; missing_indexes labelled impact_basis, ...; parameters;
memory_grant; top_operators by operators_ranked_by, ..."
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).McpToolGuide*,McpToolsListBudget*,McpDescription*,McpZeroIsAMeasurement*,McpPayloadContractCensus*,MeasurementContractCensus*,McpPageContract*,PlanTools*,AnalyzeQueryPlan*. Total 315, Failed 0, Skipped 9 (live-Postgres classes, no rig in thispass).
dropcheck.py origin/dev HEAD analyze_query_plan, run against the pushed commit. Darling:old=915 new=1511 sentences=3 missing=0. Lite:old=903 new=1499 sentences=3 missing=0.desc.py analyze_query_plan: head 585, served 616 on both products.only, and says not to run a full suite. CI runs the full suites on the push. This pass changes 6 files: 2
tool source, 2 head-pin tests, 2 budget lines, all covered by the targeted classes above.
Handoff
analyze_query_planis fully converted on both products,with the ruled shared head.
analyze_plan_xml(analyze_plan_xml moves reading guidance off tools/list (#3898) #4115) is merged intodev.analyze_procedure_plan(analyze_procedure_plan moves reading guidance off tools/list (#3898) #4117) has since merged intodevasaed89b93.This pass only read its worktree as the reference for the block's exact wording and line-split style. It did
not modify that PR.
analyze_query_store_planstays unconverted, per the wave-E common brief.AnalysisPassTokenThreadingTestson Lite, unrelatedto MCP descriptions. It was not re-verified in this pass, since the full suite did not run here. It is worth
a look if it recurs in a CI census.
🤖 Generated with Claude Code
https://claude.ai/code/session_01NUU29PuGg9TUFBsACgZg2K