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
5 changes: 4 additions & 1 deletion CodeSmellAuditor.Core.Tests/AuditPromptBuilderTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,10 @@ public void BuildSystemPrompt_IncludesRulesAndOutputTemplate()

Assert.Contains("[RULE: test.mdc]", prompt);
Assert.Contains("Prefer interfaces at boundaries.", prompt);
Assert.Contains("Status: COMPLIANT or REVIEW REQUIRED", prompt);
Assert.Contains("Status: COMPLIANT", prompt);
Assert.Contains("REVIEW REQUIRED", prompt);
Assert.Contains("EXACTLY one of", prompt);
Assert.Contains("omit Missing Context", prompt);
Assert.Contains("No analysis traces", prompt);
}

Expand Down
38 changes: 35 additions & 3 deletions CodeSmellAuditor.Core.Tests/AuditReportParserTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,47 @@ public class AuditReportParserTests
[InlineData("Status: REVIEW REQUIRED", false)]
[InlineData("# Report\nStatus: COMPLIANT\nScore: 90", true)]
[InlineData("# Report\nStatus: REVIEW REQUIRED\nScore: 40", false)]
public void ParsePassStatus_ReadsStatusLine(string body, bool expectedPass)
[InlineData("status: compliant", true)]
[InlineData("Status: Review Required", false)]
public void ParsePassStatus_ReadsExactStatusLine(string body, bool expectedPass)
{
Assert.Equal(expectedPass, AuditReportParser.ParsePassStatus(body));
}

[Fact]
public void ParsePassStatus_FallsBackWhenNoStatusLine()
public void ParsePassStatus_MissingStatusLine_FailsClosed()
{
Assert.False(AuditReportParser.ParsePassStatus("Some text with REVIEW REQUIRED in it"));
Assert.True(AuditReportParser.ParsePassStatus("All checks passed."));
Assert.False(AuditReportParser.ParsePassStatus("All checks passed."));
Assert.False(AuditReportParser.ParsePassStatus("This report looks COMPLIANT overall."));
}

[Fact]
public void ParsePassStatus_EmptyOrWhitespace_FailsClosed()
{
Assert.False(AuditReportParser.ParsePassStatus(""));
Assert.False(AuditReportParser.ParsePassStatus(" \n\t "));
Assert.False(AuditReportParser.ParsePassStatus(null!));
}

[Fact]
public void ParsePassStatus_UnknownStatusValue_FailsClosed()
{
Assert.False(AuditReportParser.ParsePassStatus("Status: PASS"));
Assert.False(AuditReportParser.ParsePassStatus("Status: COMPLIANT WITH NOTES"));
Assert.False(AuditReportParser.ParsePassStatus("Status:"));
}

[Fact]
public void ParsePassStatus_TruncatedReportWithoutStatus_FailsClosed()
{
string truncated = """
# Audit Report: SampleService.cs

## Top Findings
- Domain logic references UI directly
""";

Assert.False(AuditReportParser.ParsePassStatus(truncated));
}
}
27 changes: 22 additions & 5 deletions CodeSmellAuditor.Core/AuditPromptBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ public static class AuditPromptBuilder

# Audit Report: {filename}

Status: COMPLIANT or REVIEW REQUIRED
Score: 0-100
Status: COMPLIANT
Score: 85

## Top Findings
- (max 5 bullets, one line each)
Expand All @@ -27,6 +27,11 @@ public static class AuditPromptBuilder

## Recommended Next Step
One sentence.

Contract rules:
- The Status line must appear exactly once and use EXACTLY one of: COMPLIANT or REVIEW REQUIRED (nothing else on that line after Status:).
- The Score line must appear exactly once as an integer from 0 to 100.
- Emit Status, Score, and Top Findings first. If the output budget is tight, omit Missing Context and/or Recommended Next Step rather than truncating or omitting Status.
""";

public static string BuildSystemPrompt(IEnumerable<AuditRule> rules)
Expand Down Expand Up @@ -67,8 +72,16 @@ public static void ValidateBudget(string systemPrompt, string userPrompt, AuditC

public static class AuditReportParser
{
/// <summary>
/// Pass/fail from a dedicated Status line only. Missing or unparseable Status fails closed.
/// </summary>
public static bool ParsePassStatus(string reportBody)
{
if (string.IsNullOrWhiteSpace(reportBody))
{
return false;
}

foreach (string line in reportBody.Split('\n'))
{
string trimmed = line.Trim();
Expand All @@ -78,17 +91,21 @@ public static bool ParsePassStatus(string reportBody)
}

string statusValue = trimmed["Status:".Length..].Trim();
if (statusValue.Contains("REVIEW REQUIRED", StringComparison.OrdinalIgnoreCase))
if (statusValue.Equals("REVIEW REQUIRED", StringComparison.OrdinalIgnoreCase))
{
return false;
}

if (statusValue.Contains("COMPLIANT", StringComparison.OrdinalIgnoreCase))
if (statusValue.Equals("COMPLIANT", StringComparison.OrdinalIgnoreCase))
{
return true;
}

// Status line present but not an allowed value: fail closed.
return false;
}

return !reportBody.Contains("REVIEW REQUIRED", StringComparison.OrdinalIgnoreCase);
// No Status line (truncated or drifted report): fail closed.
return false;
}
}
Loading