Skip to content

get_collection_health's reading guide moves off tools/list (#3898) - #4063

Merged
erikdarlingdata merged 4 commits into
devfrom
feature/3898-content-collection-health
Sep 23, 2026
Merged

erikdarlingdata merged 4 commits into
devfrom
feature/3898-content-collection-health

Conversation

@erikdarlingdata

@erikdarlingdata erikdarlingdata commented Sep 23, 2026 •

Copy link
Copy Markdown
Owner

Refs #3898.

Why

get_collection_health was the single biggest description in the catalog: 21,026 served characters on both
Darling and Lite (identical text, one physical home each: DarlingMcpDataTools.cs / McpHealthTools.cs). A
client that loads tools/list paid for all of it before asking a question. This PR converts exactly that one
tool: a <=620-char head stays on tools/list, the full reading guide moves to get_tool_guide.

What changes

  • get_collection_health (Darling and Lite): head/tail split via the <<GUIDE>> marker. Heads are
    byte-identical on both SKUs (the original text already was, so no per-SKU divergence to reconcile).
  • Head states the five guardrail facts most likely to cause a misread from a truncated payload: STOPPED vs
    FAILING (gate off does not count as failing), rows_stored=0 is not automatically a fault, last_error is a
    sticky slot rather than a live-condition signal, regressed_from_productive is a separate axis from
    failed_collector_count (never add them), and alert_read_health uses a different since-restart window (not
    the 7-day one everything else on this payload uses).
  • Tail carries every other original sentence verbatim, in original order, minus the D4 removals below.
  • No topic extracted: this lane converts one tool, and none of its prose repeats text already carried by an
    existing topic from another converted family.

Per-tool served chars (both SKUs identical):

Tool Before -> after
get_collection_health 21,026 -> 583

Corrections

None. Original prose was verified against the surrounding paragraph structure only (this lane did not cross-check
against the C# body given the deadline); no factual error found in what was read.

D4 removals (6, all in the tail; the dropcheck script reports each as one "missing" line)

  1. The ".453 install" pg_statement_stats story (85% productive -> EXTENSION_MISSING on 23 of 50 PostgreSQL
    clusters). Deleted whole; it was pure anecdote with no separable rule. The two sentences immediately after it
    (last_productive_at is... and get_fleet_overview counts...) DID move to the tail unchanged; the
    dropcheck script reports all three as one "missing" chunk only because its sentence splitter merges runs that
    continue in lowercase, not because two of the three were dropped.
  2. The "before The alert pass retries a store read once, two seconds later, before counting it failed — every alert-read kill this week was a transient inside a write band that a retry survives #3848" issue reference in the retried_reads paragraph. Deleted the reference only; kept the
    rule ("Every one of those was a swallowed failure, so read the two together...").
  3. The query_store dogfood-server anecdote (13,834 ms avg over 1,155 runs, ~80,900 ms per productive run).
    Kept the rule before it ("...because a collector's runs are not always one population.") and the rule after
    it ("Read the three together: avg close to p95...").
  4. The dominance-numbers anecdote (3.58 over 72 items vs 4.20 over 15 items). Kept "Dominance is NOT that
    verdict, because its ceiling is items." and "A fixed threshold on dominance misreads exactly the widest
    fan-outs..." on either side.
  5. The pg_deadlocks 49,258,335 ms / 79,333-run story. Kept "This is deliberately NOT a band." before it and
    "A verdict keyed on cost-plus-zero-rows would fire on the healthy quiet install rather than the blind one."
    after it.
  6. The pg_deadlocks in-database-to-AWS-API-route story (the six-days-later stale-denial bug report). Kept "A
    fault recorded before a code path changed will sit in last_error for the rest of the window while every cycle
    since succeeds." before it and "Do not infer a live condition from last_error alone." after it.

Verified with dropcheck.py origin/dev HEAD get_collection_health: missing=6 on both Darling and Lite,
exactly these six lines, nothing else.

Re-pointed pins (2, both mechanical)

  1. Darling.Tests.McpToolGuideTests.GetToolGuide_WithNoArguments_IsTheIndex (and no Lite twin needed - Lite's
    equivalent fixture host does not wire McpHealthTools): the fixture's ordinal tools_with_guides list now
    has get_collection_health first (before get_file_io_stats), since this fixture's host registers
    DarlingMcpDataTools and the tool joined the guide-bearing roster. Added the name in its correct ordinal
    slot.
  2. Darling.Tests.CollectionOutputBesideCostTests.BothSkusToolDescriptions_TeachThatACorrectZeroExists and its
    Lite twin: both asserted the literal string "that zero was CORRECT", which was inside the pg_deadlocks
    anecdote deleted by D4 Lite: collector health status bar shows global errors instead of per-server #5. Re-pointed to "would fire on the healthy quiet install", the kept rule
    immediately after it, which teaches the identical fact (a zero can be the healthy reading) the test's name
    describes.

D9 traps (5, for this one tool)

  1. Q: A collector shows status: STOPPED and 0 runs this window - does it count toward the server's
    failing-collector total?
    Correct: no - STOPPED means its gate (AppliesTo) turned off for this target, not that it is erroring.
    Prevents it: head's "STOPPED (gate off) does not count as failing."
  2. Q: A row shows rows_stored: 0 - is that collector broken?
    Correct: not necessarily - output_finding says whether it is a resting event-collector (correct) or a
    denial (needs a grant); a zero from an event-driven collector (deadlocks, blocking, etc.) is the healthy
    resting state.
    Prevents it: head's "rows_stored=0 is NOT a fault by itself - output_finding says whether it is a resting
    event-collector or a denial needing a grant."
  3. Q: last_error shows a permission-denied message - is that condition happening right now?
    Correct: not necessarily - last_error is a sticky slot that can hold a stale message from before a code
    path changed; check last_error_at/denied_since_last_success for currency.
    Prevents it: head's "last_error is a sticky slot, not necessarily current: check
    last_error_at/denied_since_last_success."
  4. Q: A server shows both regressed_collector_count: 1 and failed_collector_count: 1 - are there 2
    distinct problems, or is this the same collector counted twice?
    Correct: could be either - the two are a SEPARATE axis and are never meant to be added; a regression can
    read WARNING on day one and FAILING once its success clock runs out, counted on both.
    Prevents it: head's "regressed_from_productive floors WARNING on an axis SEPARATE from
    failed_collector_count - never add them."
  5. Q: alert_read_health.server_read_failures reads 0 for the trailing 7 days shown elsewhere on this
    payload - is that 7 days of clean alerting?
    Correct: no - this block counts in-memory since the service's last restart (counting_since), not the
    7-day collector window everything else on this payload uses; a recent restart makes a 0 uninformative.
    Prevents it: head's "alert_read_health uses a DIFFERENT since-restart window, not this one; zero there
    means none since restart, not none in 7 days."

Test plan

  • Build Darling.Tests and Lite.Tests: 0 Warning(s), 0 Error(s) both.
  • dropcheck.py origin/dev HEAD get_collection_health: missing=6 on both SKUs, exactly the D4 list above.
  • Targeted run, Darling.Tests (23 classes: every test file git grep found mentioning
    get_collection_health, plus the generic MCP guide/budget infrastructure): 474 total, 0 failed, 9 skipped
    (live-Postgres, no rig).
  • Targeted run, Lite.Tests (13 classes, same method): 258 total, 0 failed, 0 skipped.
  • Full suites, run by the coordinator on e1da486 (this branch merged with origin/dev at the time): both test projects build with 0 warnings. Darling.Tests: 13,120 total, 0 failed, 631 skipped (live PostgreSQL, no rig). Lite.Tests: 5,224 total, 0 failed. After Move get_alert_settings/get_notification_routes reading guidance off tools/list (#3898) #4061 merged, the coordinator merged origin/dev again and derived the get_tool_guide index test's roster (1c5694c). McpToolGuide* and McpToolsListBudget* pass on both products.
  • Live-Postgres classes: skip without a rig (unchanged by this PR; none of them assert on tool description
    text).

Handoff

Nothing else in this family was in this lane's brief (it was scoped to exactly this one tool). No other tool
touched.

🤖 Generated with Claude Code

https://claude.ai/code/session_01M7r1CXmEcBmsrTV34CCLfF

erikdarlingdata and others added 2 commits September 23, 2026 14:55
…ad stays under budget (#3898)

Splits the single biggest MCP tool description (21,026 served chars) into a <=620-char head that
tools/list still serves and a full reading-guide tail that get_tool_guide serves on demand, on both
Darling and Lite (byte-identical, since the source text already was). Five anecdotes-with-numbers and
one stale issue reference come off the wire per D4; the rule each one taught stays in the tail.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01M7r1CXmEcBmsrTV34CCLfF
… tense without the issue reference (#3898)

Dropping 'before #3848' (D4) left 'Every one of those was a swallowed failure', which reads as current. It now says each retried read would once have been a swallowed failure.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Claude-Session: https://claude.ai/code/session_01M7r1CXmEcBmsrTV34CCLfF
@erikdarlingdata

Copy link
Copy Markdown
Owner Author

Coordinator D9 misreading check (haiku, served tools/list text only, no get_tool_guide): traps 1-5 correct, with correct reasoning, each answer quoting the head. The PR lists only 5 traps, so I added 4 probes of rules that stay in the tail: avg vs p95 durations, dominance vs share, retried_reads rising with failures at zero, and an expensive collector that stored zero rows. There was no confident misread. For each one the model said the served text does not settle it and it would call get_tool_guide. dropcheck: missing=6 on both SKUs, exactly the six D4 removals listed. Coordinator commit e1da486: dropping 'before #3848' had left 'Every one of those was a swallowed failure', which reads as current. It now reads 'would once have been a swallowed failure', on both SKUs, in the tail only. The coordinator is running both full suites now.

…r-bearing tools (#3898)

The hand-kept list of converted tools made every content PR that converts a Data (Darling) or Discovery (Lite) tool edit the same lines, so two such PRs in flight conflict. The expected roster is now every tool on the fixture's types whose Description carries the marker, sorted ordinally.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Claude-Session: https://claude.ai/code/session_01M7r1CXmEcBmsrTV34CCLfF
@erikdarlingdata
erikdarlingdata marked this pull request as ready for review September 23, 2026 19:07
@erikdarlingdata
erikdarlingdata enabled auto-merge (squash) September 23, 2026 19:07
@erikdarlingdata
erikdarlingdata merged commit c7760e0 into dev Sep 23, 2026
16 of 18 checks passed
@erikdarlingdata
erikdarlingdata deleted the feature/3898-content-collection-health branch September 23, 2026 19:16
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