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
146 changes: 146 additions & 0 deletions OrchestratorIDE.UnitTests/ContextFabricOpenExtractionTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,96 @@ public async Task ReadCorpusAsync_WithOpenExtractionReading_AcceptsClaimsFromUnM
});
}

[Test]
public async Task ReadCorpusAsync_OpenExtraction_RepairRecoversFactTheFirstPassMissedEntirely()
{
// Regression guard for the CF-7 gate finding (2026-07-17): a compliant open-extraction
// reader that returns zero claims for a segment previously short-circuited straight to
// rejection -- the missing-evidence repair pass (which exists for marked mode) never even
// ran, because it lived after an early return on "claims must contain at least 1 item".
// A segment whose one real fact sits among filler lines should now get a second, best-
// effort look before being rejected.
var fixture = DeterministicExpandedFabricCorpus.Create();
var fact = fixture.Manifest.LocalFacts[0];
var segment = fixture.Corpus.Segments.Single(s => s.SegmentId == fact.SegmentId);
var singleSegmentCorpus = fixture.Corpus with { Segments = [segment] };

var runtime = new OpenExtractionRepairScriptedRuntime(
initialClaimStatements: [],
repairClaimStatements: [fact.StatementText]);
var options = FabricRunOptions.Default with { OpenExtractionReading = true };
var runner = new ContextFabricFeasibilityRunner(runtime, options);

var report = await runner.ReadCorpusAsync(singleSegmentCorpus);

Assert.Multiple(() =>
{
Assert.That(runtime.RepairCallCount, Is.EqualTo(1), "the empty first pass must trigger exactly one repair attempt");
Assert.That(report.SegmentResults[0].Accepted, Is.True, string.Join("; ", report.SegmentResults[0].Errors));
Assert.That(report.SegmentResults[0].Card!.Claims.SelectMany(c => c.Citations).Select(c => c.Quote),
Has.Some.EqualTo(fact.StatementText));
});
}

[Test]
public async Task ReadCorpusAsync_OpenExtraction_RepairDecliningEveryCandidate_StillRejectsTheSegment()
{
// The repair prompt is allowed to return zero claims (every flagged candidate turns out to
// be filler) -- the completeness check must not paper over a genuinely fact-free segment by
// force-accepting it just because a repair call happened.
var fixture = DeterministicExpandedFabricCorpus.Create();
var fact = fixture.Manifest.LocalFacts[0];
var segment = fixture.Corpus.Segments.Single(s => s.SegmentId == fact.SegmentId);
var singleSegmentCorpus = fixture.Corpus with { Segments = [segment] };

var runtime = new OpenExtractionRepairScriptedRuntime(
initialClaimStatements: [],
repairClaimStatements: []);
var options = FabricRunOptions.Default with { OpenExtractionReading = true };
var runner = new ContextFabricFeasibilityRunner(runtime, options);

var report = await runner.ReadCorpusAsync(singleSegmentCorpus);

Assert.Multiple(() =>
{
Assert.That(runtime.RepairCallCount, Is.EqualTo(1));
Assert.That(report.SegmentResults[0].Accepted, Is.False);
});
}

[Test]
public async Task ReadCorpusAsync_OpenExtraction_AllCandidateCodesAlreadyCovered_SkipsRepairEntirely()
{
// A segment whose reader output already cites every candidate-coded line must not pay for
// a repair call it doesn't need. This corpus densely packs multiple planted facts per
// segment (verified: xseg-0001 alone carries a local fact, a chain hop, and a ledger row),
// so the fixture segment must be seeded with ALL of its coded lines, not just one, or the
// completeness check would (correctly) still flag the others as missing.
var fixture = DeterministicExpandedFabricCorpus.Create();
var fact = fixture.Manifest.LocalFacts[0];
var segment = fixture.Corpus.Segments.Single(s => s.SegmentId == fact.SegmentId);
var singleSegmentCorpus = fixture.Corpus with { Segments = [segment] };
var everyCodedLine = segment.Text
.Split(['\r', '\n'], StringSplitOptions.RemoveEmptyEntries)
.Where(line => System.Text.RegularExpressions.Regex.IsMatch(line, @"\b[A-Za-z]{2,10}-[0-9][0-9A-Za-z-]{0,12}\b"))
.ToArray();
Assert.That(everyCodedLine, Has.Some.EqualTo(fact.StatementText), "sanity: the fixture fact must be one of the coded lines");

var runtime = new OpenExtractionRepairScriptedRuntime(
initialClaimStatements: everyCodedLine,
repairClaimStatements: []);
var options = FabricRunOptions.Default with { OpenExtractionReading = true };
var runner = new ContextFabricFeasibilityRunner(runtime, options);

var report = await runner.ReadCorpusAsync(singleSegmentCorpus);

Assert.Multiple(() =>
{
Assert.That(runtime.RepairCallCount, Is.EqualTo(0), "every coded line is already covered -- no repair call is needed");
Assert.That(report.SegmentResults[0].Accepted, Is.True);
});
}

[Test]
public async Task ReadCorpusAsync_WithDefaultMarkedReading_RejectsUnMarkedSegment_WithZeroClaims()
{
Expand Down Expand Up @@ -98,4 +188,60 @@ public async IAsyncEnumerable<string> StreamRoleCompletionAsync(
public RuntimeStats GetStats(RuntimeRole? role = null) => new(RuntimeName, "scripted.gguf");
public string? GetLastPromptPath(RuntimeRole role) => "Scripted";
}

/// <summary>Distinguishes the initial [FABRIC_READER_OPEN] call from the follow-up
/// [FABRIC_READER_OPEN_REPAIR] call by system-message tag, so a test can script each
/// independently and assert on how many repair calls actually happened.</summary>
private sealed class OpenExtractionRepairScriptedRuntime(
IReadOnlyList<string> initialClaimStatements,
IReadOnlyList<string> repairClaimStatements)
: IRoleRuntime, IRoleRuntimeDiagnostics
{
public string RuntimeName => "scripted-open-extraction-repair";
public int RepairCallCount { get; private set; }

public async IAsyncEnumerable<string> StreamRoleCompletionAsync(
RuntimeRole role,
IEnumerable<AgentMessage> history,
IReadOnlyList<object>? tools = null,
double temperature = 0.1,
int maxTokens = 4096,
Action<ToolCall>? onToolCall = null,
Action<int, int>? onUsage = null,
[EnumeratorCancellation] CancellationToken ct = default)
{
await Task.Yield();
ct.ThrowIfCancellationRequested();
var messages = history.ToArray();
var isRepair = messages.Any(m => m.Role == MessageRole.System
&& m.Content.Contains("[FABRIC_READER_OPEN_REPAIR]", StringComparison.Ordinal));
if (isRepair) RepairCallCount++;

var input = messages.Last(m => m.Role == MessageRole.User).Content;
using var doc = JsonDocument.Parse(input);
var root = doc.RootElement;
var corpusId = root.GetProperty("corpusId").GetString()!;
var documentId = root.GetProperty("documentId").GetString()!;
var segmentId = root.GetProperty("segmentId").GetString()!;

var statements = isRepair ? repairClaimStatements : initialClaimStatements;
var claims = statements.Count == 0
? "[]"
: "[" + string.Join(",", statements.Select((statement, i) =>
$"{{\"claimId\":\"c{i + 1}\",\"type\":\"assertion\",\"text\":{JsonSerializer.Serialize(statement)}," +
$"\"confidence\":1.0,\"citations\":[{{\"segmentId\":\"{segmentId}\",\"charStart\":-1,\"charEnd\":-1," +
$"\"quote\":{JsonSerializer.Serialize(statement)},\"quoteDigest\":\"\"}}]}}")) + "]";

yield return "{\"schemaVersion\":\"cf0-evidence-card-1.0\"," +
$"\"corpusId\":{JsonSerializer.Serialize(corpusId)}," +
$"\"documentId\":{JsonSerializer.Serialize(documentId)}," +
$"\"segmentId\":{JsonSerializer.Serialize(segmentId)}," +
"\"promptVersion\":\"cf0-reader-1.2\",\"summary\":\"open extraction summary\"," +
$"\"claims\":{claims},\"entities\":[],\"conflicts\":[],\"openQuestions\":[]}}";
}

public RuntimeHealth GetHealth(RuntimeRole? role = null) => new(true, RuntimeName, "scripted.gguf");
public RuntimeStats GetStats(RuntimeRole? role = null) => new(RuntimeName, "scripted.gguf");
public string? GetLastPromptPath(RuntimeRole role) => "Scripted";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,11 @@ private static IReadOnlyList<FabricBenchmarkMetric> BuildMetrics(
if (singleNodeContextFabric is not null)
{
var summary = singleNodeContextFabric.Summary;
// Stays fully blocking (unlike question_pass_rate below): a rejected segment is a
// real recall failure the reader should not have, not an inherently-hard question
// this benchmark treats as a stretch goal. ContextFabricFeasibilityRunner's
// open-extraction completeness-repair pass (ReadSegmentAsync, added 2026-07-17) is
// what's supposed to close this to 100/100 -- if it doesn't, that is real signal.
metrics.Add(new FabricBenchmarkMetric(
"segment_terminal_coverage",
Ratio(summary.AcceptedSegments, summary.ExpectedSegments),
Expand Down
Loading
Loading