Skip to content

get_deadlocks/get_blocked_process_reports move reading guidance off tools/list (#3898) - #4123

Merged
erikdarlingdata merged 4 commits into
devfrom
feature/3898-content-deadlocks
Sep 24, 2026
Merged

erikdarlingdata merged 4 commits into
devfrom
feature/3898-content-deadlocks

Conversation

@erikdarlingdata

@erikdarlingdata erikdarlingdata commented Sep 24, 2026 •

Copy link
Copy Markdown
Owner

Refs #3898.

Why

get_deadlocks (Darling and Lite) and get_blocked_process_reports (Lite only) put their whole reading guide on
every tools/list call. get_deadlocks is a twin whose two products already carry nearly the same text. This PR
gives it one byte-identical head. get_blocked_process_reports has no Darling twin under that name. Darling's
closest tool, get_blocking, was converted separately in #4108, and its head is the model this PR follows.

What changes

Each tool's Description(...) now holds a short HEAD, served on tools/list. After the literal <<GUIDE>> marker
comes the full original text as a TAIL, served only by get_tool_guide. No SQL, payload shape, or tool behavior
changed.

get_deadlocks's head states one fact that differs by product: dedup_key is Darling-only. Per D6, the shared,
byte-identical head names it as "Darling: dedup_key ...". This is true on both products, even though Lite's own
get_deadlocks has no such parameter. GetDeadlocks_HasNoDedupKeyParameter in
Lite.Tests/McpToolGuideHeads.Deadlocks.cs pins that.

The Darling-only dedup_key parameter on get_deadlocks was 406 characters, over the D2 200-char cap. It now
matches the shape PR #4108 already established for get_blocking's own dedup_key parameter in the same file.
The parameter is 191 characters and keeps the "BEFORE limit" and "display name" sentences, both read by existing
pins below. The tool's own tail repeats the full original parameter text verbatim, labeled dedup_key:.

Served characters

Tool Darling before Darling after Lite before Lite after
get_deadlocks 822 591 694 591
get_blocked_process_reports (no Darling tool of this name) 1,340 483

Status routes

Every status word either head states, with the branch that returns it:

Tool Product Status File:line Condition
get_deadlocks Darling not_collected DarlingMcpBlockingTools.cs:246 rows.Count==0 and this engine cannot run the "deadlocks" collector (permanent)
get_deadlocks Darling precondition DarlingMcpBlockingTools.cs:251 rows.Count==0, engine capable, and the collector's last recorded run named a fixable precondition (for example SESSION_MISSING)
get_deadlocks Darling empty DarlingMcpBlockingTools.cs:252 rows.Count==0 and neither of the above matched
get_deadlocks Lite not_collected McpBlockingTools.cs:39 same condition as Darling
get_deadlocks Lite precondition McpBlockingTools.cs:44 same condition as Darling
get_deadlocks Lite empty McpBlockingTools.cs:45 same condition as Darling
get_blocked_process_reports Lite not_collected McpBlockingTools.cs:177 rows.Count==0 and this engine cannot run the "blocked_process_report" collector (permanent)
get_blocked_process_reports Lite empty McpBlockingTools.cs:178 rows.Count==0 and the engine can run it (get_blocked_process_reports has no precondition check, unlike get_deadlocks)

wait_time_ms (get_blocked_process_reports) is confirmed milliseconds by WaitTimeFormatted in
Lite/Services/LocalDataService.Blocking.cs:1295 (WaitTimeMs < 1000 ? "{WaitTimeMs} ms" : ...).

Corrections

None. Every head fact was checked against the tool body, the status-routing code, and (for wait_time_ms) the
data-service mapping before being written.

D4 removals

  • The #1140 issue reference in the Darling get_deadlocks dedup_key parameter came off the wire. It is gone
    from both the 191-character parameter and its verbatim repeat in the tail.

Re-pointed pins

  • McpPageContractTests.FingerprintReaders_NameTheScanCeilingFields reads the "BEFORE limit" sentence straight off
    the dedup_key parameter attribute. It still passes: the trimmed parameter keeps that sentence.
  • DarlingMcpBlockingToolsSurfaceAndSqlTests.ParamContract_DedupKeyDescription_AdvertisesItsScoping reads the
    "display name" scoping sentence off the same parameter. It still passes for the same reason. Both pins found a
    gap in my first trim, which had moved one sentence or the other out of the parameter. The fix matches the
    191-character shape get_blocking/get_pg_replication_slots move reading guidance off tools/list (#3898) #4108 already uses for get_blocking's own dedup_key parameter in this file.

Status routes (the status words the heads state)

Tool Status Condition File:line
get_deadlocks (Darling) not_collected No rows, and the server's engine never runs deadlock capture DarlingMcpBlockingTools.cs:246
get_deadlocks (Darling) precondition No rows, and a fixable gap is recorded, for example the XE session is gone DarlingMcpBlockingTools.cs:251
get_deadlocks (Darling) empty No rows otherwise DarlingMcpBlockingTools.cs:252
get_deadlocks (Darling) empty A dedup_key that matches nothing in the scanned window DarlingMcpBlockingTools.cs:266-267
get_deadlocks (Darling) error An exception during the read DarlingMcpBlockingTools.cs:310
get_deadlocks (Lite) not_collected, precondition, empty The same three, in the same order, when there are no rows McpBlockingTools.cs:39-45
get_deadlocks (Lite) error An exception during the read McpBlockingTools.cs:77
get_blocked_process_reports (Lite) not_collected No rows, and the server's engine never runs blocked_process_report capture McpBlockingTools.cs:177
get_blocked_process_reports (Lite) empty No rows otherwise. This tool has no precondition route. McpBlockingTools.cs:178
get_blocked_process_reports (Lite) error An exception during the read McpBlockingTools.cs:240

D9 traps

  • Q: get_blocked_process_reports answers status "empty". Did the collector run in the window?
    Correct: Not from this alone: empty covers both "none in the window" and "none collected in it".
    Prevents it: "else empty means none in the window, or none collected in it."
  • Q: get_blocked_process_reports asked for 24 hours and got a full page (truncated=true) of very recent rows. Does that mean blocking was rare over the 24 hours?
    Correct: No. The page is capped by limit, not hours_back, so a server blocking steadily fills the page from just its newest few minutes.
    Prevents it: "limit caps ROWS, not hours_back: truncated true means raise limit or narrow the window, not widen hours_back."
  • Q: truncated is true on get_blocked_process_reports. Does widening hours_back get the caller more rows?
    Correct: No. Widening hours_back cannot help, because the cap is on rows. Raise limit or narrow the window instead.
    Prevents it: "truncated true means raise limit or narrow the window, not widen hours_back."
  • Q: wait_time_ms on get_blocked_process_reports reads 500. Is that half a second or 500 seconds?
    Correct: Milliseconds, so half a second.
    Prevents it: "wait_time_ms is milliseconds."
  • Q: On get_blocked_process_reports, does blocked_last_tran_started need a timezone adjustment before comparing it to event_time?
    Correct: No, it is already de-skewed by the read, so comparing it against event_time directly is valid.
    Prevents it: "last_tran/last_batch stamps are de-skewed for direct comparison to event_time."
  • Q: get_deadlocks answers status "not_collected". Does that mean deadlocks can show up on a later call once collection catches up?
    Correct: No. not_collected means this engine can never run the deadlocks collector. It is permanent, not fixable.
    Prevents it: "not_collected wins if the engine can't run deadlocks."
  • Q: get_deadlocks answers status "precondition" naming a missing XE session. Is that the same kind of gap as not_collected?
    Correct: No. precondition names a fixable gap, like a dropped session. not_collected is permanent.
    Prevents it: "then precondition names a fixable gap (e.g. XE session gone)."
  • Q: get_deadlocks answers status "empty" (neither not_collected nor precondition). Does that prove the collector ran cleanly the whole window?
    Correct: No. Passing those two checks only rules out the specific permanent and recorded-fixable gaps. Empty can still mean nothing was collected some other way.
    Prevents it: "else empty means none in the window, or none collected in it."
  • Q: A get_deadlocks call with dedup_key set answers status "empty". Did the fingerprint scan fail?
    Correct: No. A no-match on the fingerprint is a normal empty answer, not an error.
    Prevents it: "a no-match answer is still empty."
  • Q: Does a matching dedup_key incident outside the first limit rows get missed?
    Correct: No. The fingerprint scan runs over the whole window before limit is applied, up to its own stated ceiling.
    Prevents it: "Darling: dedup_key scans the whole window before limit, up to a stated ceiling (rows_examined/scan_truncated)."
  • Q: The get_deadlocks head is byte-identical on Darling and Lite. Does that mean dedup_key works on Lite too?
    Correct: No. dedup_key is Darling-only. The shared head says so explicitly with the "Darling:" label.
    Prevents it: "Darling: dedup_key scans the whole window before limit..."
  • Q: get_deadlocks' oldest_returned_deadlock_time falls well inside the requested hours_back. Does that prove the whole window was searched?
    Correct: No. Under newest-first paging, a truncated page's oldest stamp is only how far the read reached, not proof the rest of the window was empty.
    Prevents it: "limit caps ROWS, not hours_back: truncated true means raise limit or narrow the window, not widen hours_back."
  • Q: A caller wants the full deadlock graph XML. Does get_deadlocks return it?
    Correct: No. Use get_deadlock_detail. get_deadlocks carries only victim process info.
    Prevents it: "Use get_deadlock_detail for the graph XML."

Test plan

  • Darling.Tests build: Build succeeded, 0 Warning(s), 0 Error(s)
  • Lite.Tests build: Build succeeded, 0 Warning(s), 0 Error(s)
  • Targeted classes on both products (McpToolGuide*, McpToolsListBudget*, McpPayloadContractCensus*,
    McpPageContractTests, DarlingIncidentFingerprintTests, DarlingMcpBlockingToolsSurfaceAndSqlTests,
    CollectorRuntimePreconditionTests, RuntimePreconditionReadWiringTests, CrossAppMcpToolInventoryPinTests):
    all green
  • Red-watch: broke the "not_collected wins" head phrase, confirmed
    McpToolGuideHeadsDeadlocksTests.EveryConvertedHead_CarriesItsGuardrailFact_AndThePointer failed, restored, confirmed green
  • dropcheck.py origin/dev HEAD get_deadlocks get_blocked_process_reports: missing=0 for all three
    (Darling get_deadlocks, Lite get_deadlocks, Lite get_blocked_process_reports)
  • Full Darling.Tests suite after git merge origin/dev: Total 13393, Errors 0, Failed 0, Skipped 637
    (no PostgreSQL rig)
  • One test showed Not Run. This is pre-existing and unrelated to this change. The gate here is 0 Failed and
    0 Errors, and this run met it
  • Full Lite.Tests suite: Total 5270, Errors 0, Failed 0
  • Plain-English checker run once on this body: fixed the real hits it found

Coordinator verification

  • Checked every head fact against the code on both products:
    • get_deadlocks tries not_collected, then precondition, then empty, on both products.
    • Lite's get_blocked_process_reports tries not_collected, then empty. It has no precondition route, and its head names none.
    • All three set truncated when the reader returned more than limit rows.
    • On Darling, dedup_key is fingerprinted over the window before limit, and a key that matches nothing returns empty.
    • Lite subtracts each server's own offset from the six last_tran and last_batch stamps, using McpServerLocalWindow.
  • paramcheck found 1 miss: get_deadlocks.dedup_key lost the "[FEATURE] Add a stable, machine‑readable dedup fingerprint (and involved‑object list) to alert payloads - esp. deadlocks & blocking #1140" issue reference, a D4 removal. The tail keeps the rest of the old parameter text.
  • D9, one round (haiku, one Read), 14 questions: 0 misreads. I added Q14, which asks whether get_blocked_process_reports can return precondition. The reader answered no, which is right. Four answers were "not stated", which does not count as a misread.
  • The lane left out the Status routes table. I added it above, from the code.
  • No head or code change, so no new local test run. The lane ran both full suites, and CI runs them again before the merge.

Handoff

Both tools in this lane's brief (get_deadlocks, get_blocked_process_reports) are fully converted and green. Nothing
deferred from this lane's scope.

🤖 Generated with Claude Code

https://claude.ai/code/session_01QQh8LczP1HGLXQYAx4oTZC

erikdarlingdata and others added 4 commits September 23, 2026 23:03
…ools/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
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
@erikdarlingdata
erikdarlingdata marked this pull request as ready for review September 24, 2026 03:41
@erikdarlingdata
erikdarlingdata enabled auto-merge (squash) September 24, 2026 03:41
@erikdarlingdata
erikdarlingdata merged commit cca2159 into dev Sep 24, 2026
16 of 18 checks passed
@erikdarlingdata
erikdarlingdata deleted the feature/3898-content-deadlocks branch September 24, 2026 04:21
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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant