Skip to content

Tier 2.5 reference chasing + boundary-stitch schema fix + CF model pinning - #42

Merged
hardcoreerik merged 10 commits into
masterfrom
feat/cf-citation-stitch-tier3
Jul 9, 2026
Merged

Tier 2.5 reference chasing + boundary-stitch schema fix + CF model pinning#42
hardcoreerik merged 10 commits into
masterfrom
feat/cf-citation-stitch-tier3

Conversation

@hardcoreerik

@hardcoreerik hardcoreerik commented Jul 8, 2026

Copy link
Copy Markdown
Owner

Summary

Findings-driven follow-up to the first complete 120-question CF-7 run (58/120, PR #40's merged fix stack). Status after 5 live 120-question validation cycles (~3h): 2 gates genuinely fixed and stable; MultiHop retrieval fixed and measurably improved but capped by a separate, now-identified model-compliance ceiling.

  • Boundary-stitch root cause CLOSED: THEORC_STITCH_RAW_DIR full-raw capture revealed Meta-Llama emits linkedFacts as objects instead of strings. NormalizeLinkedFactObjects flattens that shape before parsing. Live-verified: 0/2 → 2/2 PASS, held stable across all 5 re-runs.
  • Citation precision clears its gate: 89.5% → 90–92% (gate is 0.9), held stable across all 5 re-runs.
  • Tier 2.5 — reference chasing + budget reservation: BuildEvidencePack follows tracked identifiers (RPT-064-style tokens) from included cards into linked segments, and reserves 30% of the evidence budget for MultiHop questions so the chase always gets a turn. Three real bugs found and fixed along the way (wrong text field, an overly strict doc-frequency cap, and the budget-ordering bug itself). The fix measurably worked: MultiHop full-retrieval rose 6 → 9/24 — the first change in the retrieval distribution across 5 cycles. But the pass rate stayed flat, because every one of those 9 fully-retrieved cases fails on an identical, separate gap: the model attaches only 1 of 2 (or 0 of 4) required citations despite an explicit prompt instruction. This is a model-instruction-compliance ceiling, not a retrieval or code defect — closed as such in CF_RETRIEVAL_IMPROVEMENT_PLAN.md §3c with three scoped follow-up options (few-shot, schema-enforced citation count, larger model).
  • CF model pinning: --model <substring> / THEORC_CF_MODEL / -Model script param restrict depot role resolution (ModelDepot.WithBaseModelFilter), so shared-depot churn can't silently swap or break a benchmark run.

Test plan

  • Unit suite green: 497 total, 0 failed
  • Boundary-stitch live validation: 0/2 → 2/2 PASS, stable across 5 re-runs
  • Citation precision live validation: clears the 0.9 gate, stable across 5 re-runs
  • MultiHop retrieval fix validated live: full-retrieval count 6 → 9/24 (measurable, reproducible improvement)
  • 5 full 120-question gate re-runs on NEWCOREPC (Meta-Llama), 0 NoKvSlot each time
  • Remaining MultiHop gap root-caused to model citation-compliance, not code — documented with next steps rather than left open-ended

🤖 Generated with Claude Code

Findings-driven fixes from the first full 120-question run (58/120):

1. Reference chasing (Tier 2.5): 18/24 MultiHop misses were partial
   retrieval -- chain segments linked by identifiers (RPT-064) the question
   never names. BuildEvidencePack now follows rare tracked identifiers from
   included cards into the cards they link to, iteratively (5-hop chains),
   doc-frequency-capped so corpus-filler codes are never chased.

2. Multi-hop answer prompt: the remaining 6/24 retrieved everything but
   answered tersely (final value only, one citation). The prompt now
   mechanically requires the full chain spelled out and one citation per
   link.

3. Boundary-stitch root cause CLOSED (CF_TEST_RESULTS.md section 5):
   full-raw-output capture (THEORC_STITCH_RAW_DIR) showed Meta-Llama emits
   linkedFacts as objects ({factId,text,type}) instead of strings.
   NormalizeLinkedFactObjects flattens that shape before parsing; prompt now
   demands plain strings and the referenced fact as its own entry.
   Live re-run on Meta-Llama: 0/2 -> 2/2 PASS.

4. CF model pinning: --model <substring> / THEORC_CF_MODEL / -Model script
   param restrict depot role resolution via ModelDepot.WithBaseModelFilter,
   so shared-depot changes (models toggled by Foundry work) cannot silently
   swap or break a benchmark run (two exit-66 false starts on 2026-07-07).

Unit suite: 496 total, 0 failed (6 new tests).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

This PR tightens Context Fabric evidence selection, adds multi-hop reference chasing, normalizes stitched linkedFacts before parsing, and threads model pinning through the benchmark tool and gate script.

Changes

Context Fabric changes

Layer / File(s) Summary
Budget-gated evidence inclusion
OrchestratorIDE/Services/ContextFabric/ContextFabricFeasibilityRunner.cs
Adds budget-gated inclusion helpers and routes greedy evidence selection through projected token checks while preserving uncovered anchors, pairs, and terms when candidates are skipped.
Reference chasing across evidence hops
OrchestratorIDE/Services/ContextFabric/ContextFabricFeasibilityRunner.cs, OrchestratorIDE.UnitTests/ContextFabricEvidencePackTests.cs, docs/CF_RETRIEVAL_IMPROVEMENT_PLAN.md, docs/CF_TEST_RESULTS.md
Adds bounded multi-hop chasing over tracked identifiers, updates evidence-pack tests for chained and capped traversal, and records the run results and follow-up plan in the docs.
LinkedFacts normalization in stitcher
OrchestratorIDE/Services/ContextFabric/FabricBoundaryStitcher.cs, OrchestratorIDE.UnitTests/ContextFabricCf3Tests.cs
Adds normalization for object-shaped linkedFacts before deserialization, adds optional raw-output persistence, updates the stitch prompt contract, and adds unit tests for flattening, passthrough, and non-JSON fallback.
Model pinning support
ModelDepot.cs, Tools/ContextFabricBench/Program.cs, Tools/ContextFabricBench/Run-CF7GateExpanded.ps1
Adds base-model substring filtering, threads model pin values through the benchmark CLI, and adds a matching script parameter and argument forwarding.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant BuildEvidencePack
  participant TryInclude
  participant ChaseTrackedReferences

  BuildEvidencePack->>TryInclude: evaluate candidate against EvidenceLimit
  TryInclude-->>BuildEvidencePack: include or skip
  BuildEvidencePack->>ChaseTrackedReferences: chase tracked identifiers in included cards
  ChaseTrackedReferences-->>BuildEvidencePack: bounded chased evidence
Loading

Possibly related PRs

  • hardcoreerik/TheOrc#34: Both PRs modify OrchestratorIDE/Services/ContextFabric/ContextFabricFeasibilityRunner evidence-selection logic.
  • hardcoreerik/TheOrc#40: Both PRs change OrchestratorIDE/Services/ContextFabric/ContextFabricFeasibilityRunner.cs token-budget gating and evidence-context limits.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main changes: reference chasing, boundary-stitch schema normalization, and model pinning.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/cf-citation-stitch-tier3

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
Tools/ContextFabricBench/Program.cs (1)

140-158: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Misleading diagnostics when the pin matches nothing.

depot is reassigned to the filtered result before PrintDepotDiagnostics(depot, modelRoot) is called on failure (line 156). If modelPin matches zero base models, the diagnostics will report (no active base-model .gguf files found) even though the model root actually contains models — just none matching the substring. The "Model pin" line printed earlier gives a partial hint, but the failure diagnostics themselves are factually wrong about the depot's real contents, which can misdirect troubleshooting on long (4-6 hour) benchmark runs.

🩹 Proposed fix: clarify the pin-mismatch case
             if (researcher is null || (reviewer is null && requiresReviewer))
             {
                 Console.Error.WriteLine($"No native base GGUF was resolved beneath '{Path.GetFullPath(modelRoot)}'.");
+                if (!string.IsNullOrWhiteSpace(modelPin))
+                    Console.Error.WriteLine($"  Model pin '{modelPin}' may have filtered out all otherwise-eligible base models.");
                 PrintDepotDiagnostics(depot, modelRoot);
                 return 65;
             }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Tools/ContextFabricBench/Program.cs` around lines 140 - 158, The failure
diagnostics in Program.cs are misleading after applying modelPin because depot
is overwritten before PrintDepotDiagnostics is called, so a pin that matches
nothing looks like the model root has no GGUFs at all. Update the flow around
ModelDepot.Scan, WithBaseModelFilter, and PrintDepotDiagnostics so the
diagnostic output can distinguish “no models under modelRoot” from “modelPin
filtered out all models” (for example by preserving the unfiltered depot or
passing the pin context into the diagnostics). Keep the existing Model pin
message, but make the error path report the actual mismatch clearly when
ResolveRole fails.
🧹 Nitpick comments (5)
OrchestratorIDE.UnitTests/ContextFabricEvidencePackTests.cs (1)

349-352: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Assert that no filler cards were chased.

This fixture’s filler cards do not match the question terms, so any seg-fill-* inclusion would indicate chase leakage. Count < 7 would still pass if the chase incorrectly added only part of the filler set.

Proposed test tightening
         Assert.That(pack.IncludedSegmentIds, Does.Contain("seg-seed"));
-        // Filler cards may enter via normal term scoring, but the chase must not add all of
-        // them: the identifier occurs in 7 cards, above the cap of 4.
-        Assert.That(pack.IncludedSegmentIds.Count, Is.LessThan(7));
+        Assert.That(pack.IncludedSegmentIds.Where(id => id.StartsWith("seg-fill-", StringComparison.Ordinal)),
+            Is.Empty);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@OrchestratorIDE.UnitTests/ContextFabricEvidencePackTests.cs` around lines 349
- 352, The test in ContextFabricEvidencePackTests should be tightened because
Count < 7 still allows partial filler-card chase leakage; update the assertions
around IncludedSegmentIds so they explicitly verify that no seg-fill-*
identifiers are present, while keeping the existing seg-seed check. Use the
pack.IncludedSegmentIds collection directly in the test to assert absence of any
filler segment IDs rather than relying on a count threshold.
Tools/ContextFabricBench/Program.cs (1)

473-478: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Usage text doesn't document --model.

PrintUsage (lines 504-520) isn't updated to mention the new --model <substring> option or its THEORC_CF_MODEL env-var equivalent, so operators running --help won't discover the pinning feature this PR adds.

📝 Proposed usage line
         Console.WriteLine("  --max-questions <n>       Cap questions processed (useful for smoke tests; default: all)");
+        Console.WriteLine("  --model <substring>       Pin role resolution to a base GGUF matching this DisplayName substring (env: THEORC_CF_MODEL)");
         Console.WriteLine("  --segments <n>            Scale-suite segment count (default 640)");
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Tools/ContextFabricBench/Program.cs` around lines 473 - 478, Update
PrintUsage so it documents the new model pinning option added in CliOptions
parsing: mention --model <substring> and its THEORC_CF_MODEL
environment-variable equivalent alongside the other CLI flags. Make sure the
usage text clearly explains that this maps to the modelPin value so operators
can discover the feature from --help.
Tools/ContextFabricBench/Run-CF7GateExpanded.ps1 (2)

113-116: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Comment-based help doesn't document -Model.

The .PARAMETER block above (covering MaxQuestions, Context, GpuLayers, SkipBuild, LogFile) has no corresponding entry for the new -Model parameter, so Get-Help output won't describe it despite the inline comment explaining its purpose.

📝 Proposed doc addition
 .PARAMETER LogFile
     Path to write a copy of stdout. Defaults to OutputDir/cf7_expanded_<timestamp>_console.log.
     Set to empty string to disable log capture.
+
+.PARAMETER Model
+    Pin role resolution to a GGUF whose filename contains this substring (e.g. "Meta-Llama").
+    Protects the run from other models being added/disabled in a shared depot mid-run.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Tools/ContextFabricBench/Run-CF7GateExpanded.ps1` around lines 113 - 116, The
comment-based help for Run-CF7GateExpanded.ps1 is missing documentation for the
new Model parameter, so update the .PARAMETER block that already documents
MaxQuestions, Context, GpuLayers, SkipBuild, and LogFile to include Model.
Describe its purpose consistently with the inline comment near the parameter
declaration so Get-Help on Run-CF7GateExpanded and its parameter metadata
includes -Model.

249-252: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Static analyzer: $Args shadows the automatic variable.

PSScriptAnalyzer flags this reuse of the automatic $args/$Args variable name (case-insensitive in PowerShell). This is a pre-existing pattern in the file (line 232) extended here rather than introduced, but a rename would be a clean, low-risk fix given the linter is already flagging it.

♻️ Proposed rename (applies to line 232 onward too)
-$Args = @(
+$BenchArgs = @(
     "--suite",            "cf7-gate-expanded",
     ...
 )
 ...
-if ($Model) {
-    $Args += @("--model", $Model)
-}
+if ($Model) {
+    $BenchArgs += @("--model", $Model)
+}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Tools/ContextFabricBench/Run-CF7GateExpanded.ps1` around lines 249 - 252, The
script uses `$Args`, which conflicts with PowerShell’s automatic `$args`
variable and triggers the analyzer; rename the collection variable in
`Run-CF7GateExpanded.ps1` from the initial declaration at the earlier `$Args`
setup through this `if ($Model)` append block, and update all references
consistently so the argument list uses a non-automatic name throughout.

Source: Linters/SAST tools

OrchestratorIDE.UnitTests/ContextFabricCf3Tests.cs (1)

218-241: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Exercise the fact fallback branch too.

NormalizeLinkedFactObjects accepts both text and fact; the new tests only cover text, so the fallback can regress unnoticed.

Suggested test
+    [Test]
+    public void NormalizeLinkedFactObjects_FlattensObjectShapedFacts_ToTheirFactProperty()
+    {
+        const string raw = """{"schemaVersion":"cf0-stitch-1.0","caseId":"c1","summary":"S.","linkedFacts":[{"fact":"A fact-only sentence."}]}""";
+
+        var normalized = FabricBoundaryStitcher.NormalizeLinkedFactObjects(raw);
+        var draft = FabricJson.ParseModelObject<FabricBoundaryStitchDraft>(normalized);
+
+        Assert.That(draft.LinkedFacts, Is.EqualTo(new[] { "A fact-only sentence." }));
+    }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@OrchestratorIDE.UnitTests/ContextFabricCf3Tests.cs` around lines 218 - 241,
Add a test that covers the `fact` fallback path in
`FabricBoundaryStitcher.NormalizeLinkedFactObjects`, since the current
`NormalizeLinkedFactObjects_FlattensObjectShapedFacts_ToTheirTextProperty` case
only verifies object-shaped linked facts using `text`. Create a similar input
payload where `linkedFacts` objects use `fact` instead of `text`, then parse it
with `FabricJson.ParseModelObject<FabricBoundaryStitchDraft>` and assert
`draft.LinkedFacts` is flattened to the fallback fact strings.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@OrchestratorIDE/Services/ContextFabric/ContextFabricFeasibilityRunner.cs`:
- Around line 786-809: The reference-chasing step in
ContextFabricFeasibilityRunner should happen before the greedy fill loop
exhausts the EvidenceLimit, since ChaseTrackedReferences is currently delayed
until after all candidates are processed. Rework the flow around the candidate
selection loop and TryInclude so you first include the seed/coverage cards,
immediately chase their tracked references, and only then run the remaining
greedy fill over candidates to use any leftover budget.
- Around line 861-863: The carrier selection in ContextFabricFeasibilityRunner
is matching tracked identifiers by substring via ContainsAnchor, which can
incorrectly pull in cards like RPT-1000 when looking for RPT-100. Update the
filtering logic in the carriers query to use exact token membership from
ExtractTrackedIdentifiers instead of ContainsAnchor, and keep the existing
CardHaystack/identifier flow so the match only succeeds when the tracked
identifier is present as a discrete identifier.

In `@OrchestratorIDE/Services/ContextFabric/FabricBoundaryStitcher.cs`:
- Around line 245-248: The fallback logic in FabricBoundaryStitcher’s text
extraction only checks fact when text is missing, so blank text values still
block a valid fact from being used. Update the loop in FabricBoundaryStitcher to
treat empty or whitespace text as absent and then fall back to obj["fact"]
before calling flattened.Add, keeping the existing null/whitespace guard in
place.

---

Outside diff comments:
In `@Tools/ContextFabricBench/Program.cs`:
- Around line 140-158: The failure diagnostics in Program.cs are misleading
after applying modelPin because depot is overwritten before
PrintDepotDiagnostics is called, so a pin that matches nothing looks like the
model root has no GGUFs at all. Update the flow around ModelDepot.Scan,
WithBaseModelFilter, and PrintDepotDiagnostics so the diagnostic output can
distinguish “no models under modelRoot” from “modelPin filtered out all models”
(for example by preserving the unfiltered depot or passing the pin context into
the diagnostics). Keep the existing Model pin message, but make the error path
report the actual mismatch clearly when ResolveRole fails.

---

Nitpick comments:
In `@OrchestratorIDE.UnitTests/ContextFabricCf3Tests.cs`:
- Around line 218-241: Add a test that covers the `fact` fallback path in
`FabricBoundaryStitcher.NormalizeLinkedFactObjects`, since the current
`NormalizeLinkedFactObjects_FlattensObjectShapedFacts_ToTheirTextProperty` case
only verifies object-shaped linked facts using `text`. Create a similar input
payload where `linkedFacts` objects use `fact` instead of `text`, then parse it
with `FabricJson.ParseModelObject<FabricBoundaryStitchDraft>` and assert
`draft.LinkedFacts` is flattened to the fallback fact strings.

In `@OrchestratorIDE.UnitTests/ContextFabricEvidencePackTests.cs`:
- Around line 349-352: The test in ContextFabricEvidencePackTests should be
tightened because Count < 7 still allows partial filler-card chase leakage;
update the assertions around IncludedSegmentIds so they explicitly verify that
no seg-fill-* identifiers are present, while keeping the existing seg-seed
check. Use the pack.IncludedSegmentIds collection directly in the test to assert
absence of any filler segment IDs rather than relying on a count threshold.

In `@Tools/ContextFabricBench/Program.cs`:
- Around line 473-478: Update PrintUsage so it documents the new model pinning
option added in CliOptions parsing: mention --model <substring> and its
THEORC_CF_MODEL environment-variable equivalent alongside the other CLI flags.
Make sure the usage text clearly explains that this maps to the modelPin value
so operators can discover the feature from --help.

In `@Tools/ContextFabricBench/Run-CF7GateExpanded.ps1`:
- Around line 113-116: The comment-based help for Run-CF7GateExpanded.ps1 is
missing documentation for the new Model parameter, so update the .PARAMETER
block that already documents MaxQuestions, Context, GpuLayers, SkipBuild, and
LogFile to include Model. Describe its purpose consistently with the inline
comment near the parameter declaration so Get-Help on Run-CF7GateExpanded and
its parameter metadata includes -Model.
- Around line 249-252: The script uses `$Args`, which conflicts with
PowerShell’s automatic `$args` variable and triggers the analyzer; rename the
collection variable in `Run-CF7GateExpanded.ps1` from the initial declaration at
the earlier `$Args` setup through this `if ($Model)` append block, and update
all references consistently so the argument list uses a non-automatic name
throughout.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 53b6aaf1-7e41-408f-9ef7-086a7c73c15f

📥 Commits

Reviewing files that changed from the base of the PR and between 50e79b8 and 12c91d8.

📒 Files selected for processing (7)
  • OrchestratorIDE.UnitTests/ContextFabricCf3Tests.cs
  • OrchestratorIDE.UnitTests/ContextFabricEvidencePackTests.cs
  • OrchestratorIDE/Core/Runtime/ModelDepot.cs
  • OrchestratorIDE/Services/ContextFabric/ContextFabricFeasibilityRunner.cs
  • OrchestratorIDE/Services/ContextFabric/FabricBoundaryStitcher.cs
  • Tools/ContextFabricBench/Program.cs
  • Tools/ContextFabricBench/Run-CF7GateExpanded.ps1

Comment on lines +786 to +809
while (candidates.Count > 0)
{
var best = candidates
.OrderByDescending(c => c.Anchors
.Where(uncoveredAnchors.Contains)
.Sum(a => 1.0 / Math.Max(1, anchorDocumentFrequency[a]))
+ PairScore(c.Pairs, uncoveredPairs))
.ThenByDescending(c => c.Terms
.Where(uncoveredTerms.Contains)
.Sum(t => 1.0 / documentFrequency.GetValueOrDefault(t, 1)))
.ThenByDescending(c => c.AnchorScore)
.ThenByDescending(c => c.TermScore)
.ThenBy(c => c.Card.SegmentId, StringComparer.Ordinal)
.First();
candidates.Remove(best);

if (!TryInclude(best.Card))
continue; // skipped for size: its anchors/terms stay uncovered for later picks
uncoveredAnchors.ExceptWith(best.Anchors);
uncoveredPairs.ExceptWith(best.Pairs);
uncoveredTerms.ExceptWith(best.Terms);
}

ChaseTrackedReferences(cards, evidence, included, TryInclude);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift

Run reference chasing before greedy fill can consume the remaining budget.

Line 809 performs the chase only after the greedy loop has tried every term-overlap candidate. With a tight EvidenceLimit, lower-value greedy cards can fill the pack first, leaving no room for the linked chain cards this PR is trying to recover.

Consider a two-phase flow: include the best seed/coverage cards, chase their tracked references, then use greedy fill for any leftover budget.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@OrchestratorIDE/Services/ContextFabric/ContextFabricFeasibilityRunner.cs`
around lines 786 - 809, The reference-chasing step in
ContextFabricFeasibilityRunner should happen before the greedy fill loop
exhausts the EvidenceLimit, since ChaseTrackedReferences is currently delayed
until after all candidates are processed. Rework the flow around the candidate
selection loop and TryInclude so you first include the seed/coverage cards,
immediately chase their tracked references, and only then run the remaining
greedy fill over candidates to use any leftover budget.

Comment thread OrchestratorIDE/Services/ContextFabric/FabricBoundaryStitcher.cs Outdated
hardcoreerik and others added 6 commits July 8, 2026 08:43
The first Tier 2.5 validation run (58/120, identical MultiHop 0/24 to the
pre-fix baseline) showed the reference chase was a no-op. Root cause:
ChaseTrackedReferences scanned CardHaystack (Summary + Claims.Text), which
is the READER MODEL'S OWN PARAPHRASE of the segment -- tracked identifiers
like RPT-064 routinely get reworded or dropped there. The verbatim text
survives only in FabricCitation.Quote, which CardHaystack never included.

Added a chase-only ChaseHaystack (CardHaystack + citation quotes) used
solely by ChaseTrackedReferences; the shared scoring haystack used by
anchor/term matching across Tiers 1/1.5/2 is untouched. Rewrote the three
chase unit tests to use claim text that paraphrases the identifier away
(matching real reader output) with the identifier only in the citation
quote -- the original tests embedded identifiers directly in claim text,
which is why they passed against the broken implementation.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…was a no-op

Honest record of the broken-chase run before the fix in 7d9e3b8 -- the
byte-identical MultiHop retrieval distribution to the prior run is what
exposed the bug.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Live re-validation (still 58/120) showed the chase engaging but leaving
long-hop chains partially retrieved (e.g. 1/5, 2/4 hits). Root cause: a
legitimate N-hop chain's shared token appears in exactly N segments by
construction, and the corpus's longest chains are 5 hops -- so the cap of
4 excluded real 4- and 5-hop chains as "filler" alongside genuine noise.
Raised to 6, still well under the measured filler frequency (7+ cards).

Also confirmed via live evidence: the 6 fully-retrieved 2-hop MultiHop
failures are NOT a retrieval or chase problem at all -- both target
segments were present in every case, but the model attached only one
citation where the stricter verifier requires two. This is a genuine
model-compliance gap (an 8B model under-following a multi-part citation
instruction), not a code defect; logging it in CF_TEST_RESULTS.md as a
known limitation rather than continuing to iterate the prompt blind.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Live evidence-card inspection replaced assumption: 2-hop failures are
answer-citation-discipline gaps (retrieval succeeded), long-hop failures
are the doc-frequency-cap bug fixed in 0f8338a.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Fourth live 120-question cycle in a row confirms citation-precision and
boundary-stitch gates now pass and hold stable, but MultiHop stays flat.
Direct inspection of multihop-chain-lh-002 (worst case, 1/5 retrieval)
proves the chase itself is now correct: CHN-201's real document frequency
is 4, well within the raised cap, and 3 of those 4 cards were confirmed to
carry the identifier verbatim. The actual blocker is execution order --
BuildEvidencePack's greedy anchor-fill runs to completion (consuming almost
all the token budget on distractor segments that share an entity name with
the question but belong to an unrelated chain) before the chase ever gets
a turn to add the real linked segments.

Documented as a scoped architectural item (two candidate fixes, budget
reservation vs. identifier-priority scoring) in
CF_RETRIEVAL_IMPROVEMENT_PLAN.md rather than continuing to guess-and-check
against 37-minute live runs -- four flat-score cycles (~2.5h of GPU time)
is the point to stop and scope properly instead of trying a fifth tweak.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Implements the budget-ordering fix scoped in CF_RETRIEVAL_IMPROVEMENT_PLAN.md
§3c (option 1): BuildEvidencePack's greedy anchor-fill now caps its own
spend at 70% of EvidenceLimit for MultiHop questions specifically, leaving
guaranteed headroom for ChaseTrackedReferences to add the segments a
question's real chain links to, instead of letting the greedy fill consume
the whole budget on distractor segments that share an entity name with the
question but belong to an unrelated chain (the corpus does this by
construction -- a small shared entityNames pool reused across many facts).
Scoped to MultiHop only; every other question kind keeps the full budget
Tiers 1/1.5/2 were validated against.

Attempted a corpus-scale synthetic reproduction of the starvation scenario
directly (two distractors sized to exhaust a tight budget) and found it too
fragile for a trustworthy regression guard -- the greedy loop retries every
candidate regardless of processing order, so undersized fixtures didn't
reliably starve the real segments the way the dense live corpus does.
Replaced with a narrower, verified-discriminating unit test: MultiHop must
admit strictly fewer cards than an otherwise-identical GlobalSynthesis
question under the same tight budget -- confirmed to fail without the fix
and pass with it. Full validation is the live 120-question benchmark.

Unit suite: 497 total, 0 failed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
OrchestratorIDE.UnitTests/ContextFabricEvidencePackTests.cs (1)

313-336: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Make the chase signal exclusive. These MultiHop chase tests still have enough overlap in Summary/Claims.Text for the ordinary greedy fill to include the cards, so they can pass even if ChaseTrackedReferences regresses. Reword the claims so only the citation quote carries the linking identifier, or tighten the budget so the chase path is required.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@OrchestratorIDE.UnitTests/ContextFabricEvidencePackTests.cs` around lines 313
- 336, The MultiHop chase test still overlaps enough with the card summaries and
claims that the normal greedy selection can pass without exercising the chase
logic. Update
BuildEvidencePack_ChasesTrackedIdentifier_IntoLinkedSegmentTheQuestionNeverNames
by making the linking identifier appear only in the citation quote for the
tracked cards, or otherwise reduce the budget/overlap so
ContextFabricFeasibilityRunner.BuildEvidencePack must rely on
ChaseTrackedReferences. Keep the assertion focused on IncludedSegmentIds for the
linked segments.
🧹 Nitpick comments (1)
OrchestratorIDE.UnitTests/ContextFabricEvidencePackTests.cs (1)

363-388: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Loose assertion on filler exclusion.

Assert.That(pack.IncludedSegmentIds.Count, Is.LessThan(7)) would also pass if several (up to 5) filler cards leaked into the pack via the chase. Since fillers share no terms/anchors with the question, a tighter assertion (e.g., asserting the count equals 1, or asserting none of the filler segment IDs are present) would more precisely validate that the frequency-cap skip actually fired.

Suggested tightening
-        Assert.That(pack.IncludedSegmentIds, Does.Contain("seg-seed"));
-        // Filler cards may enter via normal term scoring, but the chase must not add all of
-        // them: the identifier occurs in 7 cards, above the cap of 6.
-        Assert.That(pack.IncludedSegmentIds.Count, Is.LessThan(7));
+        Assert.That(pack.IncludedSegmentIds, Does.Contain("seg-seed"));
+        // The identifier occurs in 7 cards, above the cap: no filler should be chased in.
+        Assert.That(pack.IncludedSegmentIds.Where(id => id.StartsWith("seg-fill-")), Is.Empty);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@OrchestratorIDE.UnitTests/ContextFabricEvidencePackTests.cs` around lines 363
- 388, The test in BuildEvidencePack_DoesNotChaseHighFrequencyFillerIdentifiers
is too loose because it only checks that IncludedSegmentIds.Count is below 7,
which still allows filler segments to slip in. Tighten the assertion on
pack.IncludedSegmentIds so it verifies the chase was skipped by checking that
only the seed segment is included, or by explicitly asserting none of the filler
segment IDs from the fillers array are present. Keep the existing setup around
ContextFabricFeasibilityRunner, FabricBenchmarkQuestion, and BuildEvidencePack,
but make the expectation precise enough to prove the frequency-cap behavior
fired.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@OrchestratorIDE.UnitTests/ContextFabricEvidencePackTests.cs`:
- Around line 313-336: The MultiHop chase test still overlaps enough with the
card summaries and claims that the normal greedy selection can pass without
exercising the chase logic. Update
BuildEvidencePack_ChasesTrackedIdentifier_IntoLinkedSegmentTheQuestionNeverNames
by making the linking identifier appear only in the citation quote for the
tracked cards, or otherwise reduce the budget/overlap so
ContextFabricFeasibilityRunner.BuildEvidencePack must rely on
ChaseTrackedReferences. Keep the assertion focused on IncludedSegmentIds for the
linked segments.

---

Nitpick comments:
In `@OrchestratorIDE.UnitTests/ContextFabricEvidencePackTests.cs`:
- Around line 363-388: The test in
BuildEvidencePack_DoesNotChaseHighFrequencyFillerIdentifiers is too loose
because it only checks that IncludedSegmentIds.Count is below 7, which still
allows filler segments to slip in. Tighten the assertion on
pack.IncludedSegmentIds so it verifies the chase was skipped by checking that
only the seed segment is included, or by explicitly asserting none of the filler
segment IDs from the fillers array are present. Keep the existing setup around
ContextFabricFeasibilityRunner, FabricBenchmarkQuestion, and BuildEvidencePack,
but make the expectation precise enough to prove the frequency-cap behavior
fired.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 86509367-a776-4045-8e84-cc291853b27d

📥 Commits

Reviewing files that changed from the base of the PR and between 7f08a98 and a30229a.

📒 Files selected for processing (2)
  • OrchestratorIDE.UnitTests/ContextFabricEvidencePackTests.cs
  • OrchestratorIDE/Services/ContextFabric/ContextFabricFeasibilityRunner.cs
🚧 Files skipped from review as they are similar to previous changes (1)
  • OrchestratorIDE/Services/ContextFabric/ContextFabricFeasibilityRunner.cs

hardcoreerik and others added 3 commits July 8, 2026 10:54
…compliance

Fifth live 120-question cycle with the budget-reservation fix confirms it
worked: MultiHop full-retrieval rose 6->9/24, the first change in the
retrieval distribution across 5 cycles. Pass rate stayed flat anyway --
every one of the 9 now-fully-retrieved cases fails on the identical
citation-discipline gap (1 of 2 or 0 of 4 required citations given) already
diagnosed in run #11. This closes the retrieval-side investigation: the
remaining MultiHop gap is Meta-Llama-3.1-8B not reliably complying with a
multi-part citation instruction, not a retrieval or code defect.

CF_RETRIEVAL_IMPROVEMENT_PLAN.md updated with the conclusion and three
scoped next options (few-shot, schema-enforced citation count, defer to a
larger model), recommending schema enforcement since it doesn't depend on
model compliance.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- ChaseTrackedReferences carrier matching used ContainsAnchor (substring),
  so "RPT-1000" would wrongly count as a carrier for "RPT-100", pulling an
  unrelated card into a chain. Switched to exact membership via
  ExtractTrackedIdentifiers(ChaseHaystack(c)) -- keeping ChaseHaystack, not
  CardHaystack, since the latter is exactly the bug fixed in 7d9e3b8
  (CodeRabbit's suggested diff would have reverted that fix).
- NormalizeLinkedFactObjects only fell back to the "fact" property when
  "text" was missing/null; a model emitting "text": "" with a valid "fact"
  would silently drop the content. Now falls back on blank too.

Third CodeRabbit finding (run reference chasing before the greedy fill,
not after) is a real architectural point already captured as Option 2 in
CF_RETRIEVAL_IMPROVEMENT_PLAN.md SS3c -- the budget-reservation fix shipped
instead (Option 1) was chosen because it was live-validated (measured
MultiHop full-retrieval 6->9/24) and lower-risk; reordering the two passes
is a heavier restructuring left for a follow-up with its own validation
cycle, not applied blind here.

Unit suite: 497 total, 0 failed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Whole tab now sits in a page-level ScrollViewer (right-side scrollbar
reaches every section regardless of window size -- previously only the
review queue could scroll internally). The three pipeline stages
(Generate Dataset / Orc Academy / The Foundry) moved from stacked,
individually-expandable accordion sections to numbered, color-coded
cards laid out side by side across the full width, so the production
line reads left-to-right instead of requiring one-at-a-time expand
clicks. Internal field grids switched from horizontal Grid columns to
vertical label-above-field stacking to fit the narrower per-stage
columns; button rows use WrapPanel instead of a fixed-width row.
Inventory tiles (datasets/adapters/models) gained a colored accent bar
and a larger count number, reading as stat cards.

Removed the now-orphaned Expander.Expanded handlers (ExpGen_Expanded /
ExpForge_Expanded / ExpFoundry_Expanded); since all three stages are
permanently visible instead of lazily expanded, their RefreshGen() /
RefreshForge() / RefreshFoundry() calls now fire once automatically
after the panel's initial data load instead of waiting for a user to
expand a section that no longer exists.

KNOWN ISSUE: opening a workspace folder while this tab is active has
been reported to trigger a native stack-overflow crash. Investigated at
length (WER crash dump -- unanalyzable, self-contained single-file
deployments clean up their DAC/extraction directory on exit; isolated
headless repro with the real repo's data, a real shown window, and
window widths from 500-1900px -- did not reproduce it). Shipping as-is
per explicit user decision; tracked for a follow-up patch once a live
repro is caught.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@hardcoreerik
hardcoreerik merged commit 89165fc into master Jul 9, 2026
2 checks passed
@hardcoreerik
hardcoreerik deleted the feat/cf-citation-stitch-tier3 branch July 18, 2026 02:23
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