Skip to content

NoKvSlot root-cause + Tier 1 retrieval fix: anchor-phrase evidence ranking - #40

Merged
hardcoreerik merged 16 commits into
masterfrom
fix/nokvslot-cache-exhaustion
Jul 8, 2026
Merged

NoKvSlot root-cause + Tier 1 retrieval fix: anchor-phrase evidence ranking#40
hardcoreerik merged 16 commits into
masterfrom
fix/nokvslot-cache-exhaustion

Conversation

@hardcoreerik

@hardcoreerik hardcoreerik commented Jul 4, 2026

Copy link
Copy Markdown
Owner

Summary

This PR tells a complete investigation story across four layers, each validated against real 100-question CF-7 gate runs on real hardware (all runs logged in `docs/CF_TEST_RESULTS.md`):

1. NoKvSlot crash fixes and root-cause (commits 1–6)

  • Margin-adjusted token-budget gates (`ContextFabricFeasibilityRunner.cs`, `FabricBoundaryStitcher.cs`), bounded `NoKvSlot` retry, force-recycle-on-NoKvSlot defense-in-depth (`AdapterManager.cs`).
  • Root cause proven: Gemma-4's shared-KV-cache architecture hits a known, currently-unpatched upstream llama.cpp limitation (cache reuse is not supported for Gemma 4 models despite -fa enabled and --swa-full ggml-org/llama.cpp#21468, #23720; fix #23981 postdates every available LLamaSharp release). Two of our own theories (`SwaFull` sizing, cumulative cross-conversation exhaustion) were empirically disproven via A/B runs and reverted/kept-as-hardening respectively. Full writeup: `docs/CONTEXT_FABRIC_TEST_HARNESS.md` §7a.
  • `ModelAdmissionGate` now downgrades Gemma-4-class models to Provisional for Context Fabric workloads with a caveat citing the upstream issue.

2. Retrieval failure analysis + Tier 1 fix (commit 7)

Failure categorization of the Meta-Llama-3.1-8B 100-question run proved 100% of B3 failures were retrieval misses (69/69 — the required segment was absent from the evidence pack; zero model failures). Root mechanism, measured: unigram bag-of-words IDF dissolves multi-word entities ("Station Alpha" → 63/105 cards tie; 3 contain the phrase) and hyphenated identifiers ("BR-048" → {br, 048}).

Fix: anchor-phrase extraction ranked lexicographically above unigram score, coverage-aware greedy fill for multi-entity questions, verbatim identifier matching on the Exhaustive path. Validated: 31/100 → 45/100, B3 beats B2 RAG baseline for the first time.

3. Tier 1.5 — Proximity pairs for inverted entity word order (commit 8)

Paraphrased questions invert entity word order ("the Meridian relay point" vs corpus "Relay Meridian"). Contiguous phrase anchors fail silently there. Added unordered proximity pairs: mid-sentence capitalized word + nearest non-stopword neighbor within ±2 positions, matched within 2-token window in either order, at half verbatim anchor weight. Validated: 45/100 → 56/100, Paraphrased pure-misses 13 → 3.

4. Tier 2 — Reader-rejection fix (commit 9)

`FabricEvidenceProcessor.NormalizeAndValidate` rejected cards outright when the model over-generated on expanded-corpus dense segments: summary > 2 000 chars, claims > 64, or duplicate canonical claimId. Each rejection silently removed the segment card from the evidence pack. Now:

  • Long summaries are truncated to `MaxSummaryChars` with a trailing ellipsis
  • Excess claims are silently truncated to the first `MaxClaims` entries
  • Duplicate canonical claimIds are silently deduped (first occurrence kept)
    Validation run in progress on all three fleet machines with Meta-Llama.

5. CodeRabbit cleanup (commit 10)

  • Consolidated duplicate `1.15` safety margin into `FabricContextBudget.TokenSafetyMargin` (single source of truth, visible from NativeRuntime)
  • Fixed reporting drift: `InvokeAsync` now uses raw estimate for `FabricCallMetrics` fallback, margin-adjusted estimate only for the budget gate

Documentation

  • `docs/CF_TEST_RESULTS.md` — durable log of every gate run: method, bugs, per-machine results
  • `docs/CF_RETRIEVAL_IMPROVEMENT_PLAN.md` — full tiered plan with rationale

Test plan

  • Full unit suite green: 486 passed, 0 failed (10 new tests across Tier 1, 1.5, 2)
  • NoKvSlot conclusions validated across 3 machines / 2 models / 500+ combined questions
  • Tier 1 validation: 31 → 45/100, B3 beats B2 RAG baseline (NEWCOREPC, Meta-Llama)
  • Tier 1.5 validation: 45 → 56/100, Paraphrased pure-misses 13 → 3 (NEWCOREPC, Meta-Llama)
  • Tier 2 validation (HARDCORELAPTOPMSI, qwen-7b): 26 → 30/100, citation precision 77.5% → 88.2%, 0 NoKvSlot
  • Tier 2 validation still running: HARDCOREPC (qwen-7b, in flight) and NEWCOREPC (Meta-Llama, 120Q full run in flight) — results will be logged in docs/CF_TEST_RESULTS.md as they land

🤖 Generated with Claude Code

…et gates, retry on transient KV pressure

Deep investigation (three parallel research agents, cross-validated with Codex CLI and Grok CLI)
root-caused the CF-7 NoKvSlot crashes to a fixed, too-small native KV-cache pool rather than a
leak, fragmentation, or stuck reference count:

- ModelParams.SwaFull was never set and silently inherited llama.cpp's native default of true,
  forcing this Gemma-3-class model's 40 sliding-window-attention layers to reserve full-context
  KV cache instead of window-sized cache -- ~6x more cache pressure than the architecture needs,
  for no accuracy benefit.
- ContextFabricFeasibilityRunner's two token-budget gates (InvokeAsync, BuildEvidencePack) used
  ContextManager.EstimateTokens (a crude chars/4 heuristic) with zero safety margin, unlike the
  sibling EvidencePackBuilder class, which already applies a 1.15x margin for exactly this reason.
  That let real, token-dense evidence prompts silently exceed the native KV pool.
- InferUntilReadyAsync treated NoKvSlot as unconditionally fatal, contrary to LLamaSharp's own
  documented contract that it is retryable (BatchedExecutor.Infer requeues the batch internally
  rather than consuming it).

Fixes:
- Set ModelParams.SwaFull = false in LLamaSharpRuntime.LoadModelAsync.
- Apply the same 1.15x safety margin to both token-budget gates in
  ContextFabricFeasibilityRunner.cs.
- Give InferUntilReadyAsync a bounded retry-with-backoff on NoKvSlot specifically, before
  giving up.

Ruled out via prior empirical runs (docs/CONTEXT_FABRIC_TEST_HARNESS.md §7): conversation-count
recycle threshold value, and stuck/leaked ActiveCount reference counting.

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

coderabbitai Bot commented Jul 4, 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 adds role recycle signaling and retry handling for KV-slot exhaustion in native inference, documents the SWA KV-cache setting during model load, and applies token safety margins to ContextFabric budget checks and related tooling.

Changes

Runtime inference and KV-cache behavior

Layer / File(s) Summary
Role recycle signaling
OrchestratorIDE/Core/Runtime/AdapterManager.cs, OrchestratorIDE/Core/Runtime/RuntimeOrchestrator.cs
MarkRoleDegraded forwards to MarkForRecycle, and RoleEntry tracks ForceRecycle in recycle eligibility.
NoKvSlot retry handling
OrchestratorIDE/Core/Runtime/IRoleRuntime.cs
Inference drain calls now pass role, and InferUntilReadyAsync retries DecodeResult.NoKvSlot up to 8 times with delay before throwing.
SWA KV-cache configuration
OrchestratorIDE/Core/Runtime/LLamaSharpRuntime.cs
LoadModelAsync keeps LLamaSharp's native SwaFull default and documents the earlier SwaFull = false experiment.

Context Fabric token budget safety margin

Layer / File(s) Summary
Token safety margin applied to budget gating
OrchestratorIDE/Services/ContextFabric/ContextFabricFeasibilityRunner.cs, OrchestratorIDE/Services/ContextFabric/FabricBoundaryStitcher.cs, Tools/ContextFabricBench/Run-CF7GateExpanded.ps1
Margin-adjusted token estimates are used in evidence-pack gating, invocation budget checks, stitch-request budget checks, and GGUF file counting logic.

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

Possibly related PRs

  • hardcoreerik/TheOrc#32: Related changes to AdapterManager.cs recycle eligibility logic in the same runtime lifecycle path.
  • hardcoreerik/TheOrc#39: Related KV-cache exhaustion handling and recycle-path changes in AdapterManager, RuntimeOrchestrator, and IRoleRuntime.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Title check ⚠️ Warning The title mentions anchor-phrase evidence ranking, but the changes are about NoKvSlot handling, KV-cache budgeting, and recycle safeguards. Rename the PR to reflect the actual fixes, e.g. NoKvSlot retry/recycle handling and context-budget margin updates.
✅ Passed checks (4 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.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/nokvslot-cache-exhaustion

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

- FabricBoundaryStitcher.cs shared the identical unmargined token-budget-gate pattern
  Grok flagged as only partially fixed -- apply the same 1.15x margin there too.
  (ContextFabricBaselineRunner.cs was also flagged but does not actually gate/throw on
  token budget at all -- nothing to margin there without inventing a new check.)
- Fix an off-by-one in the NoKvSlot retry give-up message (reported one more retry than
  actually attempted).
- Add an env-gated (THEORC_KVCACHE_DIAGNOSTICS=1) diagnostic line per NoKvSlot retry, so
  a genuine single-conversation overflow shows a visible retry trail during its ~1.8s
  bounded backoff instead of just a delayed throw.
- Clarify in-code that SwaFull=false only affects KV-cache allocation size, not the
  attention window itself (which is architectural and unaffected either way), and is a
  no-op for architectures without SWA layers -- still pending an empirical CF-7 gate
  re-run to confirm no output-quality regression on the live model.

Co-Authored-By: Claude Sonnet 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.

Actionable comments posted: 3

🧹 Nitpick comments (1)
OrchestratorIDE/Core/Runtime/LLamaSharpRuntime.cs (1)

222-228: 🚀 Performance & Scalability | 🔵 Trivial

Keep SwaFull configurable on the batched-executor path. LLamaSharp warns false can hurt performance when SeqMax > 1, and this persistent per-role executor can hit that case once multiple conversations are active.

🤖 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/Core/Runtime/LLamaSharpRuntime.cs` around lines 222 - 228,
Keep SwaFull configurable in LLamaSharpRuntime’s executor settings instead of
hard-coding it to false. Update the batched-executor setup in the
LLamaSharpRuntime path so the value can be chosen based on SeqMax or
caller/runtime configuration, preserving the current memory-saving behavior only
where safe while allowing the per-role executor to opt into the native setting
when multiple conversations are active.
🤖 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/Core/Runtime/IRoleRuntime.cs`:
- Around line 318-337: Reset the transient retry counter in the drain loop so it
only counts consecutive NoKvSlot failures within the current recovery streak. In
the conversation drain logic inside the method that calls
conversation.Executor.Infer, keep noKvSlotRetries from accumulating across
successful decodes by clearing it whenever result is not DecodeResult.NoKvSlot,
while preserving the existing backoff and hard-fail behavior for repeated
failures.

In `@OrchestratorIDE/Services/ContextFabric/ContextFabricFeasibilityRunner.cs`:
- Around line 867-876: The token-budget gate in FabricBoundaryStitcher still
uses raw ContextManager.EstimateTokens for the prompt-size check, which can let
oversized prompts slip through and hit NoKvSlot. Update the boundary check in
the relevant stitching path to apply the same EvidenceTokenSafetyMargin used by
ContextFabricFeasibilityRunner, and keep the failure condition comparing the
margin-adjusted promptTokens plus ReaderMaxTokens against the context limit so
both budget gates behave consistently.
- Around line 867-876: The margin-adjusted prompt token estimate in
ContextFabricFeasibilityRunner is being reused beyond the budget gate, which
causes reporting drift. Keep the EvidenceTokenSafetyMargin-applied value only
for the context limit check in the prompt budget path, and preserve the raw
ContextManager.EstimateTokens total for fallback metrics. Update the logic
around the promptTokens calculation and the
FitsContext/MaximumPromptTokens/SourceToWorkingContextRatio reporting so they
use the unadjusted estimate when usage is not reported.

---

Nitpick comments:
In `@OrchestratorIDE/Core/Runtime/LLamaSharpRuntime.cs`:
- Around line 222-228: Keep SwaFull configurable in LLamaSharpRuntime’s executor
settings instead of hard-coding it to false. Update the batched-executor setup
in the LLamaSharpRuntime path so the value can be chosen based on SeqMax or
caller/runtime configuration, preserving the current memory-saving behavior only
where safe while allowing the per-role executor to opt into the native setting
when multiple conversations are active.
🪄 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: ba8130fe-c0a9-4983-8db5-ac202bdb265d

📥 Commits

Reviewing files that changed from the base of the PR and between 4205f6f and 5ef165a.

📒 Files selected for processing (3)
  • OrchestratorIDE/Core/Runtime/IRoleRuntime.cs
  • OrchestratorIDE/Core/Runtime/LLamaSharpRuntime.cs
  • OrchestratorIDE/Services/ContextFabric/ContextFabricFeasibilityRunner.cs

Comment thread OrchestratorIDE/Core/Runtime/IRoleRuntime.cs
Comment thread OrchestratorIDE/Services/ContextFabric/ContextFabricFeasibilityRunner.cs Outdated

@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.

🧹 Nitpick comments (1)
OrchestratorIDE/Services/ContextFabric/FabricBoundaryStitcher.cs (1)

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

Consider consolidating the duplicated margin constant.

The same 1.15 safety margin is now independently defined as EvidenceTokenSafetyMargin in EvidencePackBuilder.cs and ContextFabricFeasibilityRunner.cs, and here as TokenSafetyMargin. This PR itself exists because one of these gates was missed when the margin was first introduced — extracting a single shared constant (e.g., in ContextManager or a small shared options/constants class) would prevent future drift across the three sites.

♻️ Example consolidation
-    // Same under-counting risk and fix as ContextFabricFeasibilityRunner.EvidenceTokenSafetyMargin
-    // (NoKvSlot investigation, docs/CONTEXT_FABRIC_TEST_HARNESS.md §7) -- this gate shares the
-    // identical unmargined ContextManager.EstimateTokens pattern and sits on the same B-system
-    // gate-run crash surface.
-    private const double TokenSafetyMargin = 1.15;
+    // Shared with EvidencePackBuilder/ContextFabricFeasibilityRunner to avoid margin drift.
+    private const double TokenSafetyMargin = ContextFabricTokenBudget.SafetyMargin;
🤖 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/FabricBoundaryStitcher.cs` at line 19,
The safety margin value is duplicated across
FabricBoundaryStitcher.TokenSafetyMargin,
EvidencePackBuilder.EvidenceTokenSafetyMargin, and
ContextFabricFeasibilityRunner, which risks future drift. Extract the shared
1.15 margin into one common constant or shared options class (for example in
ContextManager or a small constants helper) and update all three call sites to
use that single source of truth.
🤖 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.

Nitpick comments:
In `@OrchestratorIDE/Services/ContextFabric/FabricBoundaryStitcher.cs`:
- Line 19: The safety margin value is duplicated across
FabricBoundaryStitcher.TokenSafetyMargin,
EvidencePackBuilder.EvidenceTokenSafetyMargin, and
ContextFabricFeasibilityRunner, which risks future drift. Extract the shared
1.15 margin into one common constant or shared options class (for example in
ContextManager or a small constants helper) and update all three call sites to
use that single source of truth.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 611cf369-5d38-426f-9022-23728250c495

📥 Commits

Reviewing files that changed from the base of the PR and between 5ef165a and 4542e1d.

📒 Files selected for processing (3)
  • OrchestratorIDE/Core/Runtime/IRoleRuntime.cs
  • OrchestratorIDE/Core/Runtime/LLamaSharpRuntime.cs
  • OrchestratorIDE/Services/ContextFabric/FabricBoundaryStitcher.cs
🚧 Files skipped from review as they are similar to previous changes (2)
  • OrchestratorIDE/Core/Runtime/LLamaSharpRuntime.cs
  • OrchestratorIDE/Core/Runtime/IRoleRuntime.cs

hardcoreerik and others added 2 commits July 4, 2026 11:26
…resent

Set-StrictMode -Version Latest makes .Count throw PropertyNotFoundException on a
single (non-array) FileInfo result from Get-ChildItem, instead of returning 1.
Hit for real validating the NoKvSlot fix on HARDCORELAPTOPMSI, which had exactly
one qualifying model after copying gemma-4-12B-it-qat-q4_0.gguf over.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…the conversation-count threshold

A 100-question CF-7 gate run on gemma-4-12B (the actual target model for the SwaFull fix)
showed NoKvSlot recurring on ~all subsequent conversations (85/88 in B3, 8/9 in B0) once it
first occurred on a role's executor -- not a slow climb toward SequenceRecycleThreshold, but
near-universal failure right after the first hit. That pattern is evidence the pool stays
degraded after a NoKvSlot rather than a single oversized prompt being the whole story, and
that the existing threshold-based recycle (every 24 conversations) recycles far too
infrequently to recover from it.

AdapterManager.RoleEntry now has a ForceRecycle flag, set by the new MarkForRecycle method as
soon as IRoleRuntime observes any NoKvSlot (whether the bounded retry eventually recovers or
gives up) -- the next GetOrCreateConversationAsync call for that role tears down and rebuilds
its executor (a genuinely empty KV pool) as soon as the role goes idle, instead of continuing
to serve from a pool already proven degraded.

RuntimeOrchestrator.MarkRoleDegraded forwards to AdapterManager.MarkForRecycle since
IRoleRuntime only holds a RuntimeOrchestrator reference.

Co-Authored-By: Claude Sonnet 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.

Actionable comments posted: 1

🤖 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/Core/Runtime/AdapterManager.cs`:
- Around line 281-287: The recycle/degrade mark in AdapterManager.MarkForRecycle
can be skipped if the caller’s cancellation token is already canceled while
waiting on _gate, which leaves ForceRecycle unset and allows a degraded executor
to be reused. Make MarkForRecycle immune to request cancellation once NoKvSlot
has been observed by not using the request token for the gate wait, and update
the orchestrator forwarding path that calls MarkForRecycle to stop passing
through the request CancellationToken. Use the existing MarkForRecycle and
IRoleRuntime call site as the places to fix.
🪄 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: 489aef80-292e-4277-829d-6c6a5258b39a

📥 Commits

Reviewing files that changed from the base of the PR and between 3fb2781 and 28f881f.

📒 Files selected for processing (3)
  • OrchestratorIDE/Core/Runtime/AdapterManager.cs
  • OrchestratorIDE/Core/Runtime/IRoleRuntime.cs
  • OrchestratorIDE/Core/Runtime/RuntimeOrchestrator.cs
🚧 Files skipped from review as they are similar to previous changes (1)
  • OrchestratorIDE/Core/Runtime/IRoleRuntime.cs

Comment thread OrchestratorIDE/Core/Runtime/AdapterManager.cs Outdated
hardcoreerik and others added 3 commits July 5, 2026 23:45
…rompts need

A 100-question CF-7 gate run (gemma-4-12B, the Gemma-3-class SWA architecture this setting
targeted) proved the SwaFull=false change from an earlier commit was itself the root cause of
the NoKvSlot failures on every Reviewer/Answer-stage call -- not the cumulative cross-
conversation exhaustion originally suspected.

With SwaFull=false, llama.cpp sizes the SWA-layer cache as min(fullContext, n_swa +
UBatchSize). ModelParams.UBatchSize defaults to 512, so that's min(8192, 1024+512) = ~1536
cells, not the full 8192. Context Fabric's Reviewer/Answer-stage prompts routinely run
6,000-6,500 tokens (they re-include the evidence pack plus the Researcher's draft for
review), well over that undersized window -- so every one of those calls failed NoKvSlot
outright, confirmed even on a completely fresh, empty pool immediately after a forced
executor recycle (the previous commit's fix). Raising UBatchSize enough to cover these
prompts would erase most of the intended memory saving anyway, since Context Fabric's
evidence packs are deliberately sized to use most of the available context budget --
SwaFull=false only pays off for workloads with prompts well under the context limit, which
this one is not.

The force-recycle-on-NoKvSlot fix (previous commit) stays: it's correct, harmless
defense-in-depth for genuine cumulative-pressure scenarios, it just wasn't the fix for this
specific failure mode.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…a-4 in the admission gate

Conclusive finding after direct A/B validation at 100+-question scale: NoKvSlot on
Context Fabric's Reviewer/heavy-prompt roles is specific to Gemma-4-class models, not a
bug in our own code. qwen2.5-coder-7b and Meta-Llama-3.1-8B both ran full B0-B4 pipelines
with zero NoKvSlot occurrences at 100-question scale; Gemma-4-12B hit it on the very first
conversation of a completely fresh, empty KV pool, with SwaFull true and false producing
byte-for-byte identical failures (disproving the SWA-cache-sizing theory) and force-recycle
-on-NoKvSlot making no difference either (disproving cumulative cross-conversation
exhaustion). llama.cpp's own native log confirms full-size KV caches were actually
allocated throughout.

Root cause: Gemma-4's "shared KV cache" architecture (some layers reuse another layer's K/V
tensors instead of computing their own) breaks assumptions in llama.cpp's generic
cache-management code -- documented by two independent upstream issues
(ggml-org/llama.cpp#21468, #23720). The fix (ggml-org/llama.cpp#23981) merged 2026-06-02,
after the llama.cpp commit pinned by LLamaSharp 0.27.0 (the latest release) and even after
the commit pinned by LLamaSharp's own unreleased development branch -- not fixable on our
end until a future LLamaSharp release bumps past it.

Changes:
- docs/CONTEXT_FABRIC_TEST_HARNESS.md: full investigation writeup (§7a), including the two
  disproven fixes, the upstream root cause with citations, and a separate (unrelated,
  not-yet-fixed) boundary-stitch schema-mismatch bug found on Meta-Llama-3.1-8B.
- ModelAdmissionGate.cs: downgrade Gemma-4-class models (by name pattern, not just the
  already-rejected e4b variant) from Admitted to Provisional for Context Fabric workloads,
  with a caveat citing the known issue -- so the in-app admission reasoning reflects this
  rather than claiming "strong family" status for a family with a live crash risk.
- LLamaSharpRuntime.cs: opt-in native log sink (reuses THEORC_KVCACHE_DIAGNOSTICS) that
  surfaces llama.cpp's own log lines during a diagnostic run -- this is what let us confirm
  full-size KV caches were genuinely allocated, ruling out the sizing theory conclusively.
- Tools/ContextFabricBench/Program.cs: margin-adjusted the boundary-stitch diagnostic's
  token budget (matches the existing expanded-reader headroom fix) -- a reasonable
  improvement on its own, though confirmed NOT the fix for the schema-mismatch bug found
  during this investigation (completionTokens was 276, nowhere near the token budget).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…-pack fill

Failure analysis of the Meta-Llama-3.1-8B 100-question CF-7 run proved that 100% of the 69
failed B3 questions were retrieval misses -- in every failure at least one required segment
was absent from the evidence pack the model received, and in 49/69 none were present; zero
failures had the evidence present with a wrong answer. The reader stage was verified NOT
at fault (correct cards exist with the answer verbatim). Root mechanism, measured: the
unigram bag-of-words IDF ranker dissolves multi-word entity names and hyphenated
identifiers ("Station Alpha" -> {station, alpha}: 63/105 cards tie on both tokens while
only 3 contain the phrase; "BR-048" -> {br, 048}), leaving ties broken by segment-ID sort.

Fixes (CF_RETRIEVAL_IMPROVEMENT_PLAN.md Tier 1 -- the full tiered plan including the
deliberately-deferred embedding and agentic-retrieval tiers is in that new doc):

- 1a: extract anchor phrases (hyphenated identifiers, multi-word proper-noun runs with
  question-opener trimming) and rank them LEXICOGRAPHICALLY above unigram score -- an
  anchor match cannot be outvoted by common-word noise, and anchor-less questions degrade
  exactly to the previous ordering.
- 1b: coverage-aware greedy fill -- each pick is scored against the question's not-yet-
  covered anchors/terms first, so multi-entity questions cover every named entity before
  stacking near-duplicates; once covered, ordering falls back to base scores (preserving
  the old fill-the-budget behavior for GlobalSynthesis).
- 1c: BuildExhaustiveAnswer matches scoped hyphenated identifiers verbatim in claim text
  instead of the rarest-unigram heuristic, which admitted any claim sharing the numeric
  fragment ("grade-20" vs "20 crates").

Baselines (B0/B1/B2) deliberately untouched to preserve comparability with all prior runs
logged in CF_TEST_RESULTS.md.

Six new unit tests cover the measured production failure modes, including two constructed
so the OLD tie-break provably picks the wrong card.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@hardcoreerik hardcoreerik changed the title Fix NoKvSlot crashes: SWA cache waste, unmargined token budgets, non-retryable transient failure NoKvSlot root-cause + Tier 1 retrieval fix: anchor-phrase evidence ranking Jul 7, 2026
hardcoreerik and others added 9 commits July 6, 2026 21:24
- Reset noKvSlotRetries after each successful decode: the cap now bounds CONSECUTIVE
  NoKvSlot failures rather than the lifetime total across a long drain, so separately-
  recovered episodes can no longer sum to a spurious give-up.
- MarkForRecycle/MarkRoleDegraded no longer accept a CancellationToken: the observation
  "this executor produced a NoKvSlot" remains true whether or not the observing request is
  being cancelled, and honoring a cancelled token let the degraded executor be silently
  reused by the next request. Enforced at the API level rather than trusting call sites to
  pass CancellationToken.None. Request cancellation still propagates promptly via the
  retry delay that follows the mark.
- The third finding (FabricBoundaryStitcher margin) was already addressed in 4542e1d; the
  review raced that push.

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

100-question Meta-Llama-3.1-8B run on NEWCOREPC at 5fd6ad3, identical conditions to the
baseline. Retrieval misses cut from 69 (100% of failures) to 45; 10 genuine model
failures now measurable for the first time; citation precision held at 99.1%; zero
NoKvSlot. Dominant remaining pure-miss bucket identified: Paraphrased questions with
inverted entity word order, which contiguous anchors cannot match -- candidates are an
unordered-proximity anchor variant (Tier 1.5) or Tier 2 embeddings.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… entity word order

The Tier 1 validation run's dominant residual pure-miss bucket (13 of 21) was Paraphrased
questions that invert an entity's word order -- "the Meridian relay point" vs the corpus's
"Relay Meridian". Contiguous anchors fail twice there: matching can't bridge the
inversion, and extraction yields no anchor at all because only "Meridian" is capitalized
(no 2+ word proper-noun run exists in the question).

Fix: every mid-sentence capitalized word becomes an entity head paired with its nearest
non-stopword neighbor (within two positions each side); a card matches when both words
occur within a 2-token window in ANY order. Pairs feed the same lexicographic anchor key
as verbatim anchors at half weight (0.5/df) -- a contiguous phrase match still wins
wherever both exist -- and participate in the Tier 1b coverage bookkeeping. Questions
yielding no pairs behave exactly as Tier 1.

Three new tests, including one where the distractor shares MORE question unigrams than
the correct card so plain unigram IDF provably prefers it -- only the proximity pair
rescues the target.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
100-question Meta-Llama run on NEWCOREPC at 6d28071, identical conditions to prior runs.
Cumulative across three deterministic lexical fixes: 31 -> 45 -> 56 pass rate, pure
retrieval misses 49 -> 21 -> 10, citation precision 99.1% throughout, zero NoKvSlot.
The partial-miss bucket (24 multi-segment questions) is now the largest failure class.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
FabricEvidenceProcessor.NormalizeAndValidate previously rejected evidence
cards when the model over-generated on expanded-corpus dense segments:
- summary > 2000 chars: outright rejection (11 failures observed)
- claims > 64 items: outright rejection
- duplicate canonical claimId: error + rejection

Each rejection silently removed the segment card from the evidence pack,
blocking every question that needed that segment. Now:
- Long summaries are truncated to MaxSummaryChars with a trailing ellipsis
- Excess claims are silently truncated to the first MaxClaims entries
- Duplicate canonical claimIds are silently deduped (first occurrence kept)

Three new unit tests cover the new behaviors; the existing rejection test
for canonical-id collisions is updated to expect dedup-acceptance.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ing drift

Two outstanding CodeRabbit findings from PR #40 review rounds 1-2:

1. Consolidate duplicate 1.15 safety margin
   FabricContextBudget.TokenSafetyMargin is now the single source of truth,
   referenced from ContextFabricFeasibilityRunner, FabricBoundaryStitcher, and
   EvidencePackBuilder. Rationale lives in FabricContextBudget (in
   ContextFabricContracts.cs, which compiles into NativeRuntime unlike
   EvidencePackBuilder.cs, which is why the comment previously explained the
   duplication instead of fixing it).

2. Fix reporting drift from margin-adjusted estimate reuse
   InvokeAsync in ContextFabricFeasibilityRunner and FabricBoundaryStitcher
   previously used the margin-adjusted promptTokens as both the budget gate
   threshold AND the FabricCallMetrics fallback -- inflating reported prompt
   counts by 15% whenever the native runtime did not report actual usage.
   Now: rawPromptTokens for reporting, gatePromptTokens (x1.15) for the gate.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Phase 1: Model Benchmark window — storage schema + read-only CF-7 results display

ModelBenchmarkRecord + ModelBenchmarkStore: new storage schema for per-model
Context Fabric benchmark results (model-benchmark-1.0). The store scans
.orc/adversarial/ on open, joining each cf7_gate_*.json to the nearest-in-time
cf0_*.json B3 sub-report to recover the model filename from environment.lanes.
De-duped by (modelFilename + tier), so the latest run per (model, tier) pair
is kept.

ModelBenchmarkWindow.axaml/.cs: new Avalonia window accessible via
Models → Model Benchmark…. On open it scans .orc/adversarial/ and renders
one card per model: a tier + date header, GO/NO-GO verdict badge, and metric
chips for question pass rate, citation precision, segment coverage, and
boundary stitch rate. Depot GGUF files with no benchmark record render as a
dimmed "not benchmarked" row. "Run New Benchmark…" button present but disabled
(Phase 2). Phase 1 is read-only display of existing CF-7 run artifacts.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@
…light rejection

Today's NEWCOREPC exit-66 (SmolLM2 selected for CF) turned out to be depot
state, not a resolver bug: Meta-Llama-3.1-8B was .gguf.disabled at run time,
leaving only Rejected candidates. The rejection message now lists every
scanned base model with its ContextFabricReader verdict and any skipped
.gguf.disabled files, so this failure mode is self-explanatory next time.

Also make the Model Benchmark window depot listing recursive so GGUFs in
subdirectories (e.g. content-addressed 2f/) appear as unbenchmarked rows.

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

Second model family confirms the Tier 2 truncation fix direction; B2 RAG
baseline (49/100) still beats B3 on qwen-7b, unlike Meta-Llama on NEWCOREPC.

Co-Authored-By: Claude Fable 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