Skip to content

get_collector_stall_probes/describe_custom_view_catalog/get_sweep_reports reading guidance move off tools/list (#3898) - #4074

Merged
erikdarlingdata merged 3 commits into
devfrom
feature/3898-content-platform-c
Sep 23, 2026
Merged

erikdarlingdata merged 3 commits into
devfrom
feature/3898-content-platform-c

Conversation

@erikdarlingdata

@erikdarlingdata erikdarlingdata commented Sep 23, 2026 •

Copy link
Copy Markdown
Owner

Refs #3898.

Why

A client that loads Darling's whole MCP catalog pays for every tool's reading guidance up front, in tools/list,
before it asks a single question. This PR moves that guidance for three Darling-only tools into their own
get_tool_guide reading guide, served only on request. Nothing is deleted. Every guardrail fact stays reachable,
either in the short description every client always sees, or in the guide.

What changes

Converted three tools, all on PerformanceMonitor.Darling.Service.Mcp, none with a Lite twin (checked by name
search before starting):

Tool File Before (served chars) After (served chars, pointer included)
get_collector_stall_probes DarlingMcpStallProbeTools.cs 2,678 595
describe_custom_view_catalog DarlingMcpCustomViewTools.cs 2,589 514
get_sweep_reports DarlingMcpFleetSweepTools.cs 2,274 612

Each new head states the tool's guardrail facts in under 620 characters. That covers what it returns, its gate or
floor, and what an empty or zero answer means. For get_sweep_reports, it also covers the two field contracts
the tail used to carry alone. Every original sentence moved to the tail verbatim, in its original order, except
the one D4 removal below.

describe_custom_view_catalog and get_sweep_reports already used string literals joined with +. The new head
and the marker went in as new leading pieces. Every original line stayed untouched. Nothing needed retyping in the
parts that must stay verbatim, so there was no chance of a copy error there.

Added Darling/Darling.Tests/McpToolGuideHeads.PlatformC.cs (class McpToolGuideHeadsPlatformCTests): the head
pins for this lane's three tools, following the HealthParser/Data pattern. Updated the three tools' lines in
McpToolsListBudget/DarlingMcp{StallProbe,CustomView,FleetSweep}Tools.txt to the new served lengths. The ceilings
went down, not up.

Corrections

None. All three tools' original prose matched the tool body on inspection.

D4 removals

One. get_collector_stall_probes' second sentence carried a general reading rule and, after a dash, a one-time
measured example:

...so a collector stalled in its result-set drain is itself holding the sequence that waiting_tasks,
dmv_blocking_snapshot and query_snapshots would run in — measured on one real stall, nothing was observed for
four minutes, waiting_tasks ran 2.3 seconds after it cleared and returned zero rows, and about 20 collectors
then completed inside 20 seconds as the backlog drained.

The rule (the clause up to the dash) explains why the tool exists, and it stayed in the tail verbatim. The clause
after the dash does not help read any future row. It named one past measurement, so it came off the wire per D4.
It is the only sentence dropcheck.py reports as missing for these three tools. The other two tools both report
missing=0.

Re-pointed pins

DarlingMcpFleetSweepToolsTests.cs had two pins that read the tool's raw [Description] text directly, both
sides of the marker at once. That raw text still holds the whole original sentence, so both pins kept passing
without a code change on their part. The brief asks for a deliberate re-point instead, so this PR moves them:

  • Description_CarriesTheMuteSemantics_AndTheQuietIsNotCleanBlock now checks the served head
    (McpToolGuideTests.Served(...).Served) for the short guardrail claims a client sees without calling
    get_tool_guide: MUTE SEMANTICS, DELIVERY WAS OFF, would_have_paged, ABSENT on an alerts-on sweep,
    QUIET IS NOT CLEAN, instruments_alive, instrument_liveness. It checks the tail for the fuller original
    wording that did not fit the 620-character head: muted, and nothing would have paged, the fleet_sweep knob
    name, and at most one daily rollup.
  • Description_StatesTheIds_AreEmittedAsStrings now checks the head for the short claim
    (pass sweep_id (a STRING) for one sweep). It checks the tail for the exact original sentences,
    spelled as a JSON string and pass one back verbatim, which moved there.

Red-watch: broke the head's MUTE SEMANTICS label. Ran this pin and the new head pin, and saw both fail. Restored
the text and saw both pass again.

No other existing test pins description text for these three tools. CollectorStallProbeStoreTests,
DarlingMcpCustomViewToolsTests, DarlingWebEndpointsTests, FleetSweepStateRungTests, FleetSweepWebFeedTests,
McpLatestSnapshotStampTests, and Lite's CrossAppMcpToolInventoryPinTests check tool-name rosters or JSON
payload shapes instead. This PR touches neither.

D9 traps

  1. Q: get_collector_stall_probes returns not_collected. Is the tool broken?
    Correct: No. A probe fires only when throughput drops and a collector's budget runs low. That combination
    is rare, so an empty answer here is a healthy result.
    Prevents it: Sampled only when a collector's wall-clock budget is a quarter elapsed and throughput is under 1 MB/s; rare, so a not_collected empty answer is EXPECTED and healthy.
  2. Q: A probe row shows waiting_task_count: 0. Did the sample miss something?
    Correct: No. A positive scheduler_count beside a zero count is a real all-clear, not a missed read.
    Prevents it: scheduler_count is the sample's denominator: waiting_task_count 0 beside it is a real all-clear.
  3. Q: outcome_census lists CONNECT_FAILED rows. Do those need filtering out before reading the answer?
    Correct: No. They are findings on their own, not noise to discard.
    Prevents it: outcome_census is never filtered to successes: CONNECT_FAILED/CONNECT_TIMED_OUT are findings.
  4. Q: Can several stall-probe rows average into one trend line?
    Correct: No. Each row is evidence about one moment. The tool carries no band to compare it to.
    Prevents it: Unbanded, untrended: one row is evidence about a moment.
  5. Q: Does describe_custom_view_catalog need a server_name or a time window for one server's data?
    Correct: No. It is static reference data. It reads no server and no collected data.
    Prevents it: Static reference data: no server, time window, or collected-data read.
  6. Q: A measure name looks plausible by convention. Does validate_custom_view accept a name outside the
    catalog?
    Correct: No. The compiler emits only the catalog's own identifiers, however close a guess comes.
    Prevents it: the compiler emits ONLY these identifiers.
  7. Q: A query_stats panel filters on neq for one procedure name. Does that also drop the ad-hoc bucket?
    Correct: No. The '(ad hoc)' rows stay in, because that dimension's value is never null.
    Prevents it: a neq filter on procedure name still INCLUDES those rows (the dimension value is never null).
  8. Q: get_sweep_reports runs with no watch_state argument. Does the worklist list closed items too?
    Correct: No. The default view holds open and carried items only.
    Prevents it: the watch worklist (default: open+carried only; pass watch_state for pending/closed).
  9. Q: A sweep has alerts_enabled: false and no bad verdicts. Was the fleet actually quiet that window?
    Correct: Unknown from that alone. alerts_enabled: false means delivery was off for the window, whatever
    conditions were.
    Prevents it: MUTE SEMANTICS: alerts_enabled false means DELIVERY WAS OFF that window.
  10. Q: A sweep's payload has no would_have_paged key at all. Is that the same as the key being present and
    empty?
    Correct: No. A missing key means the sweep ran alerts-on, so the check never ran.
    Prevents it: such a sweep carries would_have_paged (present-and-empty means nothing would have paged; ABSENT on an alerts-on sweep).
  11. Q: A sweep shows an empty window with no bad verdicts. Is that a clean bill of health on its own?
    Correct: No. instruments_alive: false means the sweep failed to prove its own data sources. The empty
    window is then unreadable, not healthy.
    Prevents it: QUIET IS NOT CLEAN: instruments_alive false means the sweep could not prove its data sources; read instrument_liveness before trusting a quiet window.
  12. Q: Does get_sweep_reports run a live check against the monitored servers to build its answer?
    Correct: No. It is a stored read over the monitoring store.
    Prevents it: the head's closing sentence, Stored read.
  13. Q: sweep_id arrives as a JSON number instead of a string. Does the tool round it to the nearest sweep?
    Correct: No. The parameter takes a string by contract. An id that fails to parse exactly gets refused.
    Prevents it: pass sweep_id (a STRING) for one sweep.

Test plan

  • dotnet build Darling/Darling.Tests/Darling.Tests.csproj -c Debug: 0 Warning(s), 0 Error(s).
  • dotnet build Lite.Tests/Lite.Tests.csproj -c Debug: 0 Warning(s), 0 Error(s). Checked because one
    existing pin, CrossAppMcpToolInventoryPinTests, lives there.
  • Targeted run (McpToolGuide*, McpDescription*, McpZeroIsAMeasurement*, McpPayloadContractCensus*,
    McpToolsListBudget*, plus every class this lane's orders named): Total: 326, Errors: 0, Failed: 0, Skipped: 4
    on Darling.Tests. Total: 6, Errors: 0, Failed: 0 on Lite.Tests' CrossAppMcpToolInventoryPinTests.
  • Red-watch: broke get_sweep_reports' MUTE SEMANTICS head text. Confirmed the new head pin and the
    re-pointed sweep pin both failed, restored the text, confirmed both green again.
  • dropcheck.py origin/dev HEAD get_collector_stall_probes describe_custom_view_catalog get_sweep_reports:
    missing=0 for describe_custom_view_catalog and get_sweep_reports. missing=1 for
    get_collector_stall_probes, the D4 removal above, as expected.
  • git merge origin/dev, which pulled in two more The MCP surface costs ~100k tokens of context before the first call: 157 tools, 336 KB of tools/list, 86 KB of instructions #3898 content PRs (Query Store clutter and oversized-plan backlog: MCP reading guidance moves off tools/list (#3898) #4067, get_pg_log_events/get_pg_index_bloat/get_pg_deadlocks move reading guidance off tools/list (#3898) #4068): a clean merge, no
    conflicts. Rebuilt and reran the full targeted set above, still Failed: 0.
  • Plain-English checker on this PR body: clean in doc mode after fixes. The remaining hits are the D9 labels,
    the attribution emoji, and code spans and table cells, which the brief allows.
  • Full Darling.Tests suite: not run. The context watchdog's first warning landed right after dropcheck
    confirmed clean. This lane's standing orders are to wrap up green at that point, not push through it.
    Everything this PR touches is covered by the targeted run above. The coordinator still needs to run the full
    suite once.

Coordinator verification

  • Merged origin/dev into the branch (merge commit 98c9c26).
  • dropcheck: missing=0 for describe_custom_view_catalog and get_sweep_reports. get_collector_stall_probes has missing=1, the D4 anecdote removal. The rule in that sentence stays.
  • D9: a small model answered all 13 traps from the served text alone, with no misreads. It answered Q6 and Q9 with "not stated". For Q9, "not stated" matches the correct answer.
  • Full suites on 98c9c26: Darling.Tests 13,230 total, 0 failed. The first Lite.Tests run had 1 failure, in McpToolsListBudgetTests, about Lite's get_alert_history, which this PR does not touch. The class passed alone twice, and a second full Lite run passed: 5,232 total, 0 failed. The flake is tracked in Lite budget test sometimes sees get_alert_history's dataService parameter in a full run #4075.

Handoff

Nothing deferred. All three assigned tools are converted, green, and pushed. The full-suite run is the only item
left for the coordinator, per this lane's standing orders on watchdog warnings.

🤖 Generated with Claude Code

https://claude.ai/code/session_01M7r1CXmEcBmsrTV34CCLfF

@erikdarlingdata
erikdarlingdata marked this pull request as ready for review September 23, 2026 21:04
@erikdarlingdata
erikdarlingdata enabled auto-merge (squash) September 23, 2026 21:04
@erikdarlingdata
erikdarlingdata merged commit cdeb5a7 into dev Sep 23, 2026
16 of 18 checks passed
@erikdarlingdata
erikdarlingdata deleted the feature/3898-content-platform-c branch September 23, 2026 21:13
erikdarlingdata added a commit that referenced this pull request Sep 23, 2026
…ntries in their sections (#4080)

Adds 42 entries and 42 link refs (#3992, #3995, #3996, #3998, #4001, #4002, #4003, #4007, #4010, #4011, #4013, #4015, #4020, #4022, #4025, #4029, #4030, #4031, #4036, #4038, #4039, #4040, #4044, #4047, #4048, #4049, #4050, #4051, #4055, #4061, #4063, #4064, #4065, #4066, #4067, #4068, #4069, #4070, #4071, #4073, #4074, #4078). Each PR's entry was buffered, and this lands every entry whose PR was merged on origin/dev when it ran.

#3989 left 26 entries under bare 'Changed' and 'Fixed' lines above '### Added'. They move into '### Changed' and '### Fixed', below the new entries, and one blank line stays under [Unreleased].


Claude-Session: https://claude.ai/code/session_01Ua31ugERL5DmhFVRtf6keQ

Co-authored-by: Claude Opus 5.5 <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