Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 13 additions & 4 deletions Darling/Darling.Tests/FactSourceRegistryTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -112,14 +112,23 @@ public void BothDescriptions_SpellOutTheRegistry()
{
var expected = string.Join(", ", FactScorer.KnownSources);

var darling = typeof(DarlingMcpTools).GetMethods(BindingFlags.Public | BindingFlags.Static)
/* #3898: get_analysis_facts converted, and D2 caps a converted tool's parameter descriptions at 200
characters, so source's own description no longer spells out the registry — it now states the
guardrail (an unrecognized value is refused) and points at the full list, which rides on the tool's
own tail (get_tool_guide) as an appended sentence instead of being lost. Re-pointed here: the
refusal guardrail stays on the parameter description, the full list moves to the tail pin. */
var darlingParam = typeof(DarlingMcpTools).GetMethods(BindingFlags.Public | BindingFlags.Static)
.Single(m => m.GetCustomAttribute<McpServerToolAttribute>()?.Name == "get_analysis_facts")
.GetParameters().Single(p => p.Name == "source")
.GetCustomAttribute<DescriptionAttribute>()!.Description;
Assert.Contains(expected, darling, StringComparison.Ordinal);
Assert.Contains("refused otherwise", darling, StringComparison.Ordinal);
Assert.Contains("refused", darlingParam, StringComparison.Ordinal);

/* Lite's, from source: this project does not reference the desktop app. */
var darlingTail = McpToolGuideTests.Served("get_analysis_facts").Tail!;
Assert.Contains(expected, darlingTail, StringComparison.Ordinal);

/* Lite's, from source: this project does not reference the desktop app. The full list still appears
somewhere in the file (now in the tool's tail rather than the parameter), so this coarser
whole-file check needs no change. */
var lite = RepoFile.ReadRepoFile("Lite", "Mcp", "McpAnalysisTools.cs");
Assert.Contains(expected, lite, StringComparison.Ordinal);
Assert.Contains("FactSourceFilterDescription", lite, StringComparison.Ordinal);
Expand Down
48 changes: 47 additions & 1 deletion Darling/Darling.Tests/McpToolGuideHeads.SqlCore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ namespace Darling.Tests;
/// #3898 D3 head pins for the "sqlCore" family: <c>analyze_server</c> and <c>compare_analysis</c>, converted out
/// of <c>DarlingMcpTools</c> (Darling) and their byte-identical Lite twins (both apps' bodies mirror each other
/// field-for-field, so the original prose was already byte-identical on both SKUs and the heads and tails stay
/// that way here too). Lite's twin is <c>Lite.Tests/McpToolGuideHeadsSqlCoreTests</c>. Follows the pattern in
/// that way here too). <c>get_analysis_facts</c> and <c>get_analysis_findings</c> joined the same way in a later
/// lane. Lite's twin is <c>Lite.Tests/McpToolGuideHeadsSqlCoreTests</c>. Follows the pattern in
/// <see cref="McpToolGuideHeadsDataTests"/>.
/// </summary>
public sealed class McpToolGuideHeadsSqlCoreTests
Expand All @@ -25,6 +26,8 @@ public sealed class McpToolGuideHeadsSqlCoreTests
[
"analyze_server",
"compare_analysis",
"get_analysis_facts",
"get_analysis_findings",
];

/// <summary>The per-tool guardrail phrase each head must state.</summary>
Expand All @@ -41,6 +44,17 @@ private static readonly (string Tool, string Fact)[] HeadFacts =
("compare_analysis", "unavailable = BOTH windows had zero facts"),
("compare_analysis", "coverage_caveat flags a partly-collected side"),
("compare_analysis", "band_source/band_rules say which"),
("get_analysis_facts", "WITHOUT graph traversal: facts, not findings"),
("get_analysis_facts", "so a fact here did not necessarily produce a finding"),
("get_analysis_facts", "differs from a finding's confidence in analyze_server"),
("get_analysis_facts", "collection_caveats is absent on a clean read"),
("get_analysis_facts", "ranked is absent unless 2+ objects ride"),
("get_analysis_findings", "Persisted findings from PAST analysis runs, not a new analysis"),
("get_analysis_findings", "empty: no findings in the window, a true zero"),
("get_analysis_findings", "truncated (see truncation_note) flags a read cap, so occurrence stats may under-report"),
("get_analysis_findings", "confidence_basis flags rows persisted before this scoring existed as path-shape, not corroboration"),
("get_analysis_findings", "remediation_command/structured_remediation are advisory, never executed"),
("get_analysis_findings", "Recurrence fields are LABELS at unchanged severity"),
];

[Fact]
Expand Down Expand Up @@ -83,4 +97,36 @@ public void BothHeads_CarryNoProductQualifier()
Assert.DoesNotContain("Lite", served, StringComparison.Ordinal);
}
}

/// <summary>D2: two parameter descriptions were trimmed to clear the 200-character cap once their tools
/// converted. <c>source</c>'s full accepted-value enumeration (was 515 chars) and <c>include_drilldown</c>'s
/// examples and default-false rationale (was 235 chars) both ride on their tool's own tail instead of being
/// lost — the same pattern <c>compare_analysis.baseline_hours_back</c> set above.</summary>
[Fact]
public void TrimmedParameterOverflows_RideOnTheirOwnTail()
{
Assert.Contains("source accepts: anomaly, bad_actor, blocking", McpToolGuideTests.Served("get_analysis_facts").Tail!, StringComparison.Ordinal);
Assert.Contains("queries, sessions, tempdb, waits; omit it for all.", McpToolGuideTests.Served("get_analysis_facts").Tail!, StringComparison.Ordinal);
Assert.Contains("parameter-sensitive plans or top spill queries behind the finding", McpToolGuideTests.Served("get_analysis_findings").Tail!, StringComparison.Ordinal);
}

/// <summary>The facts payload's total_facts/shown/filters were never documented; the coordinator added
/// one tail sentence in #4083, so an agent reading shown: 0 can tell a filtered-out read from no facts.</summary>
[Fact]
public void FactsPayloadCounts_AreDocumentedInTheTail()
{
Assert.Contains("total_facts counts every scored fact before the source and min_severity filters; shown counts the facts that passed them", McpToolGuideTests.Served("get_analysis_facts").Tail!, StringComparison.Ordinal);
}

/// <summary>D4: <c>get_analysis_findings</c>' two issue references ("(pre-#3538)" naming the confidence-basis
/// migration, "(#3653)" naming the recurrence-label change) are off the wire on both head and tail; the
/// guardrail each carried (a pre-migration row is path-shape, not corroboration; the three recurrence fields
/// and what they mean) stays, worded without the ref. get_analysis_facts carried no issue references.</summary>
[Fact]
public void GetAnalysisFindings_CarriesNoIssueReferences()
{
var full = McpToolGuideTests.Served("get_analysis_findings").Served + McpToolGuideTests.Served("get_analysis_findings").Tail;
Assert.DoesNotContain("#3538", full, StringComparison.Ordinal);
Assert.DoesNotContain("#3653", full, StringComparison.Ordinal);
}
}
8 changes: 4 additions & 4 deletions Darling/Darling.Tests/McpToolsListBudget/DarlingMcpTools.txt
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,17 @@ param compare_analysis.baseline_hours_back 139
param compare_analysis.hours_back 57
param compare_analysis.server_name 28

tool get_analysis_facts 1825
tool get_analysis_facts 585
param get_analysis_facts.as_of 167
param get_analysis_facts.hours_back 36
param get_analysis_facts.min_severity 90
param get_analysis_facts.server_name 28
param get_analysis_facts.source 515
param get_analysis_facts.source 180

tool get_analysis_findings 2875
tool get_analysis_findings 610
param get_analysis_findings.as_of 167
param get_analysis_findings.hours_back 49
param get_analysis_findings.include_drilldown 235
param get_analysis_findings.include_drilldown 122
param get_analysis_findings.server_name 28

tool mute_analysis_finding 1444
Expand Down
Loading
Loading