Repository navigation
get_blocking/get_pg_replication_slots move reading guidance off tools/list (#3898) - #4108
Merged
Merged
Conversation
…/list (#3898) D3 Blocking+Slots: converts both tools to the head+tail MCP guide pattern. Guardrail facts (status vocabulary, gates, units) move to a served head under 620 chars; the rest of the original prose moves verbatim to the tail get_tool_guide serves on demand. Neither tool has a Lite twin. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QQh8LczP1HGLXQYAx4oTZC
…t "the normal case" (#3898) "slots_present is the normal case" did not restate its route (slot rows in the window) and read as typical or healthy, while the code calls no_slots the common case. The clause now says slots were seen in the window and points at worst_severity for health. Served head 599 -> 618; pin 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:30
This was referenced Sep 24, 2026
erikdarlingdata
added a commit
that referenced
this pull request
Sep 24, 2026
…ools/list (#3898) (#4123) * get_deadlocks/get_blocked_process_reports move reading guidance off tools/list (#3898) get_deadlocks is a twin: one byte-identical head (591 served chars) now covers both Darling (was 822) and Lite (was 694). get_blocked_process_reports is Lite-only (was 1,340) and gets its own head (483 served), modeled on Darling's get_blocking (#4108). Every original sentence moves to the tool's own get_tool_guide tail, unchanged. The dedup_key parameter (Darling-only, get_deadlocks) was 406 chars, over the D2 200-char cap; its "BEFORE limit" scan-order sentence stays on the parameter because an existing pin (McpPageContractTests.FingerprintReaders_NameTheScanCeilingFields) reads it there, and the other two original sentences moved verbatim into the tail. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QQh8LczP1HGLXQYAx4oTZC * Re-point two existing dedup_key pins found by the full suite (#3898) McpPageContractTests.FingerprintReaders_NameTheScanCeilingFields and DarlingMcpBlockingToolsSurfaceAndSqlTests.ParamContract_DedupKeyDescription_AdvertisesItsScoping both read facts straight off the dedup_key parameter's own Description attribute. My first trim moved one or the other fact to the tail and broke whichever pin it left behind. get_blocking (#4108) already solved this in the same file: keep "BEFORE limit" and "display name" in a 191-char parameter, and repeat the full original parameter text verbatim in the tool's own tail under a "dedup_key:" label. get_deadlocks now matches that shape. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QQh8LczP1HGLXQYAx4oTZC --------- Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
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
A client that loads Darling's whole MCP catalog pays for every tool's full reading guidance before it asks a
single question.
get_blockingandget_pg_replication_slotseach carried a long description with the readingguidance folded into the part every client always receives. This PR moves that guidance behind
get_tool_guide(tools[], topics[]), so a client only pays for it when it actually asks. Nothing is deleted.What changes
Both tools convert to the head-plus-tail pattern. The served head is at most 620 characters, pointer included.
It carries the guardrail facts: the status vocabulary, what bounds the page or the scan, units, and the
empty/zero/null semantics. The full original prose moves, sentence for sentence, into the tail that
get_tool_guideserves. Neither tool has a Lite twin (git grep -n 'Name = "get_blocking"' -- Lite/Mcpand thesame search for
get_pg_replication_slotsboth come back empty), so there is no D6 lockstep pair here.Neither tool is on Lite, so there is no Lite column.
Corrections
None. Both tools' original prose held up against the code. Nothing needed correcting.
D4 removals
get_blocking'sdedup_keyparameter read "Optional #1140 alert fingerprint...". The#1140reference is gonefrom the parameter's on-wire text. It was never copied into the tail either. Neither tool's main description
carried an issue reference.
Re-pointed pins
DarlingMcpBlockingToolsSurfaceAndSqlTests.ParamContract_DedupKeyDescription_AdvertisesItsScopingcoversget_blocking,get_deadlocks, andget_deadlock_detail. Onlyget_blockingis mine. It requires thededup_keyparameter's own text to contain "Dedup Key" and "display name". Those two phrases stayed in thetrimmed parameter text on the wire. They did not move to the tail. The test's own comment gives the reason. An
agent picks arguments from a tool's own description. A caveat that is only in the tail is one that an agent
never sees if it reads only the parameter.
McpPageContractTests.FingerprintReaders_NameTheScanCeilingFields("get_blocking")requires "rows_examined" and"scan_truncated" in the tool's own raw description. That is still true. Both are in the tail, which is part of
the same attribute string. It also requires "BEFORE limit" in the
dedup_keyparameter's own text, kept in theguardrail text for the same reason as above.
get_blocking.dedup_keyneeded all three pinned phrases inside the 200-character parameter cap. It was 406characters, and 400 once the
#1140reference is dropped. The trimmed text is 191 characters: "Optional alertfingerprint (the alert's Dedup Key). The key is scoped to the server's display name and the incident's involved
objects. The fingerprint scan runs over the window BEFORE limit."
Two pieces move in full to the tool's own tail, under the label
dedup_key:. The first is what supplying the keyreturns, with the "paste it from a ticket" note. The second is the scan-ceiling detail, which the head already
covers.
D9 traps
Correct: No. not_collected already ruled out an incompatible engine. Empty still covers both "nothing
happened" and "nothing was collected".
Prevents it: "empty means none in the window, or none collected in it."
Correct: No. Widening the window can only add more candidate rows, never fewer. truncated is a row cap, not
a time cap.
Prevents it: "limit caps ROWS, not hours_back: truncated true means raise limit or narrow the window, not
widen hours_back."
window?
Correct: No. It proves no row matched that fingerprint. The window can still hold plenty of other blocking,
all filtered out by the key.
Prevents it: "a no-match answer is still empty."
Correct: No. The fingerprint scan runs over the whole window before limit is applied, up to its own stated
ceiling.
Prevents it: "dedup_key scans the whole window before limit, up to a stated ceiling
(rows_examined/scan_truncated)."
monitored server and the store?
Correct: No. These timestamps are already de-skewed to the same clock frame as event_time, so the ordering
is meaningful as read.
Prevents it: "last_tran/last_batch stamps are de-skewed for direct comparison to event_time"
Correct: Milliseconds. 500 ms is half a second.
Prevents it: "wait_time_ms is milliseconds."
abandoned slot?
Correct: No. Replication slots live on the writer, so a replica's own no_slots says nothing about the
writer's slots.
Prevents it: "a replica's no_slots doesn't clear the writer."
Correct: No. Null means the collector's -1 sentinel applied: unmeasured or not applicable, not a measured
zero.
Prevents it: "null on the collector's -1 sentinel"
Correct: No. False is the default, normal state, because max_slot_wal_keep_size is -1 by default, so there
is no configured ceiling. It is not a gap.
Prevents it: "no ceiling by default isn't a gap."
Correct: No. 0 is a real measurement: the slot is flat, no growth this window. Null means growth itself has
no measurement at all.
Prevents it: "retained_wal_growth_bytes is 0 when flat (measured), null when unmeasurable."
a bug?
Correct: No. worst_slot is chosen by severity classification, not by raw retained size. Size only breaks
ties inside the same severity.
Prevents it: "worst_slot ranks by severity, not size."
the collector is broken?
Correct: No. not_collected here means the target's engine cannot run this collector at all. It says nothing
about a working collector's health.
Prevents it: "not_collected: engine is not PostgreSQL."
display-name scoping guarantee dropped from the tool?
Correct: No. The parameter's own on-wire text still states the key is scoped to the server's display name
and the incident's involved objects. Only the illustrative wording and the already-duplicated ceiling detail
moved to the tail.
Prevents it: "The key is scoped to the server's display name and the incident's involved objects." (in the
dedup_key parameter's own served text)
slots_present. Does that status tellyou the slots are healthy?
Correct: No. It only says slot rows were sampled in the window. Health is in
worst_severityand eachslot's
severity.Prevents it: "slots_present: slots in window; read worst_severity."
Status routes
get_blocking (
Darling/PerformanceMonitor.Darling.Service/Mcp/DarlingMcpBlockingTools.cs):not_collected, line 90:rows.Count == 0and the resolved server's engine cannot run theblocked_process_reportcollector.empty(no rows), line 91:rows.Count == 0and the engine check above did not fire.empty(no match), line 112: adedup_keywas supplied and no row's fingerprint equals it, after scanning thewindow up to the fingerprint ceiling.
error, line 201: the read threw.get_pg_replication_slots (
Darling/PerformanceMonitor.Darling.Service/Mcp/DarlingMcpPgSlotTools.cs):not_collected, line 104:rows.Count == 0and the resolved server's engine cannot run thepg_replication_slotscollector, for example a SQL Server target.no_slots, line 115:rows.Count == 0and the engine check above did not fire, on a real PostgreSQL targetwith no sampled slot row in the window.
slots_present, line 183:rows.Count > 0.error, line 195: the read threw.Test plan
dotnet build Darling/Darling.Tests/Darling.Tests.csproj -c Debug: Build succeeded, 0 Warning(s), 0Error(s).
python dropcheck.py origin/dev HEAD get_blocking get_pg_replication_slots) printsget_blocking [Darling] old=1645 new=2654 sentences=8 missing=0andget_pg_replication_slots [Darling] old=803 new=1380 sentences=5 missing=0.get_pg_replication_slots's "worst_slot ranks by severity, not size." head fact.Confirmed
McpToolGuideHeadsBlockingSlotsTests.EveryConvertedHead_CarriesItsGuardrailFact_AndThePointerfailedon it. Restored and confirmed green again.
Darling.Tests.exe -class ...):DarlingMcpBlockingToolsSurfaceAndSqlTests(30 passed),McpToolsListBudgetTests+McpToolGuideTests+McpPageContractTests(58 passed),McpPayloadContractCensusTests+McpZeroIsAMeasurementTests+McpPayloadClockFrameDisciplineTests+ConsumedTimestampFrameDisciplineTests(113 passed),DarlingPgSlotReaderTests+DarlingBlockingStatsReadTestsDarlingBlockingTrendEmptyTests+DarlingContentiousObjectLabelTests+DarlingIncidentFingerprintTests(64 total, 4 skipped for no PostgreSQL rig, rest passed), and the new
McpToolGuideHeadsBlockingSlotsTests(7 passed).
Darling.Testssuite, aftergit merge origin/dev: 13,315 total, 0 errors, 2 failed, 636 skipped (noPostgreSQL rig for this lane). The 2 failures are the known
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-elevatedlocal run, and CI runs elevated. This PR does not touch those files.
PerformanceMonitor.Commonor any Lite file.body_4108.md: 29 hit(s), doc mode. The remaining hits are 28 D9 labels and the attribution emoji, which areallowed.
Coordinator verification
origin/dev(bbd7eec6) into the branch. The dropped-sentence check showsmissing=0for both tools. The parameter check showsdedup_key406 -> 191. Its one "missing" sentence differs only by the#1140reference that D4 removes. Everything else is verbatim in the tail underdedup_key:.get_blockinghead fact against the code. The window runs fromas_of - hours_backtoas_of, and rows sort byevent_time DESC. No rows givesnot_collectedfrom theblocked_process_reportengine check first, thenempty. Adedup_keywith no match givesempty. The fingerprint scan covers up to 2,000 rows of the window beforelimit. Every fact is correct.get_pg_replication_slotshead fact against the code. The collector reads the core catalog only, sonot_collectedmeans the engine is not PostgreSQL. No rows on a PostgreSQL target givesno_slotswith the per-instance scope note. The -1 sentinel serves null onretained_wal_bytes,retained_wal_gb,safe_wal_size_bytes,xmin_ageandcatalog_xmin_age. Growth is null unless both ends were measured across a window that spans time.worst_slotis the highest severity rank, and size only breaks ties.slots_present is the normal casedid not restate its route, which is "slot rows in the window" (rows.Count > 0). It also reads as "typical" or "healthy", but the code callsno_slotsthe common case, and aslots_presentanswer can carry a critical slot. The clause is nowslots_present: slots in window; read worst_severity.has_configured_wal_ceiling. My question got the right answer, but the reader took "the normal case" to mean "typical", which is the reading the code contradicts. I did not run a second round, because the edit changed only that one clause.get_pg_replication_slotswent from 599 to 618 characters. I updated its pin and budget line. The changelog buffer entry already said "fewer than 620 characters" and needed no change.DarlingInstallLocationTestsfailures with their known cause.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. Lite needs no run, because this PR changes no Lite or Common file.Handoff
DarlingInstallLocationTestsfailures are a known local test bug from Install scripts: the service account gets Read & Execute on the install root and Modify only where it writes (#4052) #4090. The The MCP surface costs ~100k tokens of context before the first call: 157 tools, 336 KB of tools/list, 86 KB of instructions #3898 coordinatorreported it to Erik. They are outside this PR.
get_blockingandget_pg_replication_slotsare fullyconverted and green.
🤖 Generated with Claude Code
https://claude.ai/code/session_01QQh8LczP1HGLXQYAx4oTZC